repo_testing.go

  1package repository
  2
  3import (
  4	"log"
  5	"math/rand"
  6	"os"
  7	"strings"
  8	"testing"
  9
 10	"github.com/stretchr/testify/require"
 11
 12	"github.com/MichaelMure/git-bug/util/lamport"
 13)
 14
 15func CleanupTestRepos(repos ...Repo) {
 16	var firstErr error
 17	for _, repo := range repos {
 18		path := repo.GetPath()
 19		if strings.HasSuffix(path, "/.git") {
 20			// for a normal repository (not --bare), we want to remove everything
 21			// including the parent directory where files are checked out
 22			path = strings.TrimSuffix(path, "/.git")
 23
 24			// Testing non-bare repo should also check path is
 25			// only .git (i.e. ./.git), but doing so, we should
 26			// try to remove the current directory and hav some
 27			// trouble. In the present case, this case should not
 28			// occur.
 29			// TODO consider warning or error when path == ".git"
 30		}
 31		// fmt.Println("Cleaning repo:", path)
 32		err := os.RemoveAll(path)
 33		if err != nil {
 34			log.Println(err)
 35			if firstErr == nil {
 36				firstErr = err
 37			}
 38		}
 39	}
 40
 41	if firstErr != nil {
 42		log.Fatal(firstErr)
 43	}
 44}
 45
 46type RepoCreator func(bare bool) TestedRepo
 47type RepoCleaner func(repos ...Repo)
 48
 49// Test suite for a Repo implementation
 50func RepoTest(t *testing.T, creator RepoCreator, cleaner RepoCleaner) {
 51	for bare, name := range map[bool]string{
 52		false: "Plain",
 53		true:  "Bare",
 54	} {
 55		t.Run(name, func(t *testing.T) {
 56			t.Run("Blob-Tree-Commit-Ref", func(t *testing.T) {
 57				repo := creator(bare)
 58				defer cleaner(repo)
 59
 60				// Blob
 61
 62				data := randomData()
 63
 64				blobHash1, err := repo.StoreData(data)
 65				require.NoError(t, err)
 66				require.True(t, blobHash1.IsValid())
 67
 68				blob1Read, err := repo.ReadData(blobHash1)
 69				require.NoError(t, err)
 70				require.Equal(t, data, blob1Read)
 71
 72				// Tree
 73
 74				blobHash2, err := repo.StoreData(randomData())
 75				require.NoError(t, err)
 76				blobHash3, err := repo.StoreData(randomData())
 77				require.NoError(t, err)
 78
 79				tree1 := []TreeEntry{
 80					{
 81						ObjectType: Blob,
 82						Hash:       blobHash1,
 83						Name:       "blob1",
 84					},
 85					{
 86						ObjectType: Blob,
 87						Hash:       blobHash2,
 88						Name:       "blob2",
 89					},
 90				}
 91
 92				treeHash1, err := repo.StoreTree(tree1)
 93				require.NoError(t, err)
 94				require.True(t, treeHash1.IsValid())
 95
 96				tree1Read, err := repo.ReadTree(treeHash1)
 97				require.NoError(t, err)
 98				require.ElementsMatch(t, tree1, tree1Read)
 99
100				tree2 := []TreeEntry{
101					{
102						ObjectType: Tree,
103						Hash:       treeHash1,
104						Name:       "tree1",
105					},
106					{
107						ObjectType: Blob,
108						Hash:       blobHash3,
109						Name:       "blob3",
110					},
111				}
112
113				treeHash2, err := repo.StoreTree(tree2)
114				require.NoError(t, err)
115				require.True(t, treeHash2.IsValid())
116
117				tree2Read, err := repo.ReadTree(treeHash2)
118				require.NoError(t, err)
119				require.ElementsMatch(t, tree2, tree2Read)
120
121				// Commit
122
123				commit1, err := repo.StoreCommit(treeHash1)
124				require.NoError(t, err)
125				require.True(t, commit1.IsValid())
126
127				treeHash1Read, err := repo.GetTreeHash(commit1)
128				require.NoError(t, err)
129				require.Equal(t, treeHash1, treeHash1Read)
130
131				commit2, err := repo.StoreCommitWithParent(treeHash2, commit1)
132				require.NoError(t, err)
133				require.True(t, commit2.IsValid())
134
135				treeHash2Read, err := repo.GetTreeHash(commit2)
136				require.NoError(t, err)
137				require.Equal(t, treeHash2, treeHash2Read)
138
139				// Ref
140
141				exist1, err := repo.RefExist("refs/bugs/ref1")
142				require.NoError(t, err)
143				require.False(t, exist1)
144
145				err = repo.UpdateRef("refs/bugs/ref1", commit2)
146				require.NoError(t, err)
147
148				exist1, err = repo.RefExist("refs/bugs/ref1")
149				require.NoError(t, err)
150				require.True(t, exist1)
151
152				ls, err := repo.ListRefs("refs/bugs")
153				require.NoError(t, err)
154				require.ElementsMatch(t, []string{"refs/bugs/ref1"}, ls)
155
156				err = repo.CopyRef("refs/bugs/ref1", "refs/bugs/ref2")
157				require.NoError(t, err)
158
159				ls, err = repo.ListRefs("refs/bugs")
160				require.NoError(t, err)
161				require.ElementsMatch(t, []string{"refs/bugs/ref1", "refs/bugs/ref2"}, ls)
162
163				commits, err := repo.ListCommits("refs/bugs/ref2")
164				require.NoError(t, err)
165				require.ElementsMatch(t, []Hash{commit1, commit2}, commits)
166
167				// Graph
168
169				commit3, err := repo.StoreCommitWithParent(treeHash1, commit1)
170				require.NoError(t, err)
171
172				ancestorHash, err := repo.FindCommonAncestor(commit2, commit3)
173				require.NoError(t, err)
174				require.Equal(t, commit1, ancestorHash)
175
176				err = repo.RemoveRef("refs/bugs/ref1")
177				require.NoError(t, err)
178			})
179
180			t.Run("Local config", func(t *testing.T) {
181				repo := creator(bare)
182				defer cleaner(repo)
183
184				testConfig(t, repo.LocalConfig())
185			})
186
187			t.Run("Clocks", func(t *testing.T) {
188				repo := creator(bare)
189				defer cleaner(repo)
190
191				clock, err := repo.GetOrCreateClock("foo")
192				require.NoError(t, err)
193				require.Equal(t, lamport.Time(1), clock.Time())
194
195				time, err := clock.Increment()
196				require.NoError(t, err)
197				require.Equal(t, lamport.Time(1), time)
198				require.Equal(t, lamport.Time(2), clock.Time())
199
200				clock2, err := repo.GetOrCreateClock("foo")
201				require.NoError(t, err)
202				require.Equal(t, lamport.Time(2), clock2.Time())
203
204				clock3, err := repo.GetOrCreateClock("bar")
205				require.NoError(t, err)
206				require.Equal(t, lamport.Time(1), clock3.Time())
207			})
208		})
209	}
210}
211
212func randomData() []byte {
213	var letterRunes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
214	b := make([]byte, 32)
215	for i := range b {
216		b[i] = letterRunes[rand.Intn(len(letterRunes))]
217	}
218	return b
219}