Skip to content

Commit

Permalink
Added Diff(a, b []byte)
Browse files Browse the repository at this point in the history
Closes #9
  • Loading branch information
dustin committed Aug 9, 2014
1 parent 63fc374 commit ec955e3
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 0 deletions.
81 changes: 81 additions & 0 deletions diff.go
@@ -0,0 +1,81 @@
package jsonpointer

import (
"log"
"reflect"
)

// DiffType represents the type of difference between two JSON
// structures.
type DiffType int

const (
// MissingA designates a path that was missing from the first
// argument of the diff.
MissingA = DiffType(iota)
// MissingB designates a path taht was missing from the second
// argument of the diff.
MissingB
// Different designates a path that is found in both
// arguments, but with different values.
Different
)

var diffNames = []string{"missing a", "missing b", "different"}

func (d DiffType) String() string {
return diffNames[d]
}

func must(err error) {
if err != nil {
log.Panic(err)
}
}

// Diff returns the differences between two json blobs.
func Diff(a, b []byte) (map[string]DiffType, error) {
alist, err := ListPointers(a)
if err != nil {
return nil, err
}
blist, err := ListPointers(b)
if err != nil {
return nil, err
}

amap := map[string]bool{}
bmap := map[string]bool{}
for _, v := range alist {
amap[v] = true
}
for _, v := range blist {
bmap[v] = true
}

rv := map[string]DiffType{}

for _, v := range alist {
if v == "" {
continue
}
if bmap[v] {
var aval, bval interface{}
must(FindDecode(a, v, &aval))
must(FindDecode(b, v, &bval))
if !reflect.DeepEqual(aval, bval) {
rv[v] = Different
}
} else {
rv[v] = MissingB
}
}

for _, v := range blist {
if !amap[v] {
rv[v] = MissingA
}
}

return rv, nil
}
95 changes: 95 additions & 0 deletions diff_test.go
@@ -0,0 +1,95 @@
package jsonpointer

import (
"io"
"reflect"
"testing"
)

func TestDiffNil(t *testing.T) {
diffs, err := Diff(nil, nil)
if err != nil {
t.Fatalf("Expected no error on nil diff, got %v", err)
}
if len(diffs) != 0 {
t.Errorf("Expected no diffs, got %v", diffs)
}
}

func TestDiffNames(t *testing.T) {
tests := map[DiffType]string{
MissingA: "missing a",
MissingB: "missing b",
Different: "different",
}

for k, v := range tests {
if k.String() != v {
t.Errorf("Expected %v for %d, got %v", v, k, k)
}
}
}

func TestMust(t *testing.T) {
must(nil) // no panic
panicked := false
func() {
defer func() { panicked = recover() != nil }()
must(io.EOF)
}()
if !panicked {
t.Fatalf("Expected a panic, but didn't get one")
}
}

func TestDiff(t *testing.T) {
var (
aFirst = `{"a": 1, "b": 3.2}`
bFirst = `{"b":3.2,"a":1}`
aTwo = `{"a": 2, "b": 3.2}`
aOnly1 = `{"a": 1}`
aOnly3 = `{"a": 3}`
broken = `{x}`
)

tests := []struct {
name string
a, b string
exp map[string]DiffType
errored bool
}{
{"Empty", "", "", map[string]DiffType{}, false},
{"Identity", aFirst, aFirst, map[string]DiffType{}, false},
{"Same", aFirst, bFirst, map[string]DiffType{}, false},
{"Other order", aFirst, bFirst, map[string]DiffType{}, false},
{"A diff", aFirst, aTwo, map[string]DiffType{"/a": Different}, false},
{"A diff rev", aTwo, aFirst, map[string]DiffType{"/a": Different}, false},
{"Missing b <- 1", aFirst, aOnly1, map[string]DiffType{"/b": MissingB}, false},
{"Missing b -> 1", aOnly1, aFirst, map[string]DiffType{"/b": MissingA}, false},
{"Missing b <- 3", aTwo, aOnly3, map[string]DiffType{
"/a": Different,
"/b": MissingB,
}, false},
{"Missing b -> 3", aOnly3, aTwo, map[string]DiffType{
"/a": Different,
"/b": MissingA,
}, false},
{"Broken A", broken, aFirst, nil, true},
{"Broken B", aFirst, broken, nil, true},
}

for _, test := range tests {
diffs, err := Diff([]byte(test.a), []byte(test.b))
if (err != nil) != test.errored {
t.Errorf("Expected error=%v on %q: %v", test.errored, test.name, err)
}
if err != nil {
continue
}
if !reflect.DeepEqual(test.exp, diffs) {
t.Errorf("Unexpected diff for %q: %v\nwanted %v",
test.name, diffs, test.exp)
}
}

}

0 comments on commit ec955e3

Please sign in to comment.