github.com/x-oss-byte/git-lfs@v2.5.2+incompatible/git/githistory/fixtures_test.go (about) 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 }