Imported Upstream version 2.5.0
[scm/test.git] / git / githistory / fixtures_test.go
1 package githistory
2
3 import (
4         "encoding/hex"
5         "fmt"
6         "io"
7         "io/ioutil"
8         "os"
9         "os/exec"
10         "path/filepath"
11         "strings"
12         "testing"
13
14         "github.com/git-lfs/gitobj"
15         "github.com/stretchr/testify/assert"
16 )
17
18 // DatabaseFromFixture returns a *gitobj.ObjectDatabase instance that is safely
19 // mutable and created from a template equivelant to the fixture that you
20 // provided it.
21 //
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))
25         if err != nil {
26                 t.Fatalf("gitobj: could not copy fixture %s: %v", name, err)
27         }
28
29         db, err := gitobj.FromFilesystem(filepath.Join(path, "objects"), "")
30         if err != nil {
31                 t.Fatalf("gitobj: could not create object database: %v", err)
32         }
33         return db
34 }
35
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))
41         if err != nil {
42                 t.Fatalf("gitobj: cannot load tree: %s: %s", tree, err)
43         }
44
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++ {
49                 part := parts[i]
50
51                 // Load the subtree given by that name.
52                 var subtree *gitobj.Tree
53                 for _, entry := range root.Entries {
54                         if entry.Name != part {
55                                 continue
56                         }
57
58                         subtree, err = db.Tree(entry.Oid)
59                         if err != nil {
60                                 t.Fatalf("gitobj: cannot load subtree %s: %s", filepath.Join(parts[:i]...), err)
61                         }
62                         break
63                 }
64
65                 if subtree == nil {
66                         t.Fatalf("gitobj: subtree %s does not exist", path)
67                 }
68
69                 // And re-assign it to root, creating a sort of pseudo-recursion.
70                 root = subtree
71         }
72
73         filename := parts[len(parts)-1]
74
75         // Find the blob given by the last entry in parts (the filename).
76         var blob *gitobj.Blob
77         for _, entry := range root.Entries {
78                 if entry.Name == filename {
79                         blob, err = db.Blob(entry.Oid)
80                         if err != nil {
81                                 t.Fatalf("gitobj: cannot load blob %x: %s", entry.Oid, err)
82                         }
83                 }
84         }
85
86         // If we couldn't find the blob, fail immediately.
87         if blob == nil {
88                 t.Fatalf("gitobj: blob at %s in %s does not exist", path, tree)
89         }
90
91         // Perform an assertion on the blob's contents.
92         got, err := ioutil.ReadAll(blob.Contents)
93         if err != nil {
94                 t.Fatalf("gitobj: cannot read contents from blob %s: %s", path, err)
95         }
96
97         assert.Equal(t, contents, string(got))
98 }
99
100 // AssertCommitParent asserts that the given commit has a parent equivalent to
101 // the one provided.
102 func AssertCommitParent(t *testing.T, db *gitobj.ObjectDatabase, sha, parent string) {
103         commit, err := db.Commit(HexDecode(t, sha))
104         if err != nil {
105                 t.Fatalf("gitobj: expected to read commit: %s, couldn't: %v", sha, err)
106         }
107
108         decoded, err := hex.DecodeString(parent)
109         if err != nil {
110                 t.Fatalf("gitobj: expected to decode parent SHA: %s, couldn't: %v", parent, err)
111         }
112
113         assert.Contains(t, commit.ParentIDs, decoded,
114                 "gitobj: expected parents of commit: %s to contain: %s", sha, parent)
115 }
116
117 // AssertCommitTree asserts that the given commit has a tree equivelant to the
118 // one provided.
119 func AssertCommitTree(t *testing.T, db *gitobj.ObjectDatabase, sha, tree string) {
120         commit, err := db.Commit(HexDecode(t, sha))
121         if err != nil {
122                 t.Fatalf("gitobj: expected to read commit: %s, couldn't: %v", sha, err)
123         }
124
125         decoded, err := hex.DecodeString(tree)
126         if err != nil {
127                 t.Fatalf("gitobj: expected to decode tree SHA: %s, couldn't: %v", tree, err)
128         }
129
130         assert.Equal(t, decoded, commit.TreeID, "gitobj: expected tree ID: %s (got: %x)", tree, commit.TreeID)
131 }
132
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()")
137
138         cmd := exec.Command("git", "rev-parse", ref)
139         cmd.Dir = root
140         out, err := cmd.Output()
141
142         assert.Nil(t, err)
143
144         assert.Equal(t, hex.EncodeToString(expected), strings.TrimSpace(string(out)))
145 }
146
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)
151         if err != nil {
152                 t.Fatalf("gitobj: could not decode string: %q, %v", sha, err)
153         }
154
155         return b
156 }
157
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)))
161         if err != nil {
162                 return "", err
163         }
164
165         if err = copyDir(fixture, p); err != nil {
166                 return "", err
167         }
168         return p, nil
169 }
170
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)
175         if err != nil {
176                 return err
177         }
178
179         if err := os.MkdirAll(to, stat.Mode()); err != nil {
180                 return err
181         }
182
183         entries, err := ioutil.ReadDir(from)
184         if err != nil {
185                 return err
186         }
187
188         for _, entry := range entries {
189                 sp := filepath.Join(from, entry.Name())
190                 dp := filepath.Join(to, entry.Name())
191
192                 if entry.IsDir() {
193                         err = copyDir(sp, dp)
194                 } else {
195                         err = copyFile(sp, dp)
196                 }
197
198                 if err != nil {
199                         return err
200                 }
201         }
202         return nil
203 }
204
205 // copyFile copies a file from "from" to "to" preserving permissions and
206 // ownership.
207 func copyFile(from, to string) error {
208         src, err := os.Open(from)
209         if err != nil {
210                 return err
211         }
212         defer src.Close()
213
214         dst, err := os.Create(to)
215         if err != nil {
216                 return err
217         }
218         defer dst.Close()
219
220         if _, err = io.Copy(dst, src); err != nil {
221                 return err
222         }
223
224         stat, err := os.Stat(from)
225         if err != nil {
226                 return err
227         }
228
229         return os.Chmod(to, stat.Mode())
230 }