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  }