Move massert into internal, and simplify it a lot

The previous massert isn't that useful compared to testify, but it's
nice to have some assertion helpers in the repo which don't require a
sub-dependency.
This commit is contained in:
Brian Picciano 2023-12-26 16:08:03 +01:00
parent 7f1cf24293
commit 9567a98606
7 changed files with 81 additions and 773 deletions

2
go.mod
View File

@ -1,3 +1,3 @@
module github.com/mediocregopher/mediocre-go-lib/v2 module github.com/mediocregopher/mediocre-go-lib/v2
go 1.15 go 1.20

View File

@ -0,0 +1,26 @@
// massert helper assertion methods for tests.
package massert
import (
"reflect"
"testing"
)
// Equal fatals if reflect.DeepEqual fails.
func Equal[T any](t testing.TB, want, got T) {
t.Helper()
if !reflect.DeepEqual(want, got) {
t.Fatalf("%#v != %#v", want, got)
}
}
// Equalf is like Equal but extra formatting is appended.
func Equalf[T any](
t testing.TB, want, got T, fmtStr string, fmtArgs ...any,
) {
t.Helper()
if !reflect.DeepEqual(want, got) {
fmtArgs = append([]any{want, got}, fmtArgs...)
t.Fatalf("%#v != %#v "+fmtStr, fmtArgs...)
}
}

View File

@ -2,9 +2,8 @@ package mctx
import ( import (
"context" "context"
"reflect"
. "testing" . "testing"
"github.com/mediocregopher/mediocre-go-lib/v2/mtest/massert"
) )
type testAnnotator [2]string type testAnnotator [2]string
@ -22,32 +21,36 @@ func TestAnnotate(t *T) {
aa := Annotations{} aa := Annotations{}
EvaluateAnnotations(ctx, aa) EvaluateAnnotations(ctx, aa)
massert.Require(t, wantAA := Annotations{
massert.Equal(Annotations{
"a": "foo", "a": "foo",
"b": "BAR", "b": "BAR",
}, aa), }
)
if !reflect.DeepEqual(wantAA, aa) {
t.Fatalf("%#v != %#v", wantAA, aa)
}
} }
func TestAnnotationsStringMap(t *T) { func TestAnnotationsStringMap(t *T) {
type A int type A int
type B int type B int
aa := Annotations{ got := Annotations{
0: "zero", 0: "zero",
1: "one", 1: "one",
A(2): "two", A(2): "two",
B(2): "TWO", B(2): "TWO",
} }.StringMap()
massert.Require(t, want := map[string]string{
massert.Equal(map[string]string{
"0": "zero", "0": "zero",
"1": "one", "1": "one",
"mctx.A(2)": "two", "mctx.A(2)": "two",
"mctx.B(2)": "TWO", "mctx.B(2)": "TWO",
}, aa.StringMap()), }
)
if !reflect.DeepEqual(want, got) {
t.Fatalf("%#v != %#v", want, got)
}
} }
func TestMergeAnnotations(t *T) { func TestMergeAnnotations(t *T) {
@ -60,12 +63,14 @@ func TestMergeAnnotations(t *T) {
aa := Annotations{} aa := Annotations{}
EvaluateAnnotations(ctx, aa) EvaluateAnnotations(ctx, aa)
err := massert.Equal(map[string]string{ got := aa.StringMap()
want := map[string]string{
"0": "ZERO", "0": "ZERO",
"1": "ONE", "1": "ONE",
"2": "TWO", "2": "TWO",
}, aa.StringMap()).Assert() }
if err != nil {
t.Fatal(err) if !reflect.DeepEqual(want, got) {
t.Fatalf("%#v != %#v", want, got)
} }
} }

View File

@ -6,12 +6,12 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/mediocregopher/mediocre-go-lib/v2/internal/massert"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx" "github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mtest/massert"
) )
func TestFullError(t *testing.T) { func TestFullError(t *testing.T) {
massert.Require(t, massert.Nil(Wrap(context.Background(), nil))) massert.Equal(t, nil, Wrap(context.Background(), nil))
ctx := mctx.Annotate(context.Background(), ctx := mctx.Annotate(context.Background(),
"a", "aaa aaa\n", "a", "aaa aaa\n",
@ -27,7 +27,7 @@ func TestFullError(t *testing.T) {
ccc ccc
* d: weird key but ok * d: weird key but ok
* line: merr/merr_test.go:22` * line: merr/merr_test.go:22`
massert.Require(t, massert.Equal(exp, e.(Error).FullError())) massert.Equal(t, exp, e.(Error).FullError())
} }
{ {
@ -39,7 +39,7 @@ func TestFullError(t *testing.T) {
ccc ccc
* d: weird key but ok * d: weird key but ok
* line: merr/merr_test.go:34` * line: merr/merr_test.go:34`
massert.Require(t, massert.Equal(exp, e.(Error).FullError())) massert.Equal(t, exp, e.(Error).FullError())
} }
} }
@ -136,10 +136,9 @@ func TestAsIsError(t *testing.T) {
var in Error var in Error
ok := errors.As(test.in, &in) ok := errors.As(test.in, &in)
massert.Require(t, massert.Comment( massert.Equalf(
massert.Equal(test.expAs != nil, ok), t, ok, test.expAs != nil, "test.in:%#v ok:%v", test.in, ok,
"test.in:%#v ok:%v", test.in, ok, )
))
if test.expAs == nil { if test.expAs == nil {
return return
@ -152,15 +151,13 @@ func TestAsIsError(t *testing.T) {
in.Ctx = nil in.Ctx = nil
expAs.Ctx = nil expAs.Ctx = nil
massert.Require(t, massert.Equal(t, expAsAA, inAA)
massert.Equal(expAsAA, inAA), massert.Equal(t, expAs, in)
massert.Equal(expAs, in), massert.Equalf(
massert.Comment( t, true, errors.Is(test.in, test.expIs),
massert.Equal(true, errors.Is(test.in, test.expIs)),
"errors.Is(\ntest.in:%#v,\ntest.expIs:%#v,\n)", test.in, test.expIs, "errors.Is(\ntest.in:%#v,\ntest.expIs:%#v,\n)", test.in, test.expIs,
),
massert.Equal(test.expStr, test.in.Error()),
) )
massert.Equal(t, test.expStr, test.in.Error())
}) })
} }
} }

