aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/difftree/change_adaptor_test.go
diff options
context:
space:
mode:
authorAlberto Cortés <alcortesm@gmail.com>2017-02-21 10:24:10 +0100
committerGitHub <noreply@github.com>2017-02-21 10:24:10 +0100
commit0b8b8da617d5a077f282e57d0300dc106a604236 (patch)
treef481480a0311764e2a306e079b48b5f9d33bc304 /plumbing/difftree/change_adaptor_test.go
parentefe9ecf9a2b4d9346a1ae105210b4dabb26aa2a8 (diff)
downloadgo-git-0b8b8da617d5a077f282e57d0300dc106a604236.tar.gz
difftree for git.Trees (#273)
Last PR to fix #82: This PR modifies the difftree package itself. The old version extracted the files in both trees and compare them by hand. The new version turn the trees into merkletrie.Noders and call the merkletrie.Difftree function on them. How to review this PR: treenoder.go: defines the treeNoder type that wraps a git.Tree and implements merkletrie.Noder. change.go: defines the type of the output of a difftree operation. The type is the same as before, but I have moved it into its own file to keep the package organized. The old package defines the Action type too (insert, delete, modify), now, we reuse merkletrie.Action and it is no longer a field, but a method. change_adaptor.go: defines functions to turn merkletrie.Changes into difftree.Changes. difftree.go: before this patch this file holds all the logic to do a difftree, now it just turns the git.Trees into treeNoders, call merkletrie.difftree on them, and turns the resulting merkletrie.Changes into difftree.Changes. The only interesting piece of code here is that noders don't have the concept of mode (file permissions). The treenoder type codifies git.Tree modes into the merkletrie.Noder hash, so changes in the mode of a file are detected as modifications, just as the original git diff-tree command does.
Diffstat (limited to 'plumbing/difftree/change_adaptor_test.go')
-rw-r--r--plumbing/difftree/change_adaptor_test.go413
1 files changed, 413 insertions, 0 deletions
diff --git a/plumbing/difftree/change_adaptor_test.go b/plumbing/difftree/change_adaptor_test.go
new file mode 100644
index 0000000..2ce2c54
--- /dev/null
+++ b/plumbing/difftree/change_adaptor_test.go
@@ -0,0 +1,413 @@
+package difftree
+
+import (
+ "os"
+ "sort"
+
+ "srcd.works/go-git.v4/plumbing"
+ "srcd.works/go-git.v4/plumbing/object"
+ "srcd.works/go-git.v4/plumbing/storer"
+ "srcd.works/go-git.v4/storage/filesystem"
+ "srcd.works/go-git.v4/utils/merkletrie"
+ "srcd.works/go-git.v4/utils/merkletrie/noder"
+
+ "github.com/src-d/go-git-fixtures"
+ . "gopkg.in/check.v1"
+)
+
+type ChangeAdaptorSuite struct {
+ fixtures.Suite
+ Storer storer.EncodedObjectStorer
+ Fixture *fixtures.Fixture
+}
+
+func (s *ChangeAdaptorSuite) SetUpSuite(c *C) {
+ s.Suite.SetUpSuite(c)
+ s.Fixture = fixtures.Basic().One()
+ sto, err := filesystem.NewStorage(s.Fixture.DotGit())
+ c.Assert(err, IsNil)
+ s.Storer = sto
+}
+
+func (s *ChangeAdaptorSuite) tree(c *C, h plumbing.Hash) *object.Tree {
+ t, err := object.GetTree(s.Storer, h)
+ c.Assert(err, IsNil)
+ return t
+}
+
+var _ = Suite(&ChangeAdaptorSuite{})
+
+// utility function to build Noders from a tree and an tree entry.
+func newNoder(t *object.Tree, e object.TreeEntry) noder.Noder {
+ return &treeNoder{
+ parent: t,
+ name: e.Name,
+ mode: e.Mode,
+ hash: e.Hash,
+ }
+}
+
+// utility function to build Paths
+func newPath(nn ...noder.Noder) noder.Path { return noder.Path(nn) }
+
+func (s *ChangeAdaptorSuite) TestTreeNoderHashHasMode(c *C) {
+ hash := plumbing.NewHash("aaaa")
+ mode := object.FileMode
+
+ treeNoder := &treeNoder{
+ hash: hash,
+ mode: mode,
+ }
+
+ expected := []byte{
+ 0xaa, 0xaa, 0x00, 0x00, // original hash is aaaa and 16 zeros
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xa4, 0x81, 0x00, 0x00, // object.FileMode in little endian
+ }
+
+ c.Assert(treeNoder.Hash(), DeepEquals, expected)
+}
+
+func (s *ChangeAdaptorSuite) TestNewChangeInsert(c *C) {
+ tree := &object.Tree{}
+ entry := object.TreeEntry{
+ Name: "name",
+ Mode: os.FileMode(42),
+ Hash: plumbing.NewHash("aaaaa"),
+ }
+ path := newPath(newNoder(tree, entry))
+
+ expectedTo, err := newChangeEntry(path)
+ c.Assert(err, IsNil)
+
+ src := merkletrie.Change{
+ From: nil,
+ To: path,
+ }
+
+ obtained, err := newChange(src)
+ c.Assert(err, IsNil)
+ action, err := obtained.Action()
+ c.Assert(err, IsNil)
+ c.Assert(action, Equals, merkletrie.Insert)
+ c.Assert(obtained.From, Equals, ChangeEntry{})
+ c.Assert(obtained.To, Equals, expectedTo)
+}
+
+func (s *ChangeAdaptorSuite) TestNewChangeDelete(c *C) {
+ tree := &object.Tree{}
+ entry := object.TreeEntry{
+ Name: "name",
+ Mode: os.FileMode(42),
+ Hash: plumbing.NewHash("aaaaa"),
+ }
+ path := newPath(newNoder(tree, entry))
+
+ expectedFrom, err := newChangeEntry(path)
+ c.Assert(err, IsNil)
+
+ src := merkletrie.Change{
+ From: path,
+ To: nil,
+ }
+
+ obtained, err := newChange(src)
+ c.Assert(err, IsNil)
+ action, err := obtained.Action()
+ c.Assert(err, IsNil)
+ c.Assert(action, Equals, merkletrie.Delete)
+ c.Assert(obtained.From, Equals, expectedFrom)
+ c.Assert(obtained.To, Equals, ChangeEntry{})
+}
+
+func (s *ChangeAdaptorSuite) TestNewChangeModify(c *C) {
+ treeA := &object.Tree{}
+ entryA := object.TreeEntry{
+ Name: "name",
+ Mode: os.FileMode(42),
+ Hash: plumbing.NewHash("aaaaa"),
+ }
+ pathA := newPath(newNoder(treeA, entryA))
+ expectedFrom, err := newChangeEntry(pathA)
+ c.Assert(err, IsNil)
+
+ treeB := &object.Tree{}
+ entryB := object.TreeEntry{
+ Name: "name",
+ Mode: os.FileMode(42),
+ Hash: plumbing.NewHash("bbbb"),
+ }
+ pathB := newPath(newNoder(treeB, entryB))
+ expectedTo, err := newChangeEntry(pathB)
+ c.Assert(err, IsNil)
+
+ src := merkletrie.Change{
+ From: pathA,
+ To: pathB,
+ }
+
+ obtained, err := newChange(src)
+ c.Assert(err, IsNil)
+ action, err := obtained.Action()
+ c.Assert(err, IsNil)
+ c.Assert(action, Equals, merkletrie.Modify)
+ c.Assert(obtained.From, Equals, expectedFrom)
+ c.Assert(obtained.To, Equals, expectedTo)
+}
+
+func (s *ChangeAdaptorSuite) TestEmptyChangeFails(c *C) {
+ change := &Change{
+ From: empty,
+ To: empty,
+ }
+ _, err := change.Action()
+ c.Assert(err, ErrorMatches, "malformed change.*")
+
+ _, _, err = change.Files()
+ c.Assert(err, ErrorMatches, "malformed change.*")
+
+ str := change.String()
+ c.Assert(str, Equals, "malformed change")
+}
+
+type noderMock struct{ noder.Noder }
+
+func (s *ChangeAdaptorSuite) TestNewChangeFailsWithChangesFromOtherNoders(c *C) {
+ src := merkletrie.Change{
+ From: newPath(noderMock{}),
+ To: nil,
+ }
+ _, err := newChange(src)
+ c.Assert(err, Not(IsNil))
+
+ src = merkletrie.Change{
+ From: nil,
+ To: newPath(noderMock{}),
+ }
+ _, err = newChange(src)
+ c.Assert(err, Not(IsNil))
+}
+
+func (s *ChangeAdaptorSuite) TestChangeStringFrom(c *C) {
+ expected := "<Action: Delete, Path: foo>"
+ change := Change{}
+ change.From.Name = "foo"
+
+ obtained := change.String()
+ c.Assert(obtained, Equals, expected)
+}
+
+func (s *ChangeAdaptorSuite) TestChangeStringTo(c *C) {
+ expected := "<Action: Insert, Path: foo>"
+ change := Change{}
+ change.To.Name = "foo"
+
+ obtained := change.String()
+ c.Assert(obtained, Equals, expected)
+}
+
+func (s *ChangeAdaptorSuite) TestChangeFilesInsert(c *C) {
+ tree := s.tree(c, plumbing.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c"))
+
+ change := Change{}
+ change.To.Name = "json/long.json"
+ change.To.Tree = tree
+ change.To.TreeEntry.Hash = plumbing.NewHash("49c6bb89b17060d7b4deacb7b338fcc6ea2352a9")
+
+ from, to, err := change.Files()
+ c.Assert(err, IsNil)
+ c.Assert(from, IsNil)
+ c.Assert(to.ID(), Equals, change.To.TreeEntry.Hash)
+}
+
+func (s *ChangeAdaptorSuite) TestChangeFilesInsertNotFound(c *C) {
+ tree := s.tree(c, plumbing.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c"))
+
+ change := Change{}
+ change.To.Name = "json/long.json"
+ change.To.Tree = tree
+ // there is no object for this hash
+ change.To.TreeEntry.Hash = plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+
+ _, _, err := change.Files()
+ c.Assert(err, Not(IsNil))
+}
+
+func (s *ChangeAdaptorSuite) TestChangeFilesDelete(c *C) {
+ tree := s.tree(c, plumbing.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c"))
+
+ change := Change{}
+ change.From.Name = "json/long.json"
+ change.From.Tree = tree
+ change.From.TreeEntry.Hash = plumbing.NewHash("49c6bb89b17060d7b4deacb7b338fcc6ea2352a9")
+
+ from, to, err := change.Files()
+ c.Assert(err, IsNil)
+ c.Assert(to, IsNil)
+ c.Assert(from.ID(), Equals, change.From.TreeEntry.Hash)
+}
+
+func (s *ChangeAdaptorSuite) TestChangeFilesDeleteNotFound(c *C) {
+ tree := s.tree(c, plumbing.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c"))
+
+ change := Change{}
+ change.From.Name = "json/long.json"
+ change.From.Tree = tree
+ // there is no object for this hash
+ change.From.TreeEntry.Hash = plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+
+ _, _, err := change.Files()
+ c.Assert(err, Not(IsNil))
+}
+
+func (s *ChangeAdaptorSuite) TestChangeFilesModify(c *C) {
+ tree := s.tree(c, plumbing.NewHash("a8d315b2b1c615d43042c3a62402b8a54288cf5c"))
+
+ change := Change{}
+ change.To.Name = "json/long.json"
+ change.To.Tree = tree
+ change.To.TreeEntry.Hash = plumbing.NewHash("49c6bb89b17060d7b4deacb7b338fcc6ea2352a9")
+ change.From.Name = "json/long.json"
+ change.From.Tree = tree
+ change.From.TreeEntry.Hash = plumbing.NewHash("9a48f23120e880dfbe41f7c9b7b708e9ee62a492")
+
+ from, to, err := change.Files()
+ c.Assert(err, IsNil)
+ c.Assert(to.ID(), Equals, change.To.TreeEntry.Hash)
+ c.Assert(from.ID(), Equals, change.From.TreeEntry.Hash)
+}
+
+func (s *ChangeAdaptorSuite) TestChangeEntryFailsWithOtherNoders(c *C) {
+ path := noder.Path{noderMock{}}
+ _, err := newChangeEntry(path)
+ c.Assert(err, Not(IsNil))
+}
+
+func (s *ChangeAdaptorSuite) TestChangeEntryFromNilIsZero(c *C) {
+ obtained, err := newChangeEntry(nil)
+ c.Assert(err, IsNil)
+ c.Assert(obtained, Equals, ChangeEntry{})
+}
+
+func (s *ChangeAdaptorSuite) TestChangeEntryFromSortPath(c *C) {
+ tree := &object.Tree{}
+ entry := object.TreeEntry{
+ Name: "name",
+ Mode: os.FileMode(42),
+ Hash: plumbing.NewHash("aaaaa"),
+ }
+ path := newPath(newNoder(tree, entry))
+
+ obtained, err := newChangeEntry(path)
+ c.Assert(err, IsNil)
+
+ c.Assert(obtained.Name, Equals, entry.Name)
+ c.Assert(obtained.Tree, Equals, tree)
+ c.Assert(obtained.TreeEntry, DeepEquals, entry)
+}
+
+func (s *ChangeAdaptorSuite) TestChangeEntryFromLongPath(c *C) {
+ treeA := &object.Tree{}
+ entryA := object.TreeEntry{
+ Name: "nameA",
+ Mode: os.FileMode(42),
+ Hash: plumbing.NewHash("aaaa"),
+ }
+
+ treeB := &object.Tree{}
+ entryB := object.TreeEntry{
+ Name: "nameB",
+ Mode: os.FileMode(24),
+ Hash: plumbing.NewHash("bbbb"),
+ }
+
+ path := newPath(
+ newNoder(treeA, entryA),
+ newNoder(treeB, entryB),
+ )
+
+ obtained, err := newChangeEntry(path)
+ c.Assert(err, IsNil)
+
+ c.Assert(obtained.Name, Equals, entryA.Name+"/"+entryB.Name)
+ c.Assert(obtained.Tree, Equals, treeB)
+ c.Assert(obtained.TreeEntry, Equals, entryB)
+}
+
+func (s *ChangeAdaptorSuite) TestNewChangesEmpty(c *C) {
+ expected := "[]"
+ changes, err := newChanges(nil)
+ c.Assert(err, IsNil)
+ obtained := changes.String()
+ c.Assert(obtained, Equals, expected)
+
+ expected = "[]"
+ changes, err = newChanges(merkletrie.Changes{})
+ c.Assert(err, IsNil)
+ obtained = changes.String()
+ c.Assert(obtained, Equals, expected)
+}
+
+func (s *ChangeAdaptorSuite) TestNewChanges(c *C) {
+ treeA := &object.Tree{}
+ entryA := object.TreeEntry{Name: "nameA"}
+ pathA := newPath(newNoder(treeA, entryA))
+ changeA := merkletrie.Change{
+ From: nil,
+ To: pathA,
+ }
+
+ treeB := &object.Tree{}
+ entryB := object.TreeEntry{Name: "nameB"}
+ pathB := newPath(newNoder(treeB, entryB))
+ changeB := merkletrie.Change{
+ From: pathB,
+ To: nil,
+ }
+ src := merkletrie.Changes{changeA, changeB}
+
+ changes, err := newChanges(src)
+ c.Assert(err, IsNil)
+ c.Assert(len(changes), Equals, 2)
+ action, err := changes[0].Action()
+ c.Assert(err, IsNil)
+ c.Assert(action, Equals, merkletrie.Insert)
+ c.Assert(changes[0].To.Name, Equals, "nameA")
+ action, err = changes[1].Action()
+ c.Assert(err, IsNil)
+ c.Assert(action, Equals, merkletrie.Delete)
+ c.Assert(changes[1].From.Name, Equals, "nameB")
+}
+
+func (s *ChangeAdaptorSuite) TestNewChangesFailsWithOtherNoders(c *C) {
+ change := merkletrie.Change{
+ From: nil,
+ To: newPath(noderMock{}),
+ }
+ src := merkletrie.Changes{change}
+
+ _, err := newChanges(src)
+ c.Assert(err, Not(IsNil))
+}
+
+func (s *ChangeAdaptorSuite) TestSortChanges(c *C) {
+ c1 := &Change{}
+ c1.To.Name = "1"
+
+ c2 := &Change{}
+ c2.From.Name = "2"
+ c2.To.Name = "2"
+
+ c3 := &Change{}
+ c3.From.Name = "3"
+
+ changes := Changes{c3, c1, c2}
+ sort.Sort(changes)
+
+ c.Assert(changes[0].String(), Equals, "<Action: Insert, Path: 1>")
+ c.Assert(changes[1].String(), Equals, "<Action: Modify, Path: 2>")
+ c.Assert(changes[2].String(), Equals, "<Action: Delete, Path: 3>")
+}