From 9567a98606feb1eccdb1fc88f76a344cd6b0437a Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Tue, 26 Dec 2023 16:08:03 +0100 Subject: [PATCH] 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. --- go.mod | 2 +- internal/massert/massert.go | 26 ++ mctx/annotate_test.go | 47 ++-- merr/merr_test.go | 29 +-- mlog/mlog_test.go | 39 ++- mtest/massert/massert.go | 462 ---------------------------------- mtest/massert/massert_test.go | 249 ------------------ 7 files changed, 81 insertions(+), 773 deletions(-) create mode 100644 internal/massert/massert.go delete mode 100644 mtest/massert/massert.go delete mode 100644 mtest/massert/massert_test.go diff --git a/go.mod b/go.mod index d488823..0a0da72 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/mediocregopher/mediocre-go-lib/v2 -go 1.15 +go 1.20 diff --git a/internal/massert/massert.go b/internal/massert/massert.go new file mode 100644 index 0000000..84bdaa8 --- /dev/null +++ b/internal/massert/massert.go @@ -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...) + } +} diff --git a/mctx/annotate_test.go b/mctx/annotate_test.go index 053c841..83dafb0 100644 --- a/mctx/annotate_test.go +++ b/mctx/annotate_test.go @@ -2,9 +2,8 @@ package mctx import ( "context" + "reflect" . "testing" - - "github.com/mediocregopher/mediocre-go-lib/v2/mtest/massert" ) type testAnnotator [2]string @@ -22,32 +21,36 @@ func TestAnnotate(t *T) { aa := Annotations{} EvaluateAnnotations(ctx, aa) - massert.Require(t, - massert.Equal(Annotations{ - "a": "foo", - "b": "BAR", - }, aa), - ) + wantAA := Annotations{ + "a": "foo", + "b": "BAR", + } + + if !reflect.DeepEqual(wantAA, aa) { + t.Fatalf("%#v != %#v", wantAA, aa) + } } func TestAnnotationsStringMap(t *T) { type A int type B int - aa := Annotations{ + got := Annotations{ 0: "zero", 1: "one", A(2): "two", B(2): "TWO", + }.StringMap() + + want := map[string]string{ + "0": "zero", + "1": "one", + "mctx.A(2)": "two", + "mctx.B(2)": "TWO", } - massert.Require(t, - massert.Equal(map[string]string{ - "0": "zero", - "1": "one", - "mctx.A(2)": "two", - "mctx.B(2)": "TWO", - }, aa.StringMap()), - ) + if !reflect.DeepEqual(want, got) { + t.Fatalf("%#v != %#v", want, got) + } } func TestMergeAnnotations(t *T) { @@ -60,12 +63,14 @@ func TestMergeAnnotations(t *T) { aa := Annotations{} EvaluateAnnotations(ctx, aa) - err := massert.Equal(map[string]string{ + got := aa.StringMap() + want := map[string]string{ "0": "ZERO", "1": "ONE", "2": "TWO", - }, aa.StringMap()).Assert() - if err != nil { - t.Fatal(err) + } + + if !reflect.DeepEqual(want, got) { + t.Fatalf("%#v != %#v", want, got) } } diff --git a/merr/merr_test.go b/merr/merr_test.go index 942b06b..1b1234a 100644 --- a/merr/merr_test.go +++ b/merr/merr_test.go @@ -6,12 +6,12 @@ import ( "fmt" "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/mtest/massert" ) 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(), "a", "aaa aaa\n", @@ -27,7 +27,7 @@ func TestFullError(t *testing.T) { ccc * d: weird key but ok * 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 * d: weird key but ok * 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 ok := errors.As(test.in, &in) - massert.Require(t, massert.Comment( - massert.Equal(test.expAs != nil, ok), - "test.in:%#v ok:%v", test.in, ok, - )) + massert.Equalf( + t, ok, test.expAs != nil, "test.in:%#v ok:%v", test.in, ok, + ) if test.expAs == nil { return @@ -152,15 +151,13 @@ func TestAsIsError(t *testing.T) { in.Ctx = nil expAs.Ctx = nil - massert.Require(t, - massert.Equal(expAsAA, inAA), - massert.Equal(expAs, in), - massert.Comment( - massert.Equal(true, errors.Is(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, expAsAA, inAA) + massert.Equal(t, expAs, in) + massert.Equalf( + t, true, errors.Is(test.in, test.expIs), + "errors.Is(\ntest.in:%#v,\ntest.expIs:%#v,\n)", test.in, test.expIs, ) + massert.Equal(t, test.expStr, test.in.Error()) }) } } diff --git a/mlog/mlog_test.go b/mlog/mlog_test.go index a9dfbaf..d7c1cf8 100644 --- a/mlog/mlog_test.go +++ b/mlog/mlog_test.go @@ -6,39 +6,36 @@ import ( "errors" "fmt" "strings" + "testing" . "testing" "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/mtest/massert" ) func TestTruncate(t *T) { - massert.Require(t, - massert.Equal("abc", Truncate("abc", 4)), - massert.Equal("abc", Truncate("abc", 3)), - massert.Equal("ab...", Truncate("abc", 2)), - ) + massert.Equal(t, "abc", Truncate("abc", 4)) + massert.Equal(t, "abc", Truncate("abc", 3)) + massert.Equal(t, "ab...", Truncate("abc", 2)) } -func TestLogger(t *T) { +func TestJSONLogger(t *T) { buf := new(bytes.Buffer) now := time.Now().UTC() td, ts := now.Format(msgTimeFormat), fmt.Sprint(now.UnixNano()) l := NewLogger(&LoggerOpts{ - MessageHandler: NewMessageHandler(buf), + MessageHandler: NewJSONMessageHandler(buf), Now: func() time.Time { return now }, }) - assertOut := func(expected string) massert.Assertion { + assertOut := func(t *testing.T, expected string) { expected = strings.ReplaceAll(expected, "", td) expected = strings.ReplaceAll(expected, "", ts) out, err := buf.ReadString('\n') - return massert.All( - massert.Nil(err), - massert.Equal(expected, strings.TrimSpace(out)), - ) + massert.Equal(t, nil, err) + massert.Equal(t, expected, strings.TrimSpace(out)) } ctx := context.Background() @@ -48,23 +45,17 @@ func TestLogger(t *T) { l.Info(ctx, "bar") l.Warn(ctx, "baz", errors.New("ERR")) l.Error(ctx, "buz", errors.New("ERR")) - massert.Require(t, - assertOut(`{"td":"","ts":,"level":"INFO","descr":"bar","level_int":30}`), - assertOut(`{"td":"","ts":,"level":"WARN","descr":"baz","level_int":20,"annotations":{"errMsg":"ERR"}}`), - assertOut(`{"td":"","ts":,"level":"ERROR","descr":"buz","level_int":10,"annotations":{"errMsg":"ERR"}}`), - ) + assertOut(t, `{"td":"","ts":,"level":"INFO","descr":"bar","level_int":30}`) + assertOut(t, `{"td":"","ts":,"level":"WARN","descr":"baz","level_int":20,"annotations":{"errMsg":"ERR"}}`) + assertOut(t, `{"td":"","ts":,"level":"ERROR","descr":"buz","level_int":10,"annotations":{"errMsg":"ERR"}}`) // annotate context ctx = mctx.Annotate(ctx, "foo", "bar") l.Info(ctx, "bar") - massert.Require(t, - assertOut(`{"td":"","ts":,"level":"INFO","descr":"bar","level_int":30,"annotations":{"foo":"bar"}}`), - ) + assertOut(t, `{"td":"","ts":,"level":"INFO","descr":"bar","level_int":30,"annotations":{"foo":"bar"}}`) // add namespace l = l.WithNamespace("ns") l.Info(ctx, "bar") - massert.Require(t, - assertOut(`{"td":"","ts":,"level":"INFO","ns":["ns"],"descr":"bar","level_int":30,"annotations":{"foo":"bar"}}`), - ) + assertOut(t, `{"td":"","ts":,"level":"INFO","ns":["ns"],"descr":"bar","level_int":30,"annotations":{"foo":"bar"}}`) } diff --git a/mtest/massert/massert.go b/mtest/massert/massert.go deleted file mode 100644 index 9d4ee74..0000000 --- a/mtest/massert/massert.go +++ /dev/null @@ -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{}) diff --git a/mtest/massert/massert_test.go b/mtest/massert/massert_test.go deleted file mode 100644 index 125417b..0000000 --- a/mtest/massert/massert_test.go +++ /dev/null @@ -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) -}