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