moved file* sources
This commit is contained in:
parent
8f3e4b42d7
commit
94d27468fb
62
filechangeinfo.go
Normal file
62
filechangeinfo.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"bytes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileChangeInfo struct {
|
||||||
|
path string
|
||||||
|
typeflag byte
|
||||||
|
md5sum [16]byte
|
||||||
|
diffType DiffType
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiffType int
|
||||||
|
|
||||||
|
// enum to show whether a file has changed
|
||||||
|
const (
|
||||||
|
Unchanged DiffType = iota
|
||||||
|
Changed
|
||||||
|
Added
|
||||||
|
Removed
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d DiffType) String() string {
|
||||||
|
switch d {
|
||||||
|
case Unchanged:
|
||||||
|
return "Unchanged"
|
||||||
|
case Changed:
|
||||||
|
return "Changed"
|
||||||
|
case Added:
|
||||||
|
return "Added"
|
||||||
|
case Removed:
|
||||||
|
return "Removed"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%d", int(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a DiffType) merge(b DiffType) DiffType {
|
||||||
|
if a == b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return Changed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *FileChangeInfo) getDiffType(b *FileChangeInfo) DiffType {
|
||||||
|
if a == nil && b == nil {
|
||||||
|
return Unchanged
|
||||||
|
}
|
||||||
|
if a == nil || b == nil {
|
||||||
|
return Changed
|
||||||
|
}
|
||||||
|
if a.typeflag == b.typeflag {
|
||||||
|
if bytes.Compare(a.md5sum[:], b.md5sum[:]) == 0 {
|
||||||
|
return Unchanged
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Changed
|
||||||
|
}
|
||||||
|
|
||||||
|
|
64
filechangeinfo_test.go
Normal file
64
filechangeinfo_test.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAssignDiffType(t *testing.T) {
|
||||||
|
tree := NewTree()
|
||||||
|
tree.AddPath("/usr", BlankFileChangeInfo("/usr", Changed))
|
||||||
|
if tree.root.children["usr"].data.diffType != Changed {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeDiffTypes(t *testing.T) {
|
||||||
|
a := Unchanged
|
||||||
|
b := Unchanged
|
||||||
|
merged := a.merge(b)
|
||||||
|
if merged != Unchanged {
|
||||||
|
t.Errorf("Expected Unchaged (0) but got %v", merged)
|
||||||
|
}
|
||||||
|
a = Changed
|
||||||
|
b = Unchanged
|
||||||
|
merged = a.merge(b)
|
||||||
|
if merged != Changed {
|
||||||
|
t.Errorf("Expected Unchaged (0) but got %v", merged)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDiffTypeFromChildren(t *testing.T) {
|
||||||
|
tree := NewTree()
|
||||||
|
tree.AddPath("/usr", BlankFileChangeInfo("/usr", Unchanged))
|
||||||
|
info1 := BlankFileChangeInfo("/usr/bin", Added)
|
||||||
|
tree.AddPath("/usr/bin", info1)
|
||||||
|
info2 := BlankFileChangeInfo("/usr/bin2", Removed)
|
||||||
|
tree.AddPath("/usr/bin2", info2)
|
||||||
|
tree.root.children["usr"].deriveDiffType(Unchanged)
|
||||||
|
if tree.root.children["usr"].data.diffType != Changed {
|
||||||
|
t.Errorf("Expected Changed but got %v", tree.root.children["usr"].data.diffType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertDiffType(node *FileNode, expectedDiffType DiffType, t *testing.T) error {
|
||||||
|
if node.data == nil {
|
||||||
|
t.Errorf("Expected *FileChangeInfo but got nil at path %s", node.Path())
|
||||||
|
return fmt.Errorf("expected *FileChangeInfo but got nil at path %s", node.Path())
|
||||||
|
}
|
||||||
|
if node.data.diffType != expectedDiffType {
|
||||||
|
t.Errorf("Expecting node at %s to have DiffType %v, but had %v", node.Path(), expectedDiffType, node.data.diffType)
|
||||||
|
return fmt.Errorf("Assertion failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BlankFileChangeInfo(path string, diffType DiffType) (f *FileChangeInfo) {
|
||||||
|
result := FileChangeInfo{
|
||||||
|
path: path,
|
||||||
|
typeflag: 1,
|
||||||
|
md5sum: [16]byte{1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
diffType: diffType,
|
||||||
|
}
|
||||||
|
return &result
|
||||||
|
}
|
189
filenode.go
Normal file
189
filenode.go
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileNode struct {
|
||||||
|
tree *FileTree
|
||||||
|
parent *FileNode
|
||||||
|
name string
|
||||||
|
collapsed bool
|
||||||
|
data *FileChangeInfo
|
||||||
|
children map[string]*FileNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNode(parent *FileNode, name string, data *FileChangeInfo) (node *FileNode) {
|
||||||
|
node = new(FileNode)
|
||||||
|
node.name = name
|
||||||
|
if data == nil {
|
||||||
|
data = &FileChangeInfo{}
|
||||||
|
}
|
||||||
|
node.data = data
|
||||||
|
node.children = make(map[string]*FileNode)
|
||||||
|
node.parent = parent
|
||||||
|
if parent != nil {
|
||||||
|
node.tree = parent.tree
|
||||||
|
}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *FileNode) Copy() *FileNode {
|
||||||
|
// newNode := new(FileNode)
|
||||||
|
// *newNode = *node
|
||||||
|
// return newNode
|
||||||
|
newNode := NewNode(node.parent, node.name, node.data)
|
||||||
|
for name, child := range node.children {
|
||||||
|
newNode.children[name] = child.Copy()
|
||||||
|
}
|
||||||
|
return newNode
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (node *FileNode) AddChild(name string, data *FileChangeInfo) (child *FileNode) {
|
||||||
|
child = NewNode(node, name, data)
|
||||||
|
if node.children[name] != nil {
|
||||||
|
// tree node already exists, replace the payload, keep the children
|
||||||
|
node.children[name].data = data
|
||||||
|
} else {
|
||||||
|
node.children[name] = child
|
||||||
|
node.tree.size++
|
||||||
|
}
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *FileNode) Remove() error {
|
||||||
|
for _, child := range node.children {
|
||||||
|
child.Remove()
|
||||||
|
}
|
||||||
|
delete(node.parent.children, node.name)
|
||||||
|
node.tree.size--
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *FileNode) String() string {
|
||||||
|
return node.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *FileNode) Visit(visiter Visiter) error {
|
||||||
|
var keys []string
|
||||||
|
for key := range node.children {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
for _, name := range keys {
|
||||||
|
child := node.children[name]
|
||||||
|
err := child.Visit(visiter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return visiter(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (node *FileNode) VisitDepthParentFirst(visiter Visiter, evaluator VisitEvaluator) error {
|
||||||
|
err := visiter(node)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys []string
|
||||||
|
for key := range node.children {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
for _, name := range keys {
|
||||||
|
child := node.children[name]
|
||||||
|
if evaluator == nil || !evaluator(node) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = child.VisitDepthParentFirst(visiter, evaluator)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *FileNode) IsWhiteout() bool {
|
||||||
|
return strings.HasPrefix(node.name, whiteoutPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *FileNode) Path() string {
|
||||||
|
path := []string{}
|
||||||
|
curNode := node
|
||||||
|
for {
|
||||||
|
if curNode.parent == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
name := curNode.name
|
||||||
|
if curNode == node {
|
||||||
|
// white out prefixes are fictitious on leaf nodes
|
||||||
|
name = strings.TrimPrefix(name, whiteoutPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
path = append([]string{name}, path...)
|
||||||
|
curNode = curNode.parent
|
||||||
|
}
|
||||||
|
return "/" + strings.Join(path, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (node *FileNode) IsLeaf() bool {
|
||||||
|
return len(node.children) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *FileNode) deriveDiffType(diffType DiffType) error {
|
||||||
|
// THE DIFF_TYPE OF A NODE IS ALWAYS THE DIFF_TYPE OF ITS ATTRIBUTES AND ITS CONTENTS
|
||||||
|
// THE CONTENTS ARE THE BYTES OF A FILE OR THE CHILDREN OF A DIRECTORY
|
||||||
|
|
||||||
|
if node.IsLeaf() {
|
||||||
|
node.AssignDiffType(diffType)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
myDiffType := diffType
|
||||||
|
|
||||||
|
for _, v := range node.children {
|
||||||
|
vData := v.data
|
||||||
|
myDiffType = myDiffType.merge(vData.diffType)
|
||||||
|
|
||||||
|
}
|
||||||
|
node.AssignDiffType(myDiffType)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *FileNode) AssignDiffType(diffType DiffType) error {
|
||||||
|
if node.Path() == "/" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
node.data.diffType = diffType
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *FileNode) compare(b *FileNode) DiffType {
|
||||||
|
if a == nil && b == nil {
|
||||||
|
return Unchanged
|
||||||
|
}
|
||||||
|
// a is nil but not b
|
||||||
|
if a == nil && b != nil {
|
||||||
|
return Added
|
||||||
|
}
|
||||||
|
|
||||||
|
// b is nil but not a
|
||||||
|
if a != nil && b == nil {
|
||||||
|
return Removed
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.IsWhiteout() {
|
||||||
|
return Removed
|
||||||
|
}
|
||||||
|
if a.name != b.name {
|
||||||
|
panic("comparing mismatched nodes")
|
||||||
|
}
|
||||||
|
// TODO: fails on nil
|
||||||
|
|
||||||
|
return a.data.getDiffType(b.data)
|
||||||
|
}
|
109
filenode_test.go
Normal file
109
filenode_test.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
|
||||||
|
func TestAddChild(t *testing.T) {
|
||||||
|
var expected, actual int
|
||||||
|
tree := NewTree()
|
||||||
|
|
||||||
|
payload := FileChangeInfo{
|
||||||
|
path: "stufffffs",
|
||||||
|
}
|
||||||
|
|
||||||
|
one := tree.Root().AddChild("first node!", &payload)
|
||||||
|
|
||||||
|
two := tree.Root().AddChild("nil node!", nil)
|
||||||
|
|
||||||
|
tree.Root().AddChild("third node!", nil)
|
||||||
|
two.AddChild("forth, one level down...", nil)
|
||||||
|
two.AddChild("fifth, one level down...", nil)
|
||||||
|
two.AddChild("fifth, one level down...", nil)
|
||||||
|
|
||||||
|
expected, actual = 5, tree.size
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("Expected a tree size of %d got %d.", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected, actual = 2, len(two.children)
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("Expected 'twos' number of children to be %d got %d.", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected, actual = 3, len(tree.Root().children)
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("Expected 'twos' number of children to be %d got %d.", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedFC := &FileChangeInfo{
|
||||||
|
path: "stufffffs",
|
||||||
|
}
|
||||||
|
actualFC := one.data
|
||||||
|
if *expectedFC != *actualFC {
|
||||||
|
t.Errorf("Expected 'ones' payload to be %+v got %+v.", expectedFC, actualFC)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *two.data != *new(FileChangeInfo) {
|
||||||
|
t.Errorf("Expected 'twos' payload to be nil got %d.", two.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveChild(t *testing.T) {
|
||||||
|
var expected, actual int
|
||||||
|
|
||||||
|
tree := NewTree()
|
||||||
|
tree.Root().AddChild("first", nil)
|
||||||
|
two := tree.Root().AddChild("nil", nil)
|
||||||
|
tree.Root().AddChild("third", nil)
|
||||||
|
forth := two.AddChild("forth", nil)
|
||||||
|
two.AddChild("fifth", nil)
|
||||||
|
|
||||||
|
forth.Remove()
|
||||||
|
|
||||||
|
expected, actual = 4, tree.size
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("Expected a tree size of %d got %d.", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tree.Root().children["forth"] != nil {
|
||||||
|
t.Errorf("Expected 'forth' node to be deleted.")
|
||||||
|
}
|
||||||
|
|
||||||
|
two.Remove()
|
||||||
|
|
||||||
|
expected, actual = 2, tree.size
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("Expected a tree size of %d got %d.", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tree.Root().children["nil"] != nil {
|
||||||
|
t.Errorf("Expected 'nil' node to be deleted.")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPath(t *testing.T) {
|
||||||
|
expected := "/etc/nginx/nginx.conf"
|
||||||
|
tree := NewTree()
|
||||||
|
node, _ := tree.AddPath(expected, nil)
|
||||||
|
|
||||||
|
actual := node.Path()
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("Expected path '%s' got '%s'", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsWhiteout(t *testing.T) {
|
||||||
|
tree1 := NewTree()
|
||||||
|
p1, _ := tree1.AddPath("/etc/nginx/public1", nil)
|
||||||
|
p2, _ := tree1.AddPath("/etc/nginx/.wh.public2", nil)
|
||||||
|
|
||||||
|
if p1.IsWhiteout() != false {
|
||||||
|
t.Errorf("Expected path '%s' to **not** be a whiteout file", p1.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p2.IsWhiteout() != true {
|
||||||
|
t.Errorf("Expected path '%s' to be a whiteout file", p2.name)
|
||||||
|
}
|
||||||
|
}
|
@ -20,76 +20,27 @@ const (
|
|||||||
|
|
||||||
|
|
||||||
type FileTree struct {
|
type FileTree struct {
|
||||||
root *Node
|
root *FileNode
|
||||||
size int
|
size int
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Node struct {
|
|
||||||
tree *FileTree
|
|
||||||
parent *Node
|
|
||||||
name string
|
|
||||||
collapsed bool
|
|
||||||
data *FileChangeInfo
|
|
||||||
children map[string]*Node
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTree() (tree *FileTree) {
|
func NewTree() (tree *FileTree) {
|
||||||
tree = new(FileTree)
|
tree = new(FileTree)
|
||||||
tree.size = 0
|
tree.size = 0
|
||||||
tree.root = new(Node)
|
tree.root = new(FileNode)
|
||||||
tree.root.tree = tree
|
tree.root.tree = tree
|
||||||
tree.root.children = make(map[string]*Node)
|
tree.root.children = make(map[string]*FileNode)
|
||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNode(parent *Node, name string, data *FileChangeInfo) (node *Node) {
|
func (tree *FileTree) Root() *FileNode {
|
||||||
node = new(Node)
|
|
||||||
node.name = name
|
|
||||||
if data == nil {
|
|
||||||
data = &FileChangeInfo{}
|
|
||||||
}
|
|
||||||
node.data = data
|
|
||||||
node.children = make(map[string]*Node)
|
|
||||||
node.parent = parent
|
|
||||||
if parent != nil {
|
|
||||||
node.tree = parent.tree
|
|
||||||
}
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tree *FileTree) Root() *Node {
|
|
||||||
return tree.root
|
return tree.root
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *Node) AddChild(name string, data *FileChangeInfo) (child *Node) {
|
|
||||||
child = NewNode(node, name, data)
|
|
||||||
if node.children[name] != nil {
|
|
||||||
// tree node already exists, replace the payload, keep the children
|
|
||||||
node.children[name].data = data
|
|
||||||
} else {
|
|
||||||
node.children[name] = child
|
|
||||||
node.tree.size++
|
|
||||||
}
|
|
||||||
return child
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node *Node) Remove() error {
|
|
||||||
for _, child := range node.children {
|
|
||||||
child.Remove()
|
|
||||||
}
|
|
||||||
delete(node.parent.children, node.name)
|
|
||||||
node.tree.size--
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node *Node) String() string {
|
|
||||||
return node.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tree *FileTree) String() string {
|
func (tree *FileTree) String() string {
|
||||||
var renderLine func(string, []bool, bool, bool) string
|
var renderLine func(string, []bool, bool, bool) string
|
||||||
var walkTree func(*Node, []bool, int) string
|
var walkTree func(*FileNode, []bool, int) string
|
||||||
|
|
||||||
renderLine = func(nodeText string, spaces []bool, last bool, collapsed bool) string {
|
renderLine = func(nodeText string, spaces []bool, last bool, collapsed bool) string {
|
||||||
var otherBranches string
|
var otherBranches string
|
||||||
@ -114,7 +65,7 @@ func (tree *FileTree) String() string {
|
|||||||
return otherBranches + thisBranch + collapsedIndicator + nodeText + newLine
|
return otherBranches + thisBranch + collapsedIndicator + nodeText + newLine
|
||||||
}
|
}
|
||||||
|
|
||||||
walkTree = func(node *Node, spaces []bool, depth int) string {
|
walkTree = func(node *FileNode, spaces []bool, depth int) string {
|
||||||
var result string
|
var result string
|
||||||
var keys []string
|
var keys []string
|
||||||
for key := range node.children {
|
for key := range node.children {
|
||||||
@ -137,16 +88,6 @@ func (tree *FileTree) String() string {
|
|||||||
return "." + newLine + walkTree(tree.Root(), []bool{}, 0)
|
return "." + newLine + walkTree(tree.Root(), []bool{}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *Node) Copy() *Node {
|
|
||||||
// newNode := new(Node)
|
|
||||||
// *newNode = *node
|
|
||||||
// return newNode
|
|
||||||
newNode := NewNode(node.parent, node.name, node.data)
|
|
||||||
for name, child := range node.children {
|
|
||||||
newNode.children[name] = child.Copy()
|
|
||||||
}
|
|
||||||
return newNode
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tree *FileTree) Copy() *FileTree {
|
func (tree *FileTree) Copy() *FileTree {
|
||||||
newTree := NewTree()
|
newTree := NewTree()
|
||||||
@ -156,8 +97,8 @@ func (tree *FileTree) Copy() *FileTree {
|
|||||||
return newTree
|
return newTree
|
||||||
}
|
}
|
||||||
|
|
||||||
type Visiter func(*Node) error
|
type Visiter func(*FileNode) error
|
||||||
type VisitEvaluator func(*Node) bool
|
type VisitEvaluator func(*FileNode) bool
|
||||||
|
|
||||||
func (tree *FileTree) Visit(visiter Visiter) error {
|
func (tree *FileTree) Visit(visiter Visiter) error {
|
||||||
return tree.root.Visit(visiter)
|
return tree.root.Visit(visiter)
|
||||||
@ -167,72 +108,8 @@ func (tree *FileTree) VisitDepthParentFirst(visiter Visiter, evaluator VisitEval
|
|||||||
return tree.root.VisitDepthParentFirst(visiter, evaluator)
|
return tree.root.VisitDepthParentFirst(visiter, evaluator)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *Node) Visit(visiter Visiter) error {
|
|
||||||
var keys []string
|
|
||||||
for key := range node.children {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
for _, name := range keys {
|
|
||||||
child := node.children[name]
|
|
||||||
err := child.Visit(visiter)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return visiter(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node *Node) VisitDepthParentFirst(visiter Visiter, evaluator VisitEvaluator) error {
|
|
||||||
err := visiter(node)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var keys []string
|
|
||||||
for key := range node.children {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
for _, name := range keys {
|
|
||||||
child := node.children[name]
|
|
||||||
if evaluator == nil || !evaluator(node) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = child.VisitDepthParentFirst(visiter, evaluator)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node *Node) IsWhiteout() bool {
|
|
||||||
return strings.HasPrefix(node.name, whiteoutPrefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node *Node) Path() string {
|
|
||||||
path := []string{}
|
|
||||||
curNode := node
|
|
||||||
for {
|
|
||||||
if curNode.parent == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
name := curNode.name
|
|
||||||
if curNode == node {
|
|
||||||
// white out prefixes are fictitious on leaf nodes
|
|
||||||
name = strings.TrimPrefix(name, whiteoutPrefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
path = append([]string{name}, path...)
|
|
||||||
curNode = curNode.parent
|
|
||||||
}
|
|
||||||
return "/" + strings.Join(path, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tree *FileTree) Stack(upper *FileTree) error {
|
func (tree *FileTree) Stack(upper *FileTree) error {
|
||||||
graft := func(node *Node) error {
|
graft := func(node *FileNode) error {
|
||||||
if node.IsWhiteout() {
|
if node.IsWhiteout() {
|
||||||
err := tree.RemovePath(node.Path())
|
err := tree.RemovePath(node.Path())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -249,7 +126,7 @@ func (tree *FileTree) Stack(upper *FileTree) error {
|
|||||||
return upper.Visit(graft)
|
return upper.Visit(graft)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tree *FileTree) GetNode(path string) (*Node, error) {
|
func (tree *FileTree) GetNode(path string) (*FileNode, error) {
|
||||||
nodeNames := strings.Split(path, "/")
|
nodeNames := strings.Split(path, "/")
|
||||||
node := tree.Root()
|
node := tree.Root()
|
||||||
for _, name := range nodeNames {
|
for _, name := range nodeNames {
|
||||||
@ -264,7 +141,7 @@ func (tree *FileTree) GetNode(path string) (*Node, error) {
|
|||||||
return node, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tree *FileTree) AddPath(path string, data *FileChangeInfo) (*Node, error) {
|
func (tree *FileTree) AddPath(path string, data *FileChangeInfo) (*FileNode, error) {
|
||||||
nodeNames := strings.Split(path, "/")
|
nodeNames := strings.Split(path, "/")
|
||||||
node := tree.Root()
|
node := tree.Root()
|
||||||
for idx, name := range nodeNames {
|
for idx, name := range nodeNames {
|
||||||
@ -296,3 +173,39 @@ func (tree *FileTree) RemovePath(path string) error {
|
|||||||
}
|
}
|
||||||
return node.Remove()
|
return node.Remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (tree *FileTree) compare(upper *FileTree) error {
|
||||||
|
graft := func(node *FileNode) error {
|
||||||
|
if node.IsWhiteout() {
|
||||||
|
err := tree.MarkRemoved(node.Path())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Cannot remove node %s: %v", node.Path(), err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
existingNode, _ := tree.GetNode(node.Path())
|
||||||
|
if existingNode == nil {
|
||||||
|
newNode, err := tree.AddPath(node.Path(), node.data)
|
||||||
|
fmt.Printf("added new node at %s\n", newNode.Path())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Cannot add new node %s: %v", node.Path(), err.Error())
|
||||||
|
}
|
||||||
|
newNode.AssignDiffType(Added)
|
||||||
|
} else {
|
||||||
|
diffType := existingNode.compare(node)
|
||||||
|
fmt.Printf("found existing node at %s\n", existingNode.Path())
|
||||||
|
existingNode.deriveDiffType(diffType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return upper.Visit(graft)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tree *FileTree) MarkRemoved(path string) error {
|
||||||
|
node, err := tree.GetNode(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return node.AssignDiffType(Removed)
|
||||||
|
}
|
@ -1,88 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
func TestAddChild(t *testing.T) {
|
"fmt"
|
||||||
var expected, actual int
|
)
|
||||||
tree := NewTree()
|
|
||||||
|
|
||||||
payload := FileChangeInfo{
|
|
||||||
path: "stufffffs",
|
|
||||||
}
|
|
||||||
|
|
||||||
one := tree.Root().AddChild("first node!", &payload)
|
|
||||||
|
|
||||||
two := tree.Root().AddChild("nil node!", nil)
|
|
||||||
|
|
||||||
tree.Root().AddChild("third node!", nil)
|
|
||||||
two.AddChild("forth, one level down...", nil)
|
|
||||||
two.AddChild("fifth, one level down...", nil)
|
|
||||||
two.AddChild("fifth, one level down...", nil)
|
|
||||||
|
|
||||||
expected, actual = 5, tree.size
|
|
||||||
if expected != actual {
|
|
||||||
t.Errorf("Expected a tree size of %d got %d.", expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected, actual = 2, len(two.children)
|
|
||||||
if expected != actual {
|
|
||||||
t.Errorf("Expected 'twos' number of children to be %d got %d.", expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected, actual = 3, len(tree.Root().children)
|
|
||||||
if expected != actual {
|
|
||||||
t.Errorf("Expected 'twos' number of children to be %d got %d.", expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedFC := &FileChangeInfo{
|
|
||||||
path: "stufffffs",
|
|
||||||
}
|
|
||||||
actualFC := one.data
|
|
||||||
if *expectedFC != *actualFC {
|
|
||||||
t.Errorf("Expected 'ones' payload to be %+v got %+v.", expectedFC, actualFC)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *two.data != *new(FileChangeInfo) {
|
|
||||||
t.Errorf("Expected 'twos' payload to be nil got %d.", two.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRemoveChild(t *testing.T) {
|
|
||||||
var expected, actual int
|
|
||||||
|
|
||||||
tree := NewTree()
|
|
||||||
tree.Root().AddChild("first", nil)
|
|
||||||
two := tree.Root().AddChild("nil", nil)
|
|
||||||
tree.Root().AddChild("third", nil)
|
|
||||||
forth := two.AddChild("forth", nil)
|
|
||||||
two.AddChild("fifth", nil)
|
|
||||||
|
|
||||||
forth.Remove()
|
|
||||||
|
|
||||||
expected, actual = 4, tree.size
|
|
||||||
if expected != actual {
|
|
||||||
t.Errorf("Expected a tree size of %d got %d.", expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tree.Root().children["forth"] != nil {
|
|
||||||
t.Errorf("Expected 'forth' node to be deleted.")
|
|
||||||
}
|
|
||||||
|
|
||||||
two.Remove()
|
|
||||||
|
|
||||||
expected, actual = 2, tree.size
|
|
||||||
if expected != actual {
|
|
||||||
t.Errorf("Expected a tree size of %d got %d.", expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tree.Root().children["nil"] != nil {
|
|
||||||
t.Errorf("Expected 'nil' node to be deleted.")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrintTree(t *testing.T) {
|
func TestPrintTree(t *testing.T) {
|
||||||
tree := NewTree()
|
tree := NewTree()
|
||||||
@ -163,31 +84,6 @@ func TestRemovePath(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPath(t *testing.T) {
|
|
||||||
expected := "/etc/nginx/nginx.conf"
|
|
||||||
tree := NewTree()
|
|
||||||
node, _ := tree.AddPath(expected, nil)
|
|
||||||
|
|
||||||
actual := node.Path()
|
|
||||||
if expected != actual {
|
|
||||||
t.Errorf("Expected path '%s' got '%s'", expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsWhiteout(t *testing.T) {
|
|
||||||
tree1 := NewTree()
|
|
||||||
p1, _ := tree1.AddPath("/etc/nginx/public1", nil)
|
|
||||||
p2, _ := tree1.AddPath("/etc/nginx/.wh.public2", nil)
|
|
||||||
|
|
||||||
if p1.IsWhiteout() != false {
|
|
||||||
t.Errorf("Expected path '%s' to **not** be a whiteout file", p1.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p2.IsWhiteout() != true {
|
|
||||||
t.Errorf("Expected path '%s' to be a whiteout file", p2.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStack(t *testing.T) {
|
func TestStack(t *testing.T) {
|
||||||
payloadKey := "/var/run/systemd"
|
payloadKey := "/var/run/systemd"
|
||||||
payloadValue := FileChangeInfo{
|
payloadValue := FileChangeInfo{
|
||||||
@ -274,3 +170,132 @@ func TestCopy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func TestCompareWithNoChanges(t *testing.T) {
|
||||||
|
lowerTree := NewTree()
|
||||||
|
upperTree := NewTree()
|
||||||
|
paths := [...]string{"/etc", "/etc/sudoers", "/etc/hosts", "/usr/bin", "/usr/bin/bash", "/usr"}
|
||||||
|
|
||||||
|
for _, value := range paths {
|
||||||
|
fakeData := FileChangeInfo{
|
||||||
|
path: value,
|
||||||
|
typeflag: 1,
|
||||||
|
md5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
diffType: Unchanged,
|
||||||
|
}
|
||||||
|
lowerTree.AddPath(value, &fakeData)
|
||||||
|
upperTree.AddPath(value, &fakeData)
|
||||||
|
}
|
||||||
|
lowerTree.compare(upperTree)
|
||||||
|
asserter := func(n *FileNode) error {
|
||||||
|
if n.Path() == "/" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if n.data == nil {
|
||||||
|
t.Errorf("Expected *FileChangeInfo but got nil")
|
||||||
|
return fmt.Errorf("expected *FileChangeInfo but got nil")
|
||||||
|
}
|
||||||
|
if (n.data.diffType) != Unchanged {
|
||||||
|
t.Errorf("Expecting node at %s to have DiffType unchanged, but had %v", n.Path(), n.data.diffType)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := lowerTree.Visit(asserter)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareWithAdds(t *testing.T) {
|
||||||
|
lowerTree := NewTree()
|
||||||
|
upperTree := NewTree()
|
||||||
|
lowerPaths := [...]string{"/etc", "/etc/sudoers", "/usr", "/etc/hosts", "/usr/bin"}
|
||||||
|
upperPaths := [...]string{"/etc", "/etc/sudoers", "/usr", "/etc/hosts", "/usr/bin", "/usr/bin/bash"}
|
||||||
|
|
||||||
|
for _, value := range lowerPaths {
|
||||||
|
fakeData := FileChangeInfo{
|
||||||
|
path: value,
|
||||||
|
typeflag: 1,
|
||||||
|
md5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
diffType: Unchanged,
|
||||||
|
}
|
||||||
|
lowerTree.AddPath(value, &fakeData)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range upperPaths {
|
||||||
|
fakeData := FileChangeInfo{
|
||||||
|
path: value,
|
||||||
|
typeflag: 1,
|
||||||
|
md5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
diffType: Unchanged,
|
||||||
|
}
|
||||||
|
upperTree.AddPath(value, &fakeData)
|
||||||
|
}
|
||||||
|
|
||||||
|
lowerTree.compare(upperTree)
|
||||||
|
asserter := func(n *FileNode) error {
|
||||||
|
|
||||||
|
p := n.Path()
|
||||||
|
if p == "/" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Adding a file changes the folders it's in
|
||||||
|
if p == "/usr/bin/bash" {
|
||||||
|
return AssertDiffType(n, Added, t)
|
||||||
|
}
|
||||||
|
if p == "/usr/bin" {
|
||||||
|
return AssertDiffType(n, Changed, t)
|
||||||
|
}
|
||||||
|
if p == "/usr" {
|
||||||
|
return AssertDiffType(n, Changed, t)
|
||||||
|
}
|
||||||
|
return AssertDiffType(n, Unchanged, t)
|
||||||
|
}
|
||||||
|
err := lowerTree.Visit(asserter)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareWithChanges(t *testing.T) {
|
||||||
|
lowerTree := NewTree()
|
||||||
|
upperTree := NewTree()
|
||||||
|
lowerPaths := [...]string{"/etc", "/usr", "/etc/hosts", "/etc/sudoers", "/usr/bin"}
|
||||||
|
upperPaths := [...]string{"/etc", "/usr", "/etc/hosts", "/etc/sudoers", "/usr/bin"}
|
||||||
|
|
||||||
|
for _, value := range lowerPaths {
|
||||||
|
fakeData := FileChangeInfo{
|
||||||
|
path: value,
|
||||||
|
typeflag: 1,
|
||||||
|
md5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
diffType: Unchanged,
|
||||||
|
}
|
||||||
|
lowerTree.AddPath(value, &fakeData)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range upperPaths {
|
||||||
|
fakeData := FileChangeInfo{
|
||||||
|
path: value,
|
||||||
|
typeflag: 1,
|
||||||
|
md5sum: [16]byte{1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
diffType: Unchanged,
|
||||||
|
}
|
||||||
|
upperTree.AddPath(value, &fakeData)
|
||||||
|
}
|
||||||
|
|
||||||
|
lowerTree.compare(upperTree)
|
||||||
|
asserter := func(n *FileNode) error {
|
||||||
|
p := n.Path()
|
||||||
|
if p == "/" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return AssertDiffType(n, Changed, t)
|
||||||
|
}
|
||||||
|
err := lowerTree.Visit(asserter)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
12
main.go
12
main.go
@ -124,12 +124,12 @@ func demo() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
func getAbsPositionNode() (node *Node) {
|
func getAbsPositionNode() (node *FileNode) {
|
||||||
var visiter func(*Node) error
|
var visiter func(*FileNode) error
|
||||||
var evaluator func(*Node) bool
|
var evaluator func(*FileNode) bool
|
||||||
var dfsCounter uint
|
var dfsCounter uint
|
||||||
|
|
||||||
visiter = func(curNode *Node) error {
|
visiter = func(curNode *FileNode) error {
|
||||||
if dfsCounter == data.absPosition {
|
if dfsCounter == data.absPosition {
|
||||||
node = curNode
|
node = curNode
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ func getAbsPositionNode() (node *Node) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluator = func(curNode *Node) bool {
|
evaluator = func(curNode *FileNode) bool {
|
||||||
return !curNode.collapsed
|
return !curNode.collapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ func showCurNodeInSideBar(g *gocui.Gui, v *gocui.View) error {
|
|||||||
v, _ := g.View("side")
|
v, _ := g.View("side")
|
||||||
// todo: handle above error.
|
// todo: handle above error.
|
||||||
v.Clear()
|
v.Clear()
|
||||||
_, err := fmt.Fprintf(v, "Node:\n%+v\n", getAbsPositionNode())
|
_, err := fmt.Fprintf(v, "FileNode:\n%+v\n", getAbsPositionNode())
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
// todo: blerg
|
// todo: blerg
|
||||||
|
@ -163,12 +163,7 @@ func makeEntry(r *tar.Reader, h *tar.Header, path string) FileChangeInfo {
|
|||||||
|
|
||||||
var zeros = [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
var zeros = [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
|
||||||
type FileChangeInfo struct {
|
|
||||||
path string
|
|
||||||
typeflag byte
|
|
||||||
md5sum [16]byte
|
|
||||||
diffType DiffType
|
|
||||||
}
|
|
||||||
|
|
||||||
type Manifest struct {
|
type Manifest struct {
|
||||||
Config string
|
Config string
|
||||||
|
161
tree_compare.go
161
tree_compare.go
@ -1,161 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func differ() func(int) int {
|
|
||||||
sum := 0
|
|
||||||
return func(x int) int {
|
|
||||||
sum += x
|
|
||||||
return sum
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type DiffType int
|
|
||||||
|
|
||||||
// enum to show whether a file has changed
|
|
||||||
const (
|
|
||||||
Unchanged DiffType = iota
|
|
||||||
Changed
|
|
||||||
Added
|
|
||||||
Removed
|
|
||||||
)
|
|
||||||
|
|
||||||
func (d DiffType) String() string {
|
|
||||||
switch d {
|
|
||||||
case Unchanged:
|
|
||||||
return "Unchanged"
|
|
||||||
case Changed:
|
|
||||||
return "Changed"
|
|
||||||
case Added:
|
|
||||||
return "Added"
|
|
||||||
case Removed:
|
|
||||||
return "Removed"
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("%d", int(d))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareNodes(a *Node, b *Node) DiffType {
|
|
||||||
if a == nil && b == nil {
|
|
||||||
return Unchanged
|
|
||||||
}
|
|
||||||
// a is nil but not b
|
|
||||||
if a == nil && b != nil {
|
|
||||||
return Added
|
|
||||||
}
|
|
||||||
|
|
||||||
// b is nil but not a
|
|
||||||
if a != nil && b == nil {
|
|
||||||
return Removed
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.IsWhiteout() {
|
|
||||||
return Removed
|
|
||||||
}
|
|
||||||
if a.name != b.name {
|
|
||||||
panic("comparing mismatched nodes")
|
|
||||||
}
|
|
||||||
// TODO: fails on nil
|
|
||||||
|
|
||||||
return getDiffType(a.data, b.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDiffType(a *FileChangeInfo, b *FileChangeInfo) DiffType {
|
|
||||||
if a == nil && b == nil {
|
|
||||||
return Unchanged
|
|
||||||
}
|
|
||||||
if a == nil || b == nil {
|
|
||||||
return Changed
|
|
||||||
}
|
|
||||||
if a.typeflag == b.typeflag {
|
|
||||||
if bytes.Compare(a.md5sum[:], b.md5sum[:]) == 0 {
|
|
||||||
return Unchanged
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Changed
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeDiffTypes(a DiffType, b DiffType) DiffType {
|
|
||||||
if a == b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
return Changed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tree *FileTree) compareTo(upper *FileTree) error {
|
|
||||||
|
|
||||||
// TODO mark all as unchanged
|
|
||||||
markAllUnchanged := func(node *Node) error {
|
|
||||||
return node.AssignDiffType(Unchanged)
|
|
||||||
}
|
|
||||||
err := tree.Visit(markAllUnchanged)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
graft := func(node *Node) error {
|
|
||||||
if node.IsWhiteout() {
|
|
||||||
err := tree.MarkRemoved(node.Path())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Cannot remove node %s: %v", node.Path(), err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
existingNode, _ := tree.GetNode(node.Path())
|
|
||||||
if existingNode == nil {
|
|
||||||
newNode, err := tree.AddPath(node.Path(), node.data)
|
|
||||||
fmt.Printf("added new node at %s\n", newNode.Path())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Cannot add new node %s: %v", node.Path(), err.Error())
|
|
||||||
}
|
|
||||||
newNode.AssignDiffType(Added)
|
|
||||||
} else {
|
|
||||||
diffType := compareNodes(existingNode, node)
|
|
||||||
fmt.Printf("found existing node at %s\n", existingNode.Path())
|
|
||||||
existingNode.DiffTypeFromChildren(diffType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return upper.Visit(graft)
|
|
||||||
}
|
|
||||||
|
|
||||||
// THE DIFF_TYPE OF A NODE IS ALWAYS THE DIFF_TYPE OF ITS ATTRIBUTES AND ITS CONTENTS
|
|
||||||
// THE CONTENTS ARE THE BYTES OF A FILE OR THE CHILDREN OF A DIRECTORY
|
|
||||||
|
|
||||||
func (tree *FileTree) MarkRemoved(path string) error {
|
|
||||||
node, err := tree.GetNode(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return node.AssignDiffType(Removed)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node *Node) IsLeaf() bool {
|
|
||||||
return len(node.children) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node *Node) DiffTypeFromChildren(diffType DiffType) error {
|
|
||||||
if node.IsLeaf() {
|
|
||||||
node.AssignDiffType(diffType)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
myDiffType := diffType
|
|
||||||
|
|
||||||
for _, v := range node.children {
|
|
||||||
vData := v.data
|
|
||||||
myDiffType = mergeDiffTypes(myDiffType, vData.diffType)
|
|
||||||
|
|
||||||
}
|
|
||||||
node.AssignDiffType(myDiffType)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (node *Node) AssignDiffType(diffType DiffType) error {
|
|
||||||
if node.Path() == "/" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
node.data.diffType = diffType
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,190 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCompareWithNoChanges(t *testing.T) {
|
|
||||||
lowerTree := NewTree()
|
|
||||||
upperTree := NewTree()
|
|
||||||
paths := [...]string{"/etc", "/etc/sudoers", "/etc/hosts", "/usr/bin", "/usr/bin/bash", "/usr"}
|
|
||||||
|
|
||||||
for _, value := range paths {
|
|
||||||
fakeData := FileChangeInfo{
|
|
||||||
path: value,
|
|
||||||
typeflag: 1,
|
|
||||||
md5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
diffType: Unchanged,
|
|
||||||
}
|
|
||||||
lowerTree.AddPath(value, &fakeData)
|
|
||||||
upperTree.AddPath(value, &fakeData)
|
|
||||||
}
|
|
||||||
lowerTree.compareTo(upperTree)
|
|
||||||
asserter := func(n *Node) error {
|
|
||||||
if n.Path() == "/" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if n.data == nil {
|
|
||||||
t.Errorf("Expected *FileChangeInfo but got nil")
|
|
||||||
return fmt.Errorf("expected *FileChangeInfo but got nil")
|
|
||||||
}
|
|
||||||
if (n.data.diffType) != Unchanged {
|
|
||||||
t.Errorf("Expecting node at %s to have DiffType unchanged, but had %v", n.Path(), n.data.diffType)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := lowerTree.Visit(asserter)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompareWithAdds(t *testing.T) {
|
|
||||||
lowerTree := NewTree()
|
|
||||||
upperTree := NewTree()
|
|
||||||
lowerPaths := [...]string{"/etc", "/etc/sudoers", "/usr", "/etc/hosts", "/usr/bin"}
|
|
||||||
upperPaths := [...]string{"/etc", "/etc/sudoers", "/usr", "/etc/hosts", "/usr/bin", "/usr/bin/bash"}
|
|
||||||
|
|
||||||
for _, value := range lowerPaths {
|
|
||||||
fakeData := FileChangeInfo{
|
|
||||||
path: value,
|
|
||||||
typeflag: 1,
|
|
||||||
md5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
diffType: Unchanged,
|
|
||||||
}
|
|
||||||
lowerTree.AddPath(value, &fakeData)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, value := range upperPaths {
|
|
||||||
fakeData := FileChangeInfo{
|
|
||||||
path: value,
|
|
||||||
typeflag: 1,
|
|
||||||
md5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
diffType: Unchanged,
|
|
||||||
}
|
|
||||||
upperTree.AddPath(value, &fakeData)
|
|
||||||
}
|
|
||||||
|
|
||||||
lowerTree.compareTo(upperTree)
|
|
||||||
asserter := func(n *Node) error {
|
|
||||||
|
|
||||||
p := n.Path()
|
|
||||||
if p == "/" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Adding a file changes the folders it's in
|
|
||||||
if p == "/usr/bin/bash" {
|
|
||||||
return AssertDiffType(n, Added, t)
|
|
||||||
}
|
|
||||||
if p == "/usr/bin" {
|
|
||||||
return AssertDiffType(n, Changed, t)
|
|
||||||
}
|
|
||||||
if p == "/usr" {
|
|
||||||
return AssertDiffType(n, Changed, t)
|
|
||||||
}
|
|
||||||
return AssertDiffType(n, Unchanged, t)
|
|
||||||
}
|
|
||||||
err := lowerTree.Visit(asserter)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompareWithChanges(t *testing.T) {
|
|
||||||
lowerTree := NewTree()
|
|
||||||
upperTree := NewTree()
|
|
||||||
lowerPaths := [...]string{"/etc", "/usr", "/etc/hosts", "/etc/sudoers", "/usr/bin"}
|
|
||||||
upperPaths := [...]string{"/etc", "/usr", "/etc/hosts", "/etc/sudoers", "/usr/bin"}
|
|
||||||
|
|
||||||
for _, value := range lowerPaths {
|
|
||||||
fakeData := FileChangeInfo{
|
|
||||||
path: value,
|
|
||||||
typeflag: 1,
|
|
||||||
md5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
diffType: Unchanged,
|
|
||||||
}
|
|
||||||
lowerTree.AddPath(value, &fakeData)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, value := range upperPaths {
|
|
||||||
fakeData := FileChangeInfo{
|
|
||||||
path: value,
|
|
||||||
typeflag: 1,
|
|
||||||
md5sum: [16]byte{1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
diffType: Unchanged,
|
|
||||||
}
|
|
||||||
upperTree.AddPath(value, &fakeData)
|
|
||||||
}
|
|
||||||
|
|
||||||
lowerTree.compareTo(upperTree)
|
|
||||||
asserter := func(n *Node) error {
|
|
||||||
p := n.Path()
|
|
||||||
if p == "/" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return AssertDiffType(n, Changed, t)
|
|
||||||
}
|
|
||||||
err := lowerTree.Visit(asserter)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAssignDiffType(t *testing.T) {
|
|
||||||
tree := NewTree()
|
|
||||||
tree.AddPath("/usr", BlankFileChangeInfo("/usr", Changed))
|
|
||||||
if tree.root.children["usr"].data.diffType != Changed {
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMergeDiffTypes(t *testing.T) {
|
|
||||||
a := Unchanged
|
|
||||||
b := Unchanged
|
|
||||||
merged := mergeDiffTypes(a, b)
|
|
||||||
if merged != Unchanged {
|
|
||||||
t.Errorf("Expected Unchaged (0) but got %v", merged)
|
|
||||||
}
|
|
||||||
a = Changed
|
|
||||||
b = Unchanged
|
|
||||||
merged = mergeDiffTypes(a, b)
|
|
||||||
if merged != Changed {
|
|
||||||
t.Errorf("Expected Unchaged (0) but got %v", merged)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDiffTypeFromChildren(t *testing.T) {
|
|
||||||
tree := NewTree()
|
|
||||||
tree.AddPath("/usr", BlankFileChangeInfo("/usr", Unchanged))
|
|
||||||
info1 := BlankFileChangeInfo("/usr/bin", Added)
|
|
||||||
tree.AddPath("/usr/bin", info1)
|
|
||||||
info2 := BlankFileChangeInfo("/usr/bin2", Removed)
|
|
||||||
tree.AddPath("/usr/bin2", info2)
|
|
||||||
tree.root.children["usr"].DiffTypeFromChildren(Unchanged)
|
|
||||||
if tree.root.children["usr"].data.diffType != Changed {
|
|
||||||
t.Errorf("Expected Changed but got %v", tree.root.children["usr"].data.diffType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func AssertDiffType(node *Node, expectedDiffType DiffType, t *testing.T) error {
|
|
||||||
if node.data == nil {
|
|
||||||
t.Errorf("Expected *FileChangeInfo but got nil at path %s", node.Path())
|
|
||||||
return fmt.Errorf("expected *FileChangeInfo but got nil at path %s", node.Path())
|
|
||||||
}
|
|
||||||
if node.data.diffType != expectedDiffType {
|
|
||||||
t.Errorf("Expecting node at %s to have DiffType %v, but had %v", node.Path(), expectedDiffType, node.data.diffType)
|
|
||||||
return fmt.Errorf("Assertion failed")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func BlankFileChangeInfo(path string, diffType DiffType) (f *FileChangeInfo) {
|
|
||||||
result := FileChangeInfo{
|
|
||||||
path: path,
|
|
||||||
typeflag: 1,
|
|
||||||
md5sum: [16]byte{1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
diffType: diffType,
|
|
||||||
}
|
|
||||||
return &result
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user