View File

@ -6,39 +6,36 @@ import (
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
"testing"
. "testing" . "testing"
"time" "time"
"github.com/mediocregopher/mediocre-go-lib/v2/internal/massert"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx" "github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mtest/massert"
) )
func TestTruncate(t *T) { func TestTruncate(t *T) {
massert.Require(t, massert.Equal(t, "abc", Truncate("abc", 4))
massert.Equal("abc", Truncate("abc", 4)), massert.Equal(t, "abc", Truncate("abc", 3))
massert.Equal("abc", Truncate("abc", 3)), massert.Equal(t, "ab...", Truncate("abc", 2))
massert.Equal("ab...", Truncate("abc", 2)),
)
} }
func TestLogger(t *T) { func TestJSONLogger(t *T) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
now := time.Now().UTC() now := time.Now().UTC()
td, ts := now.Format(msgTimeFormat), fmt.Sprint(now.UnixNano()) td, ts := now.Format(msgTimeFormat), fmt.Sprint(now.UnixNano())
l := NewLogger(&LoggerOpts{ l := NewLogger(&LoggerOpts{
MessageHandler: NewMessageHandler(buf), MessageHandler: NewJSONMessageHandler(buf),
Now: func() time.Time { return now }, Now: func() time.Time { return now },
}) })
assertOut := func(expected string) massert.Assertion { assertOut := func(t *testing.T, expected string) {
expected = strings.ReplaceAll(expected, "<TD>", td) expected = strings.ReplaceAll(expected, "<TD>", td)
expected = strings.ReplaceAll(expected, "<TS>", ts) expected = strings.ReplaceAll(expected, "<TS>", ts)
out, err := buf.ReadString('\n') out, err := buf.ReadString('\n')
return massert.All( massert.Equal(t, nil, err)
massert.Nil(err), massert.Equal(t, expected, strings.TrimSpace(out))
massert.Equal(expected, strings.TrimSpace(out)),
)
} }
ctx := context.Background() ctx := context.Background()
@ -48,23 +45,17 @@ func TestLogger(t *T) {
l.Info(ctx, "bar") l.Info(ctx, "bar")
l.Warn(ctx, "baz", errors.New("ERR")) l.Warn(ctx, "baz", errors.New("ERR"))
l.Error(ctx, "buz", errors.New("ERR")) l.Error(ctx, "buz", errors.New("ERR"))
massert.Require(t, assertOut(t, `{"td":"<TD>","ts":<TS>,"level":"INFO","descr":"bar","level_int":30}`)
assertOut(`{"td":"<TD>","ts":<TS>,"level":"INFO","descr":"bar","level_int":30}`), assertOut(t, `{"td":"<TD>","ts":<TS>,"level":"WARN","descr":"baz","level_int":20,"annotations":{"errMsg":"ERR"}}`)
assertOut(`{"td":"<TD>","ts":<TS>,"level":"WARN","descr":"baz","level_int":20,"annotations":{"errMsg":"ERR"}}`), assertOut(t, `{"td":"<TD>","ts":<TS>,"level":"ERROR","descr":"buz","level_int":10,"annotations":{"errMsg":"ERR"}}`)
assertOut(`{"td":"<TD>","ts":<TS>,"level":"ERROR","descr":"buz","level_int":10,"annotations":{"errMsg":"ERR"}}`),
)
// annotate context // annotate context
ctx = mctx.Annotate(ctx, "foo", "bar") ctx = mctx.Annotate(ctx, "foo", "bar")
l.Info(ctx, "bar") l.Info(ctx, "bar")
massert.Require(t, assertOut(t, `{"td":"<TD>","ts":<TS>,"level":"INFO","descr":"bar","level_int":30,"annotations":{"foo":"bar"}}`)
assertOut(`{"td":"<TD>","ts":<TS>,"level":"INFO","descr":"bar","level_int":30,"annotations":{"foo":"bar"}}`),
)
// add namespace // add namespace
l = l.WithNamespace("ns") l = l.WithNamespace("ns")
l.Info(ctx, "bar") l.Info(ctx, "bar")
massert.Require(t, assertOut(t, `{"td":"<TD>","ts":<TS>,"level":"INFO","ns":["ns"],"descr":"bar","level_int":30,"annotations":{"foo":"bar"}}`)
assertOut(`{"td":"<TD>","ts":<TS>,"level":"INFO","ns":["ns"],"descr":"bar","level_int":30,"annotations":{"foo":"bar"}}`),
)
} }

