repo_testing.go

  1package repository
  2
  3import (
  4	"log"
  5	"math/rand"
  6	"testing"
  7
  8	"github.com/stretchr/testify/require"
  9
 10	"github.com/MichaelMure/git-bug/util/lamport"
 11)
 12
 13func CleanupTestRepos(repos ...Repo) {
 14	var firstErr error
 15	for _, repo := range repos {
 16		if repo, ok := repo.(TestedRepo); ok {
 17			err := repo.EraseFromDisk()
 18			if err != nil {
 19				log.Println(err)
 20				if firstErr == nil {
 21					firstErr = err
 22				}
 23			}
 24		}
 25	}
 26
 27	if firstErr != nil {
 28		log.Fatal(firstErr)
 29	}
 30}
 31
 32type RepoCreator func(bare bool) TestedRepo
 33type RepoCleaner func(repos ...Repo)
 34
 35// Test suite for a Repo implementation
 36func RepoTest(t *testing.T, creator RepoCreator, cleaner RepoCleaner) {
 37	for bare, name := range map[bool]string{
 38		false: "Plain",
 39		true:  "Bare",
 40	} {
 41		t.Run(name, func(t *testing.T) {
 42			repo := creator(bare)
 43			defer cleaner(repo)
 44
 45			t.Run("Data", func(t *testing.T) {
 46				RepoDataTest(t, repo)
 47			})
 48
 49			t.Run("Config", func(t *testing.T) {
 50				RepoConfigTest(t, repo)
 51			})
 52
 53			t.Run("Clocks", func(t *testing.T) {
 54				RepoClockTest(t, repo)
 55			})
 56		})
 57	}
 58}
 59
 60// helper to test a RepoConfig
 61func RepoConfigTest(t *testing.T, repo RepoConfig) {
 62	testConfig(t, repo.LocalConfig())
 63}
 64
 65// helper to test a RepoData
 66func RepoDataTest(t *testing.T, repo RepoData) {
 67	// Blob
 68
 69	data := randomData()
 70
 71	blobHash1, err := repo.StoreData(data)
 72	require.NoError(t, err)
 73	require.True(t, blobHash1.IsValid())
 74
 75	blob1Read, err := repo.ReadData(blobHash1)
 76	require.NoError(t, err)
 77	require.Equal(t, data, blob1Read)
 78
 79	// Tree
 80
 81	blobHash2, err := repo.StoreData(randomData())
 82	require.NoError(t, err)
 83	blobHash3, err := repo.StoreData(randomData())
 84	require.NoError(t, err)
 85
 86	tree1 := []TreeEntry{
 87		{
 88			ObjectType: Blob,
 89			Hash:       blobHash1,
 90			Name:       "blob1",
 91		},
 92		{
 93			ObjectType: Blob,
 94			Hash:       blobHash2,
 95			Name:       "blob2",
 96		},
 97	}
 98
 99	treeHash1, err := repo.StoreTree(tree1)
100	require.NoError(t, err)
101	require.True(t, treeHash1.IsValid())
102
103	tree1Read, err := repo.ReadTree(treeHash1)
104	require.NoError(t, err)
105	require.ElementsMatch(t, tree1, tree1Read)
106
107	tree2 := []TreeEntry{
108		{
109			ObjectType: Tree,
110			Hash:       treeHash1,
111			Name:       "tree1",
112		},
113		{
114			ObjectType: Blob,
115			Hash:       blobHash3,
116			Name:       "blob3",
117		},
118	}
119
120	treeHash2, err := repo.StoreTree(tree2)
121	require.NoError(t, err)
122	require.True(t, treeHash2.IsValid())
123
124	tree2Read, err := repo.ReadTree(treeHash2)
125	require.NoError(t, err)
126	require.ElementsMatch(t, tree2, tree2Read)
127
128	// Commit
129
130	commit1, err := repo.StoreCommit(treeHash1)
131	require.NoError(t, err)
132	require.True(t, commit1.IsValid())
133
134	treeHash1Read, err := repo.GetTreeHash(commit1)
135	require.NoError(t, err)
136	require.Equal(t, treeHash1, treeHash1Read)
137
138	// commit with a parent
139	commit2, err := repo.StoreCommit(treeHash2, commit1)
140	require.NoError(t, err)
141	require.True(t, commit2.IsValid())
142
143	treeHash2Read, err := repo.GetTreeHash(commit2)
144	require.NoError(t, err)
145	require.Equal(t, treeHash2, treeHash2Read)
146
147	// ReadTree should accept tree and commit hashes
148	tree1read, err := repo.ReadTree(commit1)
149	require.NoError(t, err)
150	require.Equal(t, tree1read, tree1)
151
152	c2, err := repo.ReadCommit(commit2)
153	require.NoError(t, err)
154	c2expected := Commit{Hash: commit2, Parents: []Hash{commit1}, TreeHash: treeHash2}
155	require.Equal(t, c2expected, c2)
156
157	// Ref
158
159	exist1, err := repo.RefExist("refs/bugs/ref1")
160	require.NoError(t, err)
161	require.False(t, exist1)
162
163	err = repo.UpdateRef("refs/bugs/ref1", commit2)
164	require.NoError(t, err)
165
166	exist1, err = repo.RefExist("refs/bugs/ref1")
167	require.NoError(t, err)
168	require.True(t, exist1)
169
170	h, err := repo.ResolveRef("refs/bugs/ref1")
171	require.NoError(t, err)
172	require.Equal(t, commit2, h)
173
174	ls, err := repo.ListRefs("refs/bugs")
175	require.NoError(t, err)
176	require.ElementsMatch(t, []string{"refs/bugs/ref1"}, ls)
177
178	err = repo.CopyRef("refs/bugs/ref1", "refs/bugs/ref2")
179	require.NoError(t, err)
180
181	ls, err = repo.ListRefs("refs/bugs")
182	require.NoError(t, err)
183	require.ElementsMatch(t, []string{"refs/bugs/ref1", "refs/bugs/ref2"}, ls)
184
185	commits, err := repo.ListCommits("refs/bugs/ref2")
186	require.NoError(t, err)
187	require.Equal(t, []Hash{commit1, commit2}, commits)
188
189	// Graph
190
191	commit3, err := repo.StoreCommit(treeHash1, commit1)
192	require.NoError(t, err)
193
194	ancestorHash, err := repo.FindCommonAncestor(commit2, commit3)
195	require.NoError(t, err)
196	require.Equal(t, commit1, ancestorHash)
197
198	err = repo.RemoveRef("refs/bugs/ref1")
199	require.NoError(t, err)
200}
201
202// helper to test a RepoClock
203func RepoClockTest(t *testing.T, repo RepoClock) {
204	allClocks, err := repo.AllClocks()
205	require.NoError(t, err)
206	require.Len(t, allClocks, 0)
207
208	clock, err := repo.GetOrCreateClock("foo")
209	require.NoError(t, err)
210	require.Equal(t, lamport.Time(1), clock.Time())
211
212	time, err := clock.Increment()
213	require.NoError(t, err)
214	require.Equal(t, lamport.Time(2), time)
215	require.Equal(t, lamport.Time(2), clock.Time())
216
217	clock2, err := repo.GetOrCreateClock("foo")
218	require.NoError(t, err)
219	require.Equal(t, lamport.Time(2), clock2.Time())
220
221	clock3, err := repo.GetOrCreateClock("bar")
222	require.NoError(t, err)
223	require.Equal(t, lamport.Time(1), clock3.Time())
224
225	allClocks, err = repo.AllClocks()
226	require.NoError(t, err)
227	require.Equal(t, map[string]lamport.Clock{
228		"foo": clock,
229		"bar": clock3,
230	}, allClocks)
231}
232
233func randomData() []byte {
234	var letterRunes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
235	b := make([]byte, 32)
236	for i := range b {
237		b[i] = letterRunes[rand.Intn(len(letterRunes))]
238	}
239	return b
240}
241
242func makeCommit(t *testing.T, repo RepoData, parents ...Hash) Hash {
243	blobHash, err := repo.StoreData(randomData())
244	require.NoError(t, err)
245
246	treeHash, err := repo.StoreTree([]TreeEntry{
247		{
248			ObjectType: Blob,
249			Hash:       blobHash,
250			Name:       "foo",
251		},
252	})
253	require.NoError(t, err)
254
255	commitHash, err := repo.StoreCommit(treeHash, parents...)
256	require.NoError(t, err)
257
258	return commitHash
259}