Compare commits

...

8 Commits

Author SHA1 Message Date
Nise Void 9850e4f385
Fix comments 2020-09-29 12:13:45 +02:00
Crow Crowcrow 121a4e740a Add NCmp 2020-06-12 19:35:26 +02:00
Nise Void 0ead6dfb99
Remove Cleanup from T interface 2020-06-12 16:27:05 +02:00
Nise Void 58bacd98ce
Fix calls to t.Helper and optimize 2020-05-27 19:56:16 +02:00
Nise Void 0cc5d27277
Add tests 2020-05-27 18:54:17 +02:00
Nise Void 5f6f4dd047
Add Cmp 2020-05-12 18:00:50 +02:00
Nise Void d20ac9444a
Use Cleanup to remove old entries in ts map 2020-05-12 18:00:07 +02:00
Crow Crowcrow fe8a28286e Accept an interface instead of testing.T (#1) 2020-01-17 22:36:24 +01:00
7 changed files with 300 additions and 68 deletions

126
assert.go
View File

@ -2,28 +2,32 @@ package assert
import (
"reflect"
"runtime"
"testing"
"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 *testing.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
func New(t T) Assert {
a := Assert{t}
return a
}
@ -34,16 +38,14 @@ func New(t *testing.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
// False asserts 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 /////
@ -64,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
// NotNil asserts 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
// NoError asserts the given error is 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
}
@ -162,5 +153,24 @@ func (a Assert) SameElements(expected, actual interface{}, msg ...interface{}) {
return
}
a(false, msg...)
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...)
if diff == `` {
return
}
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)
}

194
assert_test.go Normal file
View File

@ -0,0 +1,194 @@
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())
}
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())
}

23
faket_test.go Normal file
View File

@ -0,0 +1,23 @@
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 (t *fakeT) GotError() bool {
r := t.gotError
t.gotError = false
return r
}

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module git.fuyu.moe/Fuyu/assert
go 1.14
require github.com/google/go-cmp v0.4.0

3
go.sum Normal file
View File

@ -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=

View File

@ -2,10 +2,7 @@ package assert
import (
"fmt"
"reflect"
"runtime"
"strconv"
"testing"
)
func prepMsg(msg []interface{}, format string, args ...interface{}) []interface{} {
@ -15,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]*testing.T{}
func t(a Assert) *testing.T {
f := runtime.FuncForPC(reflect.ValueOf(a).Pointer())
return ts[f]
}

7
type.go Normal file
View File

@ -0,0 +1,7 @@
package assert
// T is an interface of what we use from testing.T
type T interface {
Error(...interface{})
Helper()
}