repo_testing.go

  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}