Compare commits

...

11 Commits

8 changed files with 333 additions and 66 deletions

29
LICENSE Normal file
View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2019, Fuyu
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

128
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,81 +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 it isn't`)
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()
msg = prepMsg(msg, `Expected %#v, but got %#v`, expected, actual)
a(expected == actual, msg...)
a.t.Helper()
if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
a.f(false, msg, `Expected %T(%#v), but got %T(%#v)`, expected, expected, actual, actual)
return
}
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
}
@ -156,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()
}