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 // TODO: testing for commit's signature
202}
203
204// helper to test a RepoClock
205func RepoClockTest(t *testing.T, repo RepoClock) {
206 allClocks, err := repo.AllClocks()
207 require.NoError(t, err)
208 require.Len(t, allClocks, 0)
209
210 clock, err := repo.GetOrCreateClock("foo")
211 require.NoError(t, err)
212 require.Equal(t, lamport.Time(1), clock.Time())
213
214 time, err := clock.Increment()
215 require.NoError(t, err)
216 require.Equal(t, lamport.Time(2), time)
217 require.Equal(t, lamport.Time(2), clock.Time())
218
219 clock2, err := repo.GetOrCreateClock("foo")
220 require.NoError(t, err)
221 require.Equal(t, lamport.Time(2), clock2.Time())
222
223 clock3, err := repo.GetOrCreateClock("bar")
224 require.NoError(t, err)
225 require.Equal(t, lamport.Time(1), clock3.Time())
226
227 allClocks, err = repo.AllClocks()
228 require.NoError(t, err)
229 require.Equal(t, map[string]lamport.Clock{
230 "foo": clock,
231 "bar": clock3,
232 }, allClocks)
233}
234
235func randomData() []byte {
236 var letterRunes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
237 b := make([]byte, 32)
238 for i := range b {
239 b[i] = letterRunes[rand.Intn(len(letterRunes))]
240 }
241 return b
242}