repo_testing.go

  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}