From fe8a28286ed1859a2f7495a7d316207920742269 Mon Sep 17 00:00:00 2001 From: Crow Crowcrow Date: Fri, 17 Jan 2020 22:36:24 +0100 Subject: [PATCH 1/8] Accept an interface instead of testing.T (#1) --- assert.go | 3 +-- helper.go | 5 ++--- type.go | 7 +++++++ 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 type.go diff --git a/assert.go b/assert.go index 7d5f90e..1a3166b 100644 --- a/assert.go +++ b/assert.go @@ -3,14 +3,13 @@ package assert import ( "reflect" "runtime" - "testing" ) // Assert is a helper for tests type Assert func(bool, ...interface{}) // New returns a new Assert -func New(t *testing.T) Assert { +func New(t T) Assert { a := func(ok bool, msg ...interface{}) { if !ok { if msg == nil { diff --git a/helper.go b/helper.go index 67cc4a9..e94190b 100644 --- a/helper.go +++ b/helper.go @@ -5,7 +5,6 @@ import ( "reflect" "runtime" "strconv" - "testing" ) func prepMsg(msg []interface{}, format string, args ...interface{}) []interface{} { @@ -16,9 +15,9 @@ func shell(i int) string { return "\x1B[" + strconv.Itoa(i) + "m" } -var ts = map[*runtime.Func]*testing.T{} +var ts = map[*runtime.Func]T{} -func t(a Assert) *testing.T { +func t(a Assert) T { f := runtime.FuncForPC(reflect.ValueOf(a).Pointer()) return ts[f] } diff --git a/type.go b/type.go new file mode 100644 index 0000000..1c9a1fd --- /dev/null +++ b/type.go @@ -0,0 +1,7 @@ +package assert + +// T is an interface of what we use from testing.T +type T interface { + Error(...interface{}) + Helper() +} From d20ac9444aef0fe1752865d46d69c45f8ec6581f Mon Sep 17 00:00:00 2001 From: NiseVoid Date: Tue, 12 May 2020 18:00:07 +0200 Subject: [PATCH 2/8] Use Cleanup to remove old entries in ts map --- assert.go | 4 ++++ type.go | 1 + 2 files changed, 5 insertions(+) diff --git a/assert.go b/assert.go index 1a3166b..bf26a02 100644 --- a/assert.go +++ b/assert.go @@ -24,6 +24,10 @@ func New(t T) Assert { f := runtime.FuncForPC(reflect.ValueOf(a).Pointer()) ts[f] = t + t.Cleanup(func() { + delete(ts, f) + }) + return a } diff --git a/type.go b/type.go index 1c9a1fd..2a4b72a 100644 --- a/type.go +++ b/type.go @@ -4,4 +4,5 @@ package assert type T interface { Error(...interface{}) Helper() + Cleanup(func()) } From 5f6f4dd0475a1a355803a13f003dc781637dbbd7 Mon Sep 17 00:00:00 2001 From: NiseVoid Date: Tue, 12 May 2020 18:00:50 +0200 Subject: [PATCH 3/8] Add Cmp --- assert.go | 11 +++++++++++ go.mod | 5 +++++ go.sum | 3 +++ 3 files changed, 19 insertions(+) create mode 100644 go.mod create mode 100644 go.sum diff --git a/assert.go b/assert.go index bf26a02..37582a6 100644 --- a/assert.go +++ b/assert.go @@ -3,6 +3,8 @@ package assert import ( "reflect" "runtime" + + "github.com/google/go-cmp/cmp" ) // Assert is a helper for tests @@ -167,3 +169,12 @@ func (a Assert) SameElements(expected, actual interface{}, msg ...interface{}) { a(false, msg...) } + +func (a Assert) Cmp(expected, actual interface{}, opts ...cmp.Option) { + diff := cmp.Diff(expected, actual, opts...) + if diff == `` { + return + } + + a(false, "\n"+diff) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..55a7857 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.fuyu.moe/Fuyu/assert + +go 1.14 + +require github.com/google/go-cmp v0.4.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4430646 --- /dev/null +++ b/go.sum @@ -0,0 +1,3 @@ +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 0cc5d272773f78bd37bbf9f3754821cf622f581a Mon Sep 17 00:00:00 2001 From: NiseVoid Date: Tue, 12 May 2020 18:01:11 +0200 Subject: [PATCH 4/8] Add tests --- assert_test.go | 179 +++++++++++++++++++++++++++++++++++++++++++++++++ faket_test.go | 24 +++++++ 2 files changed, 203 insertions(+) create mode 100644 assert_test.go create mode 100644 faket_test.go diff --git a/assert_test.go b/assert_test.go new file mode 100644 index 0000000..e0a2323 --- /dev/null +++ b/assert_test.go @@ -0,0 +1,179 @@ +package assert + +import ( + "testing" + "time" +) + +// ----------------------------------------- +// Slices +// ----------------------------------------- + +func TestSameElementsSlices(t *testing.T) { + assert := New(t) + ft, fa := newFakeT() + + a, b := []int{1, 2}, []int{1, 2} + fa.SameElements(a, a) + assert.False(ft.GotError()) + + fa.SameElements(a, b) + assert.False(ft.GotError()) + + b = []int{2, 1} + fa.SameElements(b, a) // SameElements ignores order + assert.False(ft.GotError()) + + b = []int{1, 2, 3} + fa.SameElements(b, a) + assert.True(ft.GotError()) + + b = []int{2, 3} + fa.SameElements(b, a) + assert.True(ft.GotError()) +} + +func TestCmpSlices(t *testing.T) { + assert := New(t) + ft, fa := newFakeT() + + a, b := []int{1, 2}, []int{1, 2} + fa.Cmp(a, a) + assert.False(ft.GotError()) + + fa.Cmp(a, b) + assert.False(ft.GotError()) + + b = []int{2, 1} + fa.Cmp(a, b) // Cmp does not accept different order + assert.True(ft.GotError()) + + b = []int{1, 2, 3} + fa.Cmp(a, b) + assert.True(ft.GotError()) + + b = []int{2, 3} + fa.Cmp(a, b) + assert.True(ft.GotError()) +} + +// ----------------------------------------- +// Maps +// ----------------------------------------- + +func TestCmpMaps(t *testing.T) { + assert := New(t) + ft, fa := newFakeT() + + a, b := map[int]int{1: 2, 3: 4}, map[int]int{1: 2, 3: 4} + fa.Cmp(a, a) + assert.False(ft.GotError()) + + fa.Cmp(a, b) + assert.False(ft.GotError()) + + b = map[int]int{1: 2, 3: 5} + fa.Cmp(a, b) + assert.True(ft.GotError()) + + b = map[int]int{1: 2, 3: 4, 5: 6} + fa.Cmp(a, b) + assert.True(ft.GotError()) + + b = map[int]int{1: 2} + fa.Cmp(a, b) + assert.True(ft.GotError()) +} + +// ----------------------------------------- +// Pointers +// ----------------------------------------- + +func TestEqPointers(t *testing.T) { + assert := New(t) + ft, fa := newFakeT() + + var a, b int + fa.Eq(&a, &a) + assert.False(ft.GotError()) + + fa.Eq(&a, &b) + assert.True(ft.GotError()) + + fa.Eq(&b, &a) + assert.True(ft.GotError()) +} + +func TestCmpPointers(t *testing.T) { + assert := New(t) + ft, fa := newFakeT() + + type B struct { + Value int + } + type A struct { + B *B + } + + fa.Cmp(A{B: &B{1}}, A{&B{1}}) + assert.False(ft.GotError()) + + fa.Cmp(A{B: &B{1}}, A{&B{2}}) + assert.True(ft.GotError()) + + fa.Cmp(A{B: &B{1}}, A{nil}) + assert.True(ft.GotError()) +} + +// ----------------------------------------- +// Timezones +// ----------------------------------------- + +func mustLoadLocation(zone string) *time.Location { + loc, err := time.LoadLocation(zone) + if err != nil { + panic(err) + } + + return loc +} + +var locAmsterdam = mustLoadLocation(`Europe/Amsterdam`) +var locTokyo = mustLoadLocation(`Asia/Tokyo`) + +func TestEqTimezones(t *testing.T) { + assert := New(t) + ft, fa := newFakeT() + + ti := time.Now() + ti, ti2 := ti.In(locAmsterdam), ti.In(locTokyo) + + fa.Eq(ti, ti2) + assert.True(ft.GotError()) +} + +func TestCmpTimezones(t *testing.T) { + assert := New(t) + ft, fa := newFakeT() + + type A struct { + Time time.Time + } + + ti := time.Now() + ti2 := ti.In(time.UTC) + fa.Cmp(&A{ti}, &A{ti2}) + assert.False(ft.GotError()) + + ti2 = ti.In(locAmsterdam) + fa.Cmp(&A{ti}, &A{ti2}) + assert.False(ft.GotError()) + + ti = ti.In(locTokyo) + fa.Cmp(&A{ti}, &A{ti2}) + assert.False(ft.GotError()) + + ti2 = ti2.Add(time.Second) + fa.Cmp(&A{ti}, &A{ti2}) + assert.True(ft.GotError()) +} diff --git a/faket_test.go b/faket_test.go new file mode 100644 index 0000000..7f5cd33 --- /dev/null +++ b/faket_test.go @@ -0,0 +1,24 @@ +package assert + +type fakeT struct { + gotError bool +} + +func newFakeT() (*fakeT, Assert) { + var ft fakeT + return &ft, New(&ft) +} + +func (t *fakeT) Error(_ ...interface{}) { + t.gotError = true +} + +func (_ *fakeT) Helper() {} +func (_ *fakeT) Cleanup(_ func()) {} + +func (t *fakeT) GotError() bool { + r := t.gotError + t.gotError = false + + return r +} From 58bacd98ce970f3e8275902d38aa5271c83964f4 Mon Sep 17 00:00:00 2001 From: NiseVoid Date: Wed, 27 May 2020 19:56:16 +0200 Subject: [PATCH 5/8] Fix calls to t.Helper and optimize --- assert.go | 103 ++++++++++++++++++++++++------------------------------ helper.go | 9 ----- 2 files changed, 45 insertions(+), 67 deletions(-) diff --git a/assert.go b/assert.go index 37582a6..6c762c3 100644 --- a/assert.go +++ b/assert.go @@ -2,33 +2,32 @@ package assert import ( "reflect" - "runtime" "github.com/google/go-cmp/cmp" ) // Assert is a helper for tests -type Assert func(bool, ...interface{}) +type Assert struct { + t T +} + +func (a Assert) f(ok bool, msg []interface{}, format string, extra ...interface{}) { + if !ok { + if format != `` { + msg = prepMsg(msg, format, extra...) + } + if msg == nil { + msg = []interface{}{`Assertion failed`} + } + msg = append(append([]interface{}{shell(1) + shell(97) + shell(41) + `FAIL!` + shell(0) + shell(1)}, msg...), shell(0), "\n") + a.t.Helper() + a.t.Error(msg...) + } +} // New returns a new Assert func New(t T) Assert { - a := func(ok bool, msg ...interface{}) { - if !ok { - if msg == nil { - msg = []interface{}{`Assertion failed`} - } - msg = append(append([]interface{}{shell(1) + shell(97) + shell(41) + `FAIL!` + shell(0) + shell(1)}, msg...), shell(0), "\n") - t.Helper() - t.Error(msg...) - } - } - - f := runtime.FuncForPC(reflect.ValueOf(a).Pointer()) - ts[f] = t - - t.Cleanup(func() { - delete(ts, f) - }) + a := Assert{t} return a } @@ -39,16 +38,14 @@ func New(t T) Assert { // True asserts the given value is true func (a Assert) True(actual bool, msg ...interface{}) { - t(a).Helper() - msg = prepMsg(msg, `Should be true, but it isn't`) - a(actual, msg...) + a.t.Helper() + a.f(actual, msg, `Should be true, but it isn't`) } // False sserts the given value is false func (a Assert) False(actual bool, msg ...interface{}) { - t(a).Helper() - msg = prepMsg(msg, `Should be false, but it isn't`) - a(!actual, msg...) + a.t.Helper() + a.f(!actual, msg, `Should be false, but it isn't`) } ///// Nil ///// @@ -69,87 +66,76 @@ func isNil(val interface{}) bool { // Nil asserts the given value is nil func (a Assert) Nil(actual interface{}, msg ...interface{}) { - t(a).Helper() - msg = prepMsg(msg, `Should be nil, but got %#v`, actual) - - a(isNil(actual), msg...) + a.t.Helper() + a.f(isNil(actual), msg, `Should be nil, but got %#v`, actual) } // NotNil sserts the given value is not nil func (a Assert) NotNil(actual interface{}, msg ...interface{}) { - t(a).Helper() - msg = prepMsg(msg, `Should not be nil, but it is`) - - a(!isNil(actual), msg...) + a.t.Helper() + a.f(!isNil(actual), msg, `Should not be nil, but it is`) } ///// Errors ///// // Error asserts the given error is not nil func (a Assert) Error(actual error, msg ...interface{}) { - t(a).Helper() - msg = prepMsg(msg, `Expected an error, but got nil`) - a(actual != nil, msg...) + a.t.Helper() + a.f(actual != nil, msg, `Expected an error, but got nil`) } // NoError asserts the given error is not nil func (a Assert) NoError(actual error, msg ...interface{}) { - t(a).Helper() - msg = prepMsg(msg, `Expected no error, but got %#v`, actual) - a(actual == nil, msg...) + a.t.Helper() + a.f(actual == nil, msg, `Expected no error, but got %#v`, actual) } ///// Comparisons ///// // Eq asserts the given values match func (a Assert) Eq(expected, actual interface{}, msg ...interface{}) { - t(a).Helper() + a.t.Helper() if reflect.TypeOf(expected) != reflect.TypeOf(actual) { - msg = prepMsg(msg, `Expected %T(%#v), but got %T(%#v)`, expected, expected, actual, actual) - a(false, msg...) + a.f(false, msg, `Expected %T(%#v), but got %T(%#v)`, expected, expected, actual, actual) return } - msg = prepMsg(msg, `Expected %#v, but got %#v`, expected, actual) - a(expected == actual, msg...) + a.f(expected == actual, msg, `Expected %#v, but got %#v`, expected, actual) } // Ne asserts the given values don't match func (a Assert) Ne(expected, actual interface{}, msg ...interface{}) { - t(a).Helper() - msg = prepMsg(msg, `Should not be %#v, but it is`, expected) - a(expected != actual, msg...) + a.t.Helper() + a.f(expected != actual, msg, `Should not be %#v, but it is`, expected) } ///// Lists ///// // Contains asserts the expected value is in the given list func (a Assert) Contains(expected, list interface{}, msg ...interface{}) { - t(a).Helper() + a.t.Helper() rlist := reflect.ValueOf(list) - a(rlist.Kind() == reflect.Slice || rlist.Kind() == reflect.Array, `Can only call assert.Contains on a slice or array`) + a.f(rlist.Kind() == reflect.Slice || rlist.Kind() == reflect.Array, nil, `Can only call assert.Contains on a slice or array`) for i := 0; i < rlist.Len(); i++ { if rlist.Index(i).Interface() == expected { return } } - msg = prepMsg(msg, `Expected %#v to be in %#v, but it isn't`, expected, list) - a(false, msg...) + a.f(false, msg, `Expected %#v to be in %#v, but it isn't`, expected, list) } // SameElements asserts the values have the same elements. It ignores the order of the elements func (a Assert) SameElements(expected, actual interface{}, msg ...interface{}) { - t(a).Helper() + a.t.Helper() rexpected, ractual := reflect.ValueOf(expected), reflect.ValueOf(actual) - a(rexpected.Kind() == reflect.Slice || rexpected.Kind() == reflect.Array, `Can only call assert.SameElements on a slice or array`) - a(ractual.Kind() == reflect.Slice || ractual.Kind() == reflect.Array, `Can only call assert.SameElements on a slice or array`) + a.f(rexpected.Kind() == reflect.Slice || rexpected.Kind() == reflect.Array, nil, `Can only call assert.SameElements on a slice or array`) + a.f(ractual.Kind() == reflect.Slice || ractual.Kind() == reflect.Array, nil, `Can only call assert.SameElements on a slice or array`) - msg = prepMsg(msg, `Expected elements of %#v to match %#v, but they don't`, expected, actual) if rexpected.Len() != ractual.Len() { - a(false, msg...) + a.f(false, msg, `Expected elements of %#v to match %#v, but they don't`, expected, actual) return } @@ -167,14 +153,15 @@ func (a Assert) SameElements(expected, actual interface{}, msg ...interface{}) { return } - a(false, msg...) + a.f(false, msg, ``) } func (a Assert) Cmp(expected, actual interface{}, opts ...cmp.Option) { + a.t.Helper() diff := cmp.Diff(expected, actual, opts...) if diff == `` { return } - a(false, "\n"+diff) + a.f(false, nil, "\n"+diff) } diff --git a/helper.go b/helper.go index e94190b..d8d5e63 100644 --- a/helper.go +++ b/helper.go @@ -2,8 +2,6 @@ package assert import ( "fmt" - "reflect" - "runtime" "strconv" ) @@ -14,10 +12,3 @@ func prepMsg(msg []interface{}, format string, args ...interface{}) []interface{ func shell(i int) string { return "\x1B[" + strconv.Itoa(i) + "m" } - -var ts = map[*runtime.Func]T{} - -func t(a Assert) T { - f := runtime.FuncForPC(reflect.ValueOf(a).Pointer()) - return ts[f] -} From 0ead6dfb9937b1a9b1388c81d3fe24d4139e43d3 Mon Sep 17 00:00:00 2001 From: NiseVoid Date: Fri, 12 Jun 2020 16:27:05 +0200 Subject: [PATCH 6/8] Remove Cleanup from T interface --- faket_test.go | 3 +-- type.go | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/faket_test.go b/faket_test.go index 7f5cd33..1a494e0 100644 --- a/faket_test.go +++ b/faket_test.go @@ -13,8 +13,7 @@ func (t *fakeT) Error(_ ...interface{}) { t.gotError = true } -func (_ *fakeT) Helper() {} -func (_ *fakeT) Cleanup(_ func()) {} +func (_ *fakeT) Helper() {} func (t *fakeT) GotError() bool { r := t.gotError diff --git a/type.go b/type.go index 2a4b72a..1c9a1fd 100644 --- a/type.go +++ b/type.go @@ -4,5 +4,4 @@ package assert type T interface { Error(...interface{}) Helper() - Cleanup(func()) } From 121a4e740af7c29584b27b6979a9f750f80dd720 Mon Sep 17 00:00:00 2001 From: Crow Crowcrow Date: Fri, 12 Jun 2020 19:35:26 +0200 Subject: [PATCH 7/8] Add NCmp --- assert.go | 9 +++++++++ assert_test.go | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/assert.go b/assert.go index 6c762c3..b89a5f2 100644 --- a/assert.go +++ b/assert.go @@ -156,6 +156,7 @@ func (a Assert) SameElements(expected, actual interface{}, msg ...interface{}) { a.f(false, msg, ``) } +// Cmp assert wrapper for go-cmp func (a Assert) Cmp(expected, actual interface{}, opts ...cmp.Option) { a.t.Helper() diff := cmp.Diff(expected, actual, opts...) @@ -165,3 +166,11 @@ func (a Assert) Cmp(expected, actual interface{}, opts ...cmp.Option) { a.f(false, nil, "\n"+diff) } + +// NCmp assert wrapper for go-cmp but fails when !Equal +func (a Assert) NCmp(expected, actual interface{}, opts ...cmp.Option) { + a.t.Helper() + + ok := cmp.Equal(expected, actual, opts...) + a.f(!ok, nil, `Should not be %#v, but it is`, expected) +} diff --git a/assert_test.go b/assert_test.go index e0a2323..0b31a12 100644 --- a/assert_test.go +++ b/assert_test.go @@ -177,3 +177,18 @@ func TestCmpTimezones(t *testing.T) { fa.Cmp(&A{ti}, &A{ti2}) assert.True(ft.GotError()) } + +func TestNCmp(t *testing.T) { + assert := New(t) + ft, fa := newFakeT() + + type A struct { + S string + } + + fa.NCmp(A{"not"}, A{"equal"}) + assert.False(ft.GotError()) + + fa.NCmp(A{"equal"}, A{"equal"}) + assert.True(ft.GotError()) +} From 9850e4f385d01b236b1dbea576bf498b40fa7d61 Mon Sep 17 00:00:00 2001 From: NiseVoid Date: Tue, 29 Sep 2020 12:13:45 +0200 Subject: [PATCH 8/8] Fix comments --- assert.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assert.go b/assert.go index b89a5f2..791b600 100644 --- a/assert.go +++ b/assert.go @@ -42,7 +42,7 @@ func (a Assert) True(actual bool, msg ...interface{}) { a.f(actual, msg, `Should be true, but it isn't`) } -// False sserts the given value is false +// False asserts the given value is false func (a Assert) False(actual bool, msg ...interface{}) { a.t.Helper() a.f(!actual, msg, `Should be false, but it isn't`) @@ -70,7 +70,7 @@ func (a Assert) Nil(actual interface{}, msg ...interface{}) { a.f(isNil(actual), msg, `Should be nil, but got %#v`, actual) } -// NotNil sserts the given value is not nil +// NotNil asserts the given value is not nil func (a Assert) NotNil(actual interface{}, msg ...interface{}) { a.t.Helper() a.f(!isNil(actual), msg, `Should not be nil, but it is`) @@ -84,7 +84,7 @@ func (a Assert) Error(actual error, msg ...interface{}) { a.f(actual != nil, msg, `Expected an error, but got nil`) } -// NoError asserts the given error is not nil +// NoError asserts the given error is nil func (a Assert) NoError(actual error, msg ...interface{}) { a.t.Helper() a.f(actual == nil, msg, `Expected no error, but got %#v`, actual)