View File

@ -1,462 +0,0 @@
// Package massert implements an assertion framework which is useful in tests.
package massert
import (
"bytes"
"errors"
"fmt"
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
"testing"
"text/tabwriter"
)
// AssertErr is an error returned by Assertions which have failed, containing
// information about both the reason for failure and the Assertion itself.
type AssertErr struct {
Err error // The error which occurred
Assertion Assertion // The Assertion which failed
}
func fmtBlock(str string) string {
if strings.Index(str, "\n") == -1 {
return str
}
return "\n\t" + strings.Replace(str, "\n", "\n\t", -1) + "\n"
}
func fmtMultiBlock(prefix string, elems ...string) string {
if len(elems) == 0 {
return prefix + "()"
} else if len(elems) == 1 {
return prefix + "(" + fmtBlock(elems[0]) + ")"
}
buf := new(bytes.Buffer)
fmt.Fprintf(buf, "%s(\n", prefix)
for _, el := range elems {
elStr := "\t" + strings.Replace(el, "\n", "\n\t", -1)
fmt.Fprintf(buf, "%s,\n", elStr)
}
fmt.Fprintf(buf, ")")
return buf.String()
}
func fmtMultiDescr(prefix string, aa ...Assertion) string {
descrs := make([]string, len(aa))
for i := range aa {
descrs[i] = aa[i].Description()
}
return fmtMultiBlock(prefix, descrs...)
}
func fmtStack(frames []runtime.Frame) string {
buf := new(bytes.Buffer)
tw := tabwriter.NewWriter(buf, 0, 4, 2, ' ', 0)
for _, frame := range frames {
file := filepath.Base(frame.File)
fmt.Fprintf(tw, "%s:%d\t%s\n", file, frame.Line, frame.Function)
}
if err := tw.Flush(); err != nil {
panic(err) // fuck it
}
return buf.String()
}
func (ae AssertErr) Error() string {
buf := new(bytes.Buffer)
fmt.Fprintf(buf, "\n")
fmt.Fprintf(buf, "Assertion: %s\n", fmtBlock(ae.Assertion.Description()))
fmt.Fprintf(buf, "Error: %s\n", fmtBlock(ae.Err.Error()))
fmt.Fprintf(buf, "Stack: %s\n", fmtBlock(fmtStack(ae.Assertion.Stack())))
return buf.String()
}
////////////////////////////////////////////////////////////////////////////////
// Assertion is an entity which will make some kind of assertion and produce an
// error if that assertion does not hold true. The error returned will generally
// be of type AssertErr.
type Assertion interface {
Assert() error
Description() string // A description of the Assertion
// Returns the callstack of where the Assertion was created, ordered from
// closest to farthest. This may not necessarily contain the entire
// callstack if that would be inconveniently cumbersome.
Stack() []runtime.Frame
}
const maxStackLen = 8
type assertion struct {
fn func() error
descr string
stack []runtime.Frame
}
func newFutureAssertion(assertFn func() error, descr string, skip int) Assertion {
pcs := make([]uintptr, maxStackLen)
// first skip is for runtime.Callers, second is for newAssertion, third is
// for whatever is calling newAssertion
numPCs := runtime.Callers(skip+3, pcs)
stack := make([]runtime.Frame, 0, maxStackLen)
frames := runtime.CallersFrames(pcs[:numPCs])
for {
frame, more := frames.Next()
stack = append(stack, frame)
if !more || len(stack) == maxStackLen {
break
}
}
a := &assertion{
descr: descr,
stack: stack,
}
a.fn = func() error {
err := assertFn()
if err == nil {
return nil
} else if ae, ok := err.(AssertErr); ok {
return ae
}
return AssertErr{
Err: err,
Assertion: a,
}
}
return a
}
func newAssertion(assertFn func() error, descr string, skip int) Assertion {
err := assertFn()
return newFutureAssertion(func() error { return err }, descr, skip+1)
}
func (a *assertion) Assert() error {
return a.fn()
}
func (a *assertion) Description() string {
return a.descr
}
func (a *assertion) Stack() []runtime.Frame {
return a.stack
}
////////////////////////////////////////////////////////////////////////////////
// Require is a convenience function which performs the Assertions and calls
// Fatal on the testing.T instance for the first Assertion which fails.
func Require(t *testing.T, aa ...Assertion) {
for _, a := range aa {
if err := a.Assert(); err != nil {
t.Fatal(err)
}
}
}
// Assert is a convenience function which performs the Assertion and calls Error
// on the testing.T instance for the first Assertion which fails.
func Assert(t *testing.T, aa ...Assertion) {
for _, a := range aa {
if err := a.Assert(); err != nil {
t.Error(err)
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Assertion wrappers
// if the Assertion is a wrapper for another, this makes sure that if the
// underlying one returns an AssertErr that this Assertion is what ends up in
// that AssertErr
type wrap struct {
Assertion
}
func (wa wrap) Assert() error {
err := wa.Assertion.Assert()
if err == nil {
return nil
}
ae := err.(AssertErr)
ae.Assertion = wa.Assertion
return ae
}
type descrWrap struct {
Assertion
descr string
}
func (dw descrWrap) Description() string {
return dw.descr
}
// Comment prepends a formatted string to the given Assertion's string
// description.
func Comment(a Assertion, msg string, args ...interface{}) Assertion {
msg = strings.TrimSpace(msg)
descr := fmt.Sprintf("/* "+msg+" */\n", args...)
descr += a.Description()
return wrap{descrWrap{Assertion: a, descr: descr}}
}
// Not negates an Assertion, so that it fails if the given Assertion does not,
// and vice-versa.
func Not(a Assertion) Assertion {
fn := func() error {
if err := a.Assert(); err == nil {
return errors.New("assertion should have failed")
}
return nil
}
return newAssertion(fn, fmtMultiDescr("Not", a), 0)
}
// Any asserts that at least one of the given Assertions succeeds.
func Any(aa ...Assertion) Assertion {
fn := func() error {
for _, a := range aa {
if err := a.Assert(); err == nil {
return nil
}
}
return errors.New("no assertions succeeded")
}
return newAssertion(fn, fmtMultiDescr("Any", aa...), 0)
}
// AnyOne asserts that exactly one of the given Assertions succeeds.
func AnyOne(aa ...Assertion) Assertion {
fn := func() error {
any := -1
for i, a := range aa {
if err := a.Assert(); err == nil {
if any >= 0 {
return fmt.Errorf("assertions indices %d and %d both succeeded", any, i)
}
any = i
}
}
if any == -1 {
return errors.New("no assertions succeeded")
}
return nil
}
return newAssertion(fn, fmtMultiDescr("AnyOne", aa...), 0)
}
// All asserts that at all of the given Assertions succeed. Its Assert method
// will return the error of whichever Assertion failed.
func All(aa ...Assertion) Assertion {
fn := func() error {
for _, a := range aa {
if err := a.Assert(); err != nil {
// newAssertion will pass this error through, so that its
// description and callstack is what gets displayed as the
// error. This isn't totally consistent with Any's behavior, but
// it's fine.
return err
}
}
return nil
}
return newAssertion(fn, fmtMultiDescr("All", aa...), 0)
}
// None asserts that all of the given Assertions fail.
//
// NOTE this is functionally equivalent to doing `Not(Any(aa...))`, but the
// error returned is more helpful.
func None(aa ...Assertion) Assertion {
fn := func() error {
for _, a := range aa {
if err := a.Assert(); err == nil {
return AssertErr{
Err: errors.New("assertion should not have succeeded"),
Assertion: a,
}
}
}
return nil
}
return newAssertion(fn, fmtMultiDescr("None", aa...), 0)
}
// Error returns an Assertion which always fails with the given error.
func Error(err error) Assertion {
return newAssertion(func() error { return err }, "", 0)
}
// Errorf is like Err but allows for a formatted string.
func Errorf(str string, args ...interface{}) Assertion {
return Error(fmt.Errorf(str, args...))
}
////////////////////////////////////////////////////////////////////////////////
func toStr(i interface{}) string {
return fmt.Sprintf("%T(%#v)", i, i)
}
// Equal asserts that the two values are exactly equal, and uses the
// reflect.DeepEqual function to determine if they are.
func Equal(a, b interface{}) Assertion {
return newAssertion(func() error {
if !reflect.DeepEqual(a, b) {
return errors.New("not exactly equal")
}
return nil
}, toStr(a)+" == "+toStr(b), 0)
}
// Nil asserts that the value is nil. This assertion works both if the value is
// the untyped nil value (e.g. `Nil(nil)`) or if it's a typed nil value (e.g.
// `Nil([]byte(nil))`).
func Nil(i interface{}) Assertion {
return newAssertion(func() error {
if i == nil {
return nil
}
v := reflect.ValueOf(i)
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface,
reflect.Map, reflect.Ptr, reflect.Slice:
if v.IsNil() {
return nil
}
default:
}
return errors.New("not nil")
}, toStr(i)+" is nil", 0)
}
type setKV struct {
k, v interface{}
}
func toSet(i interface{}, keyedMap bool) ([]interface{}, error) {
v := reflect.ValueOf(i)
switch v.Kind() {
case reflect.Array, reflect.Slice:
vv := make([]interface{}, v.Len())
for i := range vv {
vv[i] = v.Index(i).Interface()
}
return vv, nil
case reflect.Map:
keys := v.MapKeys()
vv := make([]interface{}, len(keys))
for i := range keys {
if keyedMap {
vv[i] = setKV{
k: keys[i].Interface(),
v: v.MapIndex(keys[i]).Interface(),
}
} else {
vv[i] = v.MapIndex(keys[i]).Interface()
}
}
return vv, nil
default:
return nil, fmt.Errorf("cannot turn value of type %s into a set", v.Type())
}
}
// Subset asserts that the given subset is a subset of the given set. Both must
// be of the same type and may be arrays, slices, or maps.
func Subset(set, subset interface{}) Assertion {
if reflect.TypeOf(set) != reflect.TypeOf(subset) {
panic(errors.New("set and subset aren't of same type"))
}
setVV, err := toSet(set, true)
if err != nil {
panic(err)
}
subsetVV, err := toSet(subset, true)
if err != nil {
panic(err)
}
return newAssertion(func() error {
// this is obviously not the most efficient way to do this
outer:
for i := range subsetVV {
for j := range setVV {
if reflect.DeepEqual(setVV[j], subsetVV[i]) {
continue outer
}
}
return fmt.Errorf("missing element %s", toStr(subsetVV[i]))
}
return nil
}, toStr(set)+" has subset "+toStr(subset), 0)
}
// HasValue asserts that the given set has the given element as a value in it.
// The set may be an array, a slice, or a map, and if it's a map then the elem
// will need to be a value in it.
func HasValue(set, elem interface{}) Assertion {
setVV, err := toSet(set, false)
if err != nil {
panic(err)
}
return newAssertion(func() error {
for i := range setVV {
if reflect.DeepEqual(setVV[i], elem) {
return nil
}
}
return errors.New("value not in set")
}, toStr(set)+" has value "+toStr(elem), 0)
}
// HasKey asserts that the given set (which must be a map type) has the given
// element as a key in it.
func HasKey(set, elem interface{}) Assertion {
if v := reflect.ValueOf(set); v.Kind() != reflect.Map {
panic(fmt.Errorf("type %s is not a map", v.Type()))
}
setVV, err := toSet(set, true)
if err != nil {
panic(err)
}
return newAssertion(func() error {
for _, kv := range setVV {
if reflect.DeepEqual(kv.(setKV).k, elem) {
return nil
}
}
return errors.New("value not a key in the map")
}, toStr(set)+" has key "+toStr(elem), 0)
}
// Length asserts that the given set has the given number of elements in it. The
// set may be an array, a slice, or a map. A nil value'd set is considered to be
// a length of zero.
func Length(set interface{}, length int) Assertion {
setVV, err := toSet(set, false)
if err != nil {
panic(err)
}
return newAssertion(func() error {
if len(setVV) != length {
return fmt.Errorf("set not correct length, is %d", len(setVV))
}
return nil
}, toStr(set)+" has length "+strconv.Itoa(length), 0)
}
// TODO ChanRead(ch interface{}, within time.Duration, callback func(interface{}) error)
// TODO ChanBlock(ch interface{}, for time.Duration)
// TODO ChanClosed(ch interface{})

View File

@ -1,249 +0,0 @@
package massert
import (
"errors"
. "testing"
)
func succeed() Assertion {
return newAssertion(func() error { return nil }, "Succeed", 0)
}
func fail() Assertion {
return newAssertion(func() error { return errors.New("failure") }, "Fail", 0)
}
func TestNot(t *T) {
if err := Not(succeed()).Assert(); err == nil {
t.Fatal("Not(succeed()) should have failed")
}
if err := Not(fail()).Assert(); err != nil {
t.Fatal(err)
}
}
func TestAny(t *T) {
if err := Any().Assert(); err == nil {
t.Fatal("empty Any should fail")
}
if err := Any(succeed(), succeed()).Assert(); err != nil {
t.Fatal(err)
}
if err := Any(succeed(), fail()).Assert(); err != nil {
t.Fatal(err)
}
if err := Any(fail(), fail()).Assert(); err == nil {
t.Fatal("Any should have failed with all inner fail Assertions")
}
}
func TestAnyOne(t *T) {
if err := AnyOne().Assert(); err == nil {
t.Fatal("empty AnyOne should fail")
}
if err := AnyOne(succeed(), succeed()).Assert(); err == nil {
t.Fatal("AnyOne with two succeeds should fail")
}
if err := AnyOne(succeed(), fail()).Assert(); err != nil {
t.Fatal(err)
}
if err := AnyOne(fail(), fail()).Assert(); err == nil {
t.Fatal("AnyOne should have failed with all inner fail Assertions")
}
}
func TestAll(t *T) {
if err := All().Assert(); err != nil {
t.Fatal(err)
}
if err := All(succeed(), succeed()).Assert(); err != nil {
t.Fatal(err)
}
if err := All(succeed(), fail()).Assert(); err == nil {
t.Fatal("All should have failed with one inner fail Assertion")
}
if err := All(fail(), fail()).Assert(); err == nil {
t.Fatal("All should have failed with all inner fail Assertions")
}
}
func TestNone(t *T) {
if err := None().Assert(); err != nil {
t.Fatal(err)
}
if err := None(succeed(), succeed()).Assert(); err == nil {
t.Fatal("None should have failed with all inner succeed Assertions")
}
if err := None(succeed(), fail()).Assert(); err == nil {
t.Fatal("None should have failed with one inner succeed Assertion")
}
if err := None(fail(), fail()).Assert(); err != nil {
t.Fatal(err)
}
}
func TestEqual(t *T) {
Require(t,
Equal(1, 1),
Equal("foo", "foo"),
)
Require(t, None(
Equal(1, 2),
Equal(1, int64(1)),
Equal(1, uint64(1)),
Equal("foo", "bar"),
))
// test that assertions take in the value at the moment the assertion is
// made
var aa []Assertion
m := map[string]int{}
m["foo"] = 1
aa = append(aa, Equal(1, m["foo"]))
m["foo"] = 2
aa = append(aa, Equal(2, m["foo"]))
Require(t, aa...)
}
func TestNil(t *T) {
Require(t,
Nil(nil),
Nil([]byte(nil)),
Nil(map[int]int(nil)),
Nil((*struct{})(nil)),
Nil(interface{}(nil)),
Nil(error(nil)),
)
Require(t, None(
Nil(1),
Nil([]byte("foo")),
Nil(map[int]int{1: 1}),
Nil(&struct{}{}),
Nil(interface{}("hi")),
Nil(errors.New("some error")),
))
}
func TestSubset(t *T) {
Require(t,
Subset([]int{1, 2, 3}, []int{}),
Subset([]int{1, 2, 3}, []int{1}),
Subset([]int{1, 2, 3}, []int{2}),
Subset([]int{1, 2, 3}, []int{1, 2}),
Subset([]int{1, 2, 3}, []int{2, 1}),
Subset([]int{1, 2, 3}, []int{1, 2, 3}),
Subset([]int{1, 2, 3}, []int{1, 3, 2}),
Subset(map[int]int{1: 1, 2: 2}, map[int]int{}),
Subset(map[int]int{1: 1, 2: 2}, map[int]int{1: 1}),
Subset(map[int]int{1: 1, 2: 2}, map[int]int{1: 1, 2: 2}),
)
Require(t, None(
Subset([]int{}, []int{1, 2, 3}),
Subset([]int{1, 2, 3}, []int{4}),
Subset([]int{1, 2, 3}, []int{1, 3, 2, 4}),
Subset(map[int]int{1: 1, 2: 2}, map[int]int{1: 2}),
Subset(map[int]int{1: 1, 2: 2}, map[int]int{1: 1, 3: 3}),
))
// make sure changes don't retroactively fail the assertion
m := map[int]int{1: 1, 2: 2}
a := Subset(m, map[int]int{1: 1})
m[1] = 2
Require(t, a)
}
func TestHasValue(t *T) {
Require(t,
HasValue([]int{1}, 1),
HasValue([]int{1, 2}, 1),
HasValue([]int{2, 1}, 1),
HasValue(map[int]int{1: 1}, 1),
HasValue(map[int]int{1: 2}, 2),
HasValue(map[int]int{1: 2, 2: 1}, 1),
HasValue(map[int]int{1: 2, 2: 2}, 2),
)
Require(t, None(
HasValue([]int{}, 1),
HasValue([]int{1}, 2),
HasValue([]int{2, 1}, 3),
HasValue(map[int]int{}, 1),
HasValue(map[int]int{1: 1}, 2),
HasValue(map[int]int{1: 2}, 1),
HasValue(map[int]int{1: 2, 2: 1}, 3),
))
// make sure changes don't retroactively fail the assertion
m := map[int]int{1: 1}
a := HasValue(m, 1)
m[1] = 2
Require(t, a)
}
func TestHasKey(t *T) {
Require(t,
HasKey(map[int]int{1: 1}, 1),
HasKey(map[int]int{1: 1, 2: 2}, 1),
HasKey(map[int]int{1: 1, 2: 2}, 2),
)
Require(t, None(
HasKey(map[int]int{}, 1),
HasKey(map[int]int{2: 2}, 1),
))
// make sure changes don't retroactively fail the assertion
m := map[int]int{1: 1}
a := HasKey(m, 1)
delete(m, 1)
Require(t, a)
}
func TestLength(t *T) {
Require(t,
Length([]int(nil), 0),
Length([]int{}, 0),
Length([]int{1}, 1),
Length([]int{1, 2}, 2),
Length(map[int]int(nil), 0),
Length(map[int]int{}, 0),
Length(map[int]int{1: 1}, 1),
Length(map[int]int{1: 1, 2: 2}, 2),
)
Require(t, None(
Length([]int(nil), 1),
Length([]int{}, 1),
Length([]int{1}, 0),
Length([]int{1}, 2),
Length([]int{1, 2}, 1),
Length([]int{1, 2}, 3),
Length(map[int]int(nil), 1),
Length(map[int]int{}, 1),
))
// make sure changes don't retroactively fail the assertion
m := map[int]int{1: 1}
a := Length(m, 1)
m[2] = 2
Require(t, a)
}