14 "github.com/git-lfs/gitobj"
15 "github.com/stretchr/testify/assert"
18 // DatabaseFromFixture returns a *gitobj.ObjectDatabase instance that is safely
19 // mutable and created from a template equivelant to the fixture that you
22 // If any error was encountered, it will call t.Fatalf() immediately.
23 func DatabaseFromFixture(t *testing.T, name string) *gitobj.ObjectDatabase {
24 path, err := copyToTmp(filepath.Join("fixtures", name))
26 t.Fatalf("gitobj: could not copy fixture %s: %v", name, err)
29 db, err := gitobj.FromFilesystem(filepath.Join(path, "objects"), "")
31 t.Fatalf("gitobj: could not create object database: %v", err)
36 // AssertBlobContents asserts that the blob contents given by loading the path
37 // starting from the root tree "tree" has the given "contents".
38 func AssertBlobContents(t *testing.T, db *gitobj.ObjectDatabase, tree, path, contents string) {
39 // First, load the root tree.
40 root, err := db.Tree(HexDecode(t, tree))
42 t.Fatalf("gitobj: cannot load tree: %s: %s", tree, err)
45 // Then, iterating through each part of the filepath (i.e., a/b/c.txt ->
46 // []string{"a", "b", "c.txt"}).
47 parts := strings.Split(path, "/")
48 for i := 0; i < len(parts)-1; i++ {
51 // Load the subtree given by that name.
52 var subtree *gitobj.Tree
53 for _, entry := range root.Entries {
54 if entry.Name != part {
58 subtree, err = db.Tree(entry.Oid)
60 t.Fatalf("gitobj: cannot load subtree %s: %s", filepath.Join(parts[:i]...), err)
66 t.Fatalf("gitobj: subtree %s does not exist", path)
69 // And re-assign it to root, creating a sort of pseudo-recursion.
73 filename := parts[len(parts)-1]
75 // Find the blob given by the last entry in parts (the filename).
77 for _, entry := range root.Entries {
78 if entry.Name == filename {
79 blob, err = db.Blob(entry.Oid)
81 t.Fatalf("gitobj: cannot load blob %x: %s", entry.Oid, err)
86 // If we couldn't find the blob, fail immediately.
88 t.Fatalf("gitobj: blob at %s in %s does not exist", path, tree)
91 // Perform an assertion on the blob's contents.
92 got, err := ioutil.ReadAll(blob.Contents)
94 t.Fatalf("gitobj: cannot read contents from blob %s: %s", path, err)
97 assert.Equal(t, contents, string(got))
100 // AssertCommitParent asserts that the given commit has a parent equivalent to
102 func AssertCommitParent(t *testing.T, db *gitobj.ObjectDatabase, sha, parent string) {
103 commit, err := db.Commit(HexDecode(t, sha))
105 t.Fatalf("gitobj: expected to read commit: %s, couldn't: %v", sha, err)
108 decoded, err := hex.DecodeString(parent)
110 t.Fatalf("gitobj: expected to decode parent SHA: %s, couldn't: %v", parent, err)
113 assert.Contains(t, commit.ParentIDs, decoded,
114 "gitobj: expected parents of commit: %s to contain: %s", sha, parent)
117 // AssertCommitTree asserts that the given commit has a tree equivelant to the
119 func AssertCommitTree(t *testing.T, db *gitobj.ObjectDatabase, sha, tree string) {
120 commit, err := db.Commit(HexDecode(t, sha))
122 t.Fatalf("gitobj: expected to read commit: %s, couldn't: %v", sha, err)
125 decoded, err := hex.DecodeString(tree)
127 t.Fatalf("gitobj: expected to decode tree SHA: %s, couldn't: %v", tree, err)
130 assert.Equal(t, decoded, commit.TreeID, "gitobj: expected tree ID: %s (got: %x)", tree, commit.TreeID)
133 // AssertRef asserts that a given refname points at the expected commit.
134 func AssertRef(t *testing.T, db *gitobj.ObjectDatabase, ref string, expected []byte) {
135 root, ok := db.Root()
136 assert.True(t, ok, "gitobj: expected *odb.ObjectDatabase to have Root()")
138 cmd := exec.Command("git", "rev-parse", ref)
140 out, err := cmd.Output()
144 assert.Equal(t, hex.EncodeToString(expected), strings.TrimSpace(string(out)))
147 // HexDecode decodes the given ASCII hex-encoded string into []byte's, or fails
148 // the test immediately if the given "sha" wasn't a valid hex-encoded sequence.
149 func HexDecode(t *testing.T, sha string) []byte {
150 b, err := hex.DecodeString(sha)
152 t.Fatalf("gitobj: could not decode string: %q, %v", sha, err)
158 // copyToTmp copies the given fixutre to a folder in /tmp.
159 func copyToTmp(fixture string) (string, error) {
160 p, err := ioutil.TempDir("", fmt.Sprintf("git-lfs-fixture-%s", filepath.Dir(fixture)))
165 if err = copyDir(fixture, p); err != nil {
171 // copyDir copies a directory (and recursively all files and subdirectories)
172 // from "from" to "to" preserving permissions and ownership.
173 func copyDir(from, to string) error {
174 stat, err := os.Stat(from)
179 if err := os.MkdirAll(to, stat.Mode()); err != nil {
183 entries, err := ioutil.ReadDir(from)
188 for _, entry := range entries {
189 sp := filepath.Join(from, entry.Name())
190 dp := filepath.Join(to, entry.Name())
193 err = copyDir(sp, dp)
195 err = copyFile(sp, dp)
205 // copyFile copies a file from "from" to "to" preserving permissions and
207 func copyFile(from, to string) error {
208 src, err := os.Open(from)
214 dst, err := os.Create(to)
220 if _, err = io.Copy(dst, src); err != nil {
224 stat, err := os.Stat(from)
229 return os.Chmod(to, stat.Mode())