summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Vogt <michael.vogt@gmail.com>2018-06-28 19:31:08 +0200
committerGustavo Niemeyer <gustavo@niemeyer.net>2018-06-28 14:31:08 -0300
commit788fd78401277ebd861206a03c884797c6ec5541 (patch)
tree23bb6ba6a894fad51ac76d53b3fd9dd985d8e1fc
parent20d25e2804050c1cd24a7eea1e7a6447dd0e74ec (diff)
downloadcheck-v1.tar.xz
Print pretty diff when equality checks fail (#100)v1
-rw-r--r--check.go17
-rw-r--r--check_test.go4
-rw-r--r--checkers.go70
-rw-r--r--checkers_test.go29
-rw-r--r--integration_test.go94
5 files changed, 199 insertions, 15 deletions
diff --git a/check.go b/check.go
index 137a274..de714c2 100644
--- a/check.go
+++ b/check.go
@@ -239,7 +239,7 @@ func (c *C) logValue(label string, value interface{}) {
}
}
-func (c *C) logMultiLine(s string) {
+func formatMultiLine(s string, quote bool) []byte {
b := make([]byte, 0, len(s)*2)
i := 0
n := len(s)
@@ -249,14 +249,23 @@ func (c *C) logMultiLine(s string) {
j++
}
b = append(b, "... "...)
- b = strconv.AppendQuote(b, s[i:j])
- if j < n {
+ if quote {
+ b = strconv.AppendQuote(b, s[i:j])
+ } else {
+ b = append(b, s[i:j]...)
+ b = bytes.TrimSpace(b)
+ }
+ if quote && j < n {
b = append(b, " +"...)
}
b = append(b, '\n')
i = j
}
- c.writeLog(b)
+ return b
+}
+
+func (c *C) logMultiLine(s string) {
+ c.writeLog(formatMultiLine(s, true))
}
func isMultiLine(s string) bool {
diff --git a/check_test.go b/check_test.go
index 871b325..e8b0f52 100644
--- a/check_test.go
+++ b/check_test.go
@@ -187,8 +187,8 @@ func checkState(c *check.C, result interface{}, expected *expectedState) {
log := c.GetTestLog()
matched, matchError := regexp.MatchString("^"+expected.log+"$", log)
if matchError != nil {
- c.Errorf("Error in matching expression used in testing %s",
- expected.name)
+ c.Errorf("Error in matching expression used in testing %s: %v",
+ expected.name, matchError)
} else if !matched {
c.Errorf("%s logged:\n----------\n%s----------\n\nExpected:\n----------\n%s\n----------",
expected.name, log, expected.log)
diff --git a/checkers.go b/checkers.go
index 3749545..cdd7f9a 100644
--- a/checkers.go
+++ b/checkers.go
@@ -4,6 +4,9 @@ import (
"fmt"
"reflect"
"regexp"
+ "strings"
+
+ "github.com/kr/pretty"
)
// -----------------------------------------------------------------------
@@ -90,6 +93,10 @@ func (checker *notChecker) Info() *CheckerInfo {
func (checker *notChecker) Check(params []interface{}, names []string) (result bool, error string) {
result, error = checker.sub.Check(params, names)
result = !result
+ if result {
+ // clear error message if the new result is true
+ error = ""
+ }
return
}
@@ -153,6 +160,56 @@ func (checker *notNilChecker) Check(params []interface{}, names []string) (resul
// -----------------------------------------------------------------------
// Equals checker.
+func diffworthy(a interface{}) bool {
+ t := reflect.TypeOf(a)
+ switch t.Kind() {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct, reflect.String, reflect.Ptr:
+ return true
+ }
+ return false
+}
+
+// formatUnequal will dump the actual and expected values into a textual
+// representation and return an error message containing a diff.
+func formatUnequal(obtained interface{}, expected interface{}) string {
+ // We do not do diffs for basic types because go-check already
+ // shows them very cleanly.
+ if !diffworthy(obtained) || !diffworthy(expected) {
+ return ""
+ }
+
+ // Handle strings, short strings are ignored (go-check formats
+ // them very nicely already). We do multi-line strings by
+ // generating two string slices and using kr.Diff to compare
+ // those (kr.Diff does not do string diffs by itself).
+ aStr, aOK := obtained.(string)
+ bStr, bOK := expected.(string)
+ if aOK && bOK {
+ l1 := strings.Split(aStr, "\n")
+ l2 := strings.Split(bStr, "\n")
+ // the "2" here is a bit arbitrary
+ if len(l1) > 2 && len(l2) > 2 {
+ diff := pretty.Diff(l1, l2)
+ return fmt.Sprintf(`String difference:
+%s`, formatMultiLine(strings.Join(diff, "\n"), false))
+ }
+ // string too short
+ return ""
+ }
+
+ // generic diff
+ diff := pretty.Diff(obtained, expected)
+ if len(diff) == 0 {
+ // No diff, this happens when e.g. just struct
+ // pointers are different but the structs have
+ // identical values.
+ return ""
+ }
+
+ return fmt.Sprintf(`Difference:
+%s`, formatMultiLine(strings.Join(diff, "\n"), false))
+}
+
type equalsChecker struct {
*CheckerInfo
}
@@ -175,7 +232,12 @@ func (checker *equalsChecker) Check(params []interface{}, names []string) (resul
error = fmt.Sprint(v)
}
}()
- return params[0] == params[1], ""
+
+ result = params[0] == params[1]
+ if !result {
+ error = formatUnequal(params[0], params[1])
+ }
+ return
}
// -----------------------------------------------------------------------
@@ -200,7 +262,11 @@ var DeepEquals Checker = &deepEqualsChecker{
}
func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) {
- return reflect.DeepEqual(params[0], params[1]), ""
+ result = reflect.DeepEqual(params[0], params[1])
+ if !result {
+ error = formatUnequal(params[0], params[1])
+ }
+ return
}
// -----------------------------------------------------------------------
diff --git a/checkers_test.go b/checkers_test.go
index 5c69747..a2e87b6 100644
--- a/checkers_test.go
+++ b/checkers_test.go
@@ -2,9 +2,10 @@ package check_test
import (
"errors"
- "gopkg.in/check.v1"
"reflect"
"runtime"
+
+ "gopkg.in/check.v1"
)
type CheckersS struct{}
@@ -77,6 +78,7 @@ func (s *CheckersS) TestNot(c *check.C) {
testCheck(c, check.Not(check.IsNil), false, "", nil)
testCheck(c, check.Not(check.IsNil), true, "", "a")
+ testCheck(c, check.Not(check.Equals), true, "", 42, 43)
}
type simpleStruct struct {
@@ -101,11 +103,16 @@ func (s *CheckersS) TestEquals(c *check.C) {
// Struct values
testCheck(c, check.Equals, true, "", simpleStruct{1}, simpleStruct{1})
- testCheck(c, check.Equals, false, "", simpleStruct{1}, simpleStruct{2})
+ testCheck(c, check.Equals, false, `Difference:
+... i: 1 != 2
+`, simpleStruct{1}, simpleStruct{2})
- // Struct pointers
+ // Struct pointers, no difference in values, just pointer
testCheck(c, check.Equals, false, "", &simpleStruct{1}, &simpleStruct{1})
- testCheck(c, check.Equals, false, "", &simpleStruct{1}, &simpleStruct{2})
+ // Struct pointers, different pointers and different values
+ testCheck(c, check.Equals, false, `Difference:
+... i: 1 != 2
+`, &simpleStruct{1}, &simpleStruct{2})
}
func (s *CheckersS) TestDeepEquals(c *check.C) {
@@ -123,15 +130,23 @@ func (s *CheckersS) TestDeepEquals(c *check.C) {
// Slices
testCheck(c, check.DeepEquals, true, "", []byte{1, 2}, []byte{1, 2})
- testCheck(c, check.DeepEquals, false, "", []byte{1, 2}, []byte{1, 3})
+ testCheck(c, check.DeepEquals, false, `Difference:
+... [1]: 2 != 3
+`, []byte{1, 2}, []byte{1, 3})
// Struct values
testCheck(c, check.DeepEquals, true, "", simpleStruct{1}, simpleStruct{1})
- testCheck(c, check.DeepEquals, false, "", simpleStruct{1}, simpleStruct{2})
+ testCheck(c, check.DeepEquals, false, `Difference:
+... i: 1 != 2
+`, simpleStruct{1}, simpleStruct{2})
// Struct pointers
testCheck(c, check.DeepEquals, true, "", &simpleStruct{1}, &simpleStruct{1})
- testCheck(c, check.DeepEquals, false, "", &simpleStruct{1}, &simpleStruct{2})
+ s1 := &simpleStruct{1}
+ s2 := &simpleStruct{2}
+ testCheck(c, check.DeepEquals, false, `Difference:
+... i: 1 != 2
+`, s1, s2)
}
func (s *CheckersS) TestHasLen(c *check.C) {
diff --git a/integration_test.go b/integration_test.go
new file mode 100644
index 0000000..dfcd952
--- /dev/null
+++ b/integration_test.go
@@ -0,0 +1,94 @@
+// Integration tests
+
+package check_test
+
+import (
+ . "gopkg.in/check.v1"
+)
+
+// -----------------------------------------------------------------------
+// Integration test suite.
+
+type integrationS struct{}
+
+var _ = Suite(&integrationS{})
+
+type integrationTestHelper struct{}
+
+func (s *integrationTestHelper) TestMultiLineStringEqualFails(c *C) {
+ c.Check("foo\nbar\nbaz\nboom\n", Equals, "foo\nbaar\nbaz\nboom\n")
+}
+
+func (s *integrationTestHelper) TestStringEqualFails(c *C) {
+ c.Check("foo", Equals, "bar")
+}
+
+func (s *integrationTestHelper) TestIntEqualFails(c *C) {
+ c.Check(42, Equals, 43)
+}
+
+type complexStruct struct {
+ r, i int
+}
+
+func (s *integrationTestHelper) TestStructEqualFails(c *C) {
+ c.Check(complexStruct{1, 2}, Equals, complexStruct{3, 4})
+}
+
+func (s *integrationS) TestOutput(c *C) {
+ helper := integrationTestHelper{}
+ output := String{}
+ Run(&helper, &RunConf{Output: &output})
+ c.Assert(output.value, Equals, `
+----------------------------------------------------------------------
+FAIL: integration_test.go:26: integrationTestHelper.TestIntEqualFails
+
+integration_test.go:27:
+ c.Check(42, Equals, 43)
+... obtained int = 42
+... expected int = 43
+
+
+----------------------------------------------------------------------
+FAIL: integration_test.go:18: integrationTestHelper.TestMultiLineStringEqualFails
+
+integration_test.go:19:
+ c.Check("foo\nbar\nbaz\nboom\n", Equals, "foo\nbaar\nbaz\nboom\n")
+... obtained string = "" +
+... "foo\n" +
+... "bar\n" +
+... "baz\n" +
+... "boom\n"
+... expected string = "" +
+... "foo\n" +
+... "baar\n" +
+... "baz\n" +
+... "boom\n"
+... String difference:
+... [1]: "bar" != "baar"
+
+
+
+----------------------------------------------------------------------
+FAIL: integration_test.go:22: integrationTestHelper.TestStringEqualFails
+
+integration_test.go:23:
+ c.Check("foo", Equals, "bar")
+... obtained string = "foo"
+... expected string = "bar"
+
+
+----------------------------------------------------------------------
+FAIL: integration_test.go:34: integrationTestHelper.TestStructEqualFails
+
+integration_test.go:35:
+ c.Check(complexStruct{1, 2}, Equals, complexStruct{3, 4})
+... obtained check_test.complexStruct = check_test.complexStruct{r:1, i:2}
+... expected check_test.complexStruct = check_test.complexStruct{r:3, i:4}
+... Difference:
+... r: 1 != 3
+... i: 2 != 4
+
+
+`)
+}