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 repo := creator(bare)
57 defer cleaner(repo)
58
59 t.Run("Data", func(t *testing.T) {
60 RepoDataTest(t, repo)
61 })
62
63 t.Run("Config", func(t *testing.T) {
64 RepoConfigTest(t, repo)
65 })
66
67 t.Run("Clocks", func(t *testing.T) {
68 RepoClockTest(t, repo)
69 })
70 })
71 }
72}
73
74// helper to test a RepoConfig
75func RepoConfigTest(t *testing.T, repo RepoConfig) {
76 testConfig(t, repo.LocalConfig())
77}
78
79// helper to test a RepoData
80func RepoDataTest(t *testing.T, repo RepoData) {
81 // Blob
82
83 data := randomData()
84
85 blobHash1, err := repo.StoreData(data)
86 require.NoError(t, err)
87 require.True(t, blobHash1.IsValid())
88
89 blob1Read, err := repo.ReadData(blobHash1)
90 require.NoError(t, err)
91 require.Equal(t, data, blob1Read)
92
93 // Tree
94
95 blobHash2, err := repo.StoreData(randomData())
96 require.NoError(t, err)
97 blobHash3, err := repo.StoreData(randomData())
98 require.NoError(t, err)
99
100 tree1 := []TreeEntry{
101 {
102 ObjectType: Blob,
103 Hash: blobHash1,
104 Name: "blob1",
105 },
106 {
107 ObjectType: Blob,
108 Hash: blobHash2,
109 Name: "blob2",
110 },
111 }
112
113 treeHash1, err := repo.StoreTree(tree1)
114 require.NoError(t, err)
115 require.True(t, treeHash1.IsValid())
116
117 tree1Read, err := repo.ReadTree(treeHash1)
118 require.NoError(t, err)
119 require.ElementsMatch(t, tree1, tree1Read)
120
121 tree2 := []TreeEntry{
122 {
123 ObjectType: Tree,
124 Hash: treeHash1,
125 Name: "tree1",
126 },
127 {
128 ObjectType: Blob,
129 Hash: blobHash3,
130 Name: "blob3",
131 },
132 }
133
134 treeHash2, err := repo.StoreTree(tree2)
135 require.NoError(t, err)
136 require.True(t, treeHash2.IsValid())
137
138 tree2Read, err := repo.ReadTree(treeHash2)
139 require.NoError(t, err)
140 require.ElementsMatch(t, tree2, tree2Read)
141
142 // Commit
143
144 commit1, err := repo.StoreCommit(treeHash1)
145 require.NoError(t, err)
146 require.True(t, commit1.IsValid())
147
148 treeHash1Read, err := repo.GetTreeHash(commit1)
149 require.NoError(t, err)
150 require.Equal(t, treeHash1, treeHash1Read)
151
152 commit2, err := repo.StoreCommitWithParent(treeHash2, commit1)
153 require.NoError(t, err)
154 require.True(t, commit2.IsValid())
155
156 treeHash2Read, err := repo.GetTreeHash(commit2)
157 require.NoError(t, err)
158 require.Equal(t, treeHash2, treeHash2Read)
159
160 // ReadTree should accept tree and commit hashes
161 tree1read, err := repo.ReadTree(commit1)
162 require.NoError(t, err)
163 require.Equal(t, tree1read, tree1)
164
165 // Ref
166
167 exist1, err := repo.RefExist("refs/bugs/ref1")
168 require.NoError(t, err)
169 require.False(t, exist1)
170
171 err = repo.UpdateRef("refs/bugs/ref1", commit2)
172 require.NoError(t, err)
173
174 exist1, err = repo.RefExist("refs/bugs/ref1")
175 require.NoError(t, err)
176 require.True(t, exist1)
177
178 ls, err := repo.ListRefs("refs/bugs")
179 require.NoError(t, err)
180 require.ElementsMatch(t, []string{"refs/bugs/ref1"}, ls)
181
182 err = repo.CopyRef("refs/bugs/ref1", "refs/bugs/ref2")
183 require.NoError(t, err)
184
185 ls, err = repo.ListRefs("refs/bugs")
186 require.NoError(t, err)
187 require.ElementsMatch(t, []string{"refs/bugs/ref1", "refs/bugs/ref2"}, ls)
188
189 commits, err := repo.ListCommits("refs/bugs/ref2")
190 require.NoError(t, err)
191 require.Equal(t, []Hash{commit1, commit2}, commits)
192
193 // Graph
194
195 commit3, err := repo.StoreCommitWithParent(treeHash1, commit1)
196 require.NoError(t, err)
197
198 ancestorHash, err := repo.FindCommonAncestor(commit2, commit3)
199 require.NoError(t, err)
200 require.Equal(t, commit1, ancestorHash)
201
202 err = repo.RemoveRef("refs/bugs/ref1")
203 require.NoError(t, err)
204}
205
206// helper to test a RepoClock
207func RepoClockTest(t *testing.T, repo RepoClock) {
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(1), 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
226func randomData() []byte {
227 var letterRunes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
228 b := make([]byte, 32)
229 for i := range b {
230 b[i] = letterRunes[rand.Intn(len(letterRunes))]
231 }
232 return b
233}