massert: add All, None, Any, AnyOne, and flesh out tests
This commit is contained in:
parent
1b26e2958a
commit
c297e09446
@ -26,6 +26,23 @@ func fmtBlock(str string) string {
|
|||||||
return "\n\t" + strings.Replace(str, "\n", "\n\t", -1) + "\n"
|
return "\n\t" + strings.Replace(str, "\n", "\n\t", -1) + "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fmtMultiDescr(prefix string, aa ...Assertion) string {
|
||||||
|
if len(aa) == 0 {
|
||||||
|
return prefix + "()"
|
||||||
|
} else if len(aa) == 1 {
|
||||||
|
return prefix + "(" + fmtBlock(aa[0].Description()) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
fmt.Fprintf(buf, "%s(\n", prefix)
|
||||||
|
for _, a := range aa {
|
||||||
|
descrStr := "\t" + strings.Replace(a.Description(), "\n", "\n\t", -1)
|
||||||
|
fmt.Fprintf(buf, "%s,\n", descrStr)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(buf, ")")
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
func fmtStack(frames []runtime.Frame) string {
|
func fmtStack(frames []runtime.Frame) string {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
tw := tabwriter.NewWriter(buf, 0, 4, 2, ' ', 0)
|
tw := tabwriter.NewWriter(buf, 0, 4, 2, ' ', 0)
|
||||||
@ -41,6 +58,7 @@ func fmtStack(frames []runtime.Frame) string {
|
|||||||
|
|
||||||
func (ae AssertErr) Error() string {
|
func (ae AssertErr) Error() string {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
fmt.Fprintf(buf, "\n")
|
||||||
fmt.Fprintf(buf, "Assertion: %s\n", fmtBlock(ae.Assertion.Description()))
|
fmt.Fprintf(buf, "Assertion: %s\n", fmtBlock(ae.Assertion.Description()))
|
||||||
fmt.Fprintf(buf, "Error: %s\n", fmtBlock(ae.Err.Error()))
|
fmt.Fprintf(buf, "Error: %s\n", fmtBlock(ae.Err.Error()))
|
||||||
fmt.Fprintf(buf, "Stack: %s\n", fmtBlock(fmtStack(ae.Assertion.Stack())))
|
fmt.Fprintf(buf, "Stack: %s\n", fmtBlock(fmtStack(ae.Assertion.Stack())))
|
||||||
@ -93,6 +111,8 @@ func newAssertion(assertFn func() error, descr string, skip int) Assertion {
|
|||||||
err := assertFn()
|
err := assertFn()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
|
} else if ae, ok := err.(AssertErr); ok {
|
||||||
|
return ae
|
||||||
}
|
}
|
||||||
return AssertErr{
|
return AssertErr{
|
||||||
Err: err,
|
Err: err,
|
||||||
@ -114,31 +134,6 @@ func (a *assertion) Stack() []runtime.Frame {
|
|||||||
return a.stack
|
return a.stack
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assertions represents a set of Assertions which can be tested all at once.
|
|
||||||
type Assertions []Assertion
|
|
||||||
|
|
||||||
// New returns an empty set of Assertions which can be Add'd to.
|
|
||||||
func New() Assertions {
|
|
||||||
return make(Assertions, 0, 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds the given Assertion to the set.
|
|
||||||
func (aa *Assertions) Add(a Assertion) {
|
|
||||||
(*aa) = append(*aa, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert performs the Assert method of each of the set's Assertions
|
|
||||||
// sequentially, stopping at the first error and generating a new one which
|
|
||||||
// includes the Assertion's string and stack information.
|
|
||||||
func (aa Assertions) Assert() error {
|
|
||||||
for _, a := range aa {
|
|
||||||
if err := a.Assert(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Assertion wrappers
|
// Assertion wrappers
|
||||||
|
|
||||||
@ -168,7 +163,7 @@ func (dw descrWrap) Description() string {
|
|||||||
return dw.descr
|
return dw.descr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comment prepends a formatted string to the given Assertions string
|
// Comment prepends a formatted string to the given Assertion's string
|
||||||
// description.
|
// description.
|
||||||
func Comment(a Assertion, msg string, args ...interface{}) Assertion {
|
func Comment(a Assertion, msg string, args ...interface{}) Assertion {
|
||||||
msg = strings.TrimSpace(msg)
|
msg = strings.TrimSpace(msg)
|
||||||
@ -177,28 +172,86 @@ func Comment(a Assertion, msg string, args ...interface{}) Assertion {
|
|||||||
return wrap{descrWrap{Assertion: a, descr: descr}}
|
return wrap{descrWrap{Assertion: a, descr: descr}}
|
||||||
}
|
}
|
||||||
|
|
||||||
type not struct {
|
// Not negates an Assertion, so that it fails if the given Assertion does not,
|
||||||
Assertion
|
// 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n not) Assert() error {
|
// Any asserts that at least one of the given Assertions succeeds.
|
||||||
if err := n.Assertion.Assert(); err == nil {
|
func Any(aa ...Assertion) Assertion {
|
||||||
return AssertErr{
|
fn := func() error {
|
||||||
Err: errors.New("assertion should have failed"),
|
for _, a := range aa {
|
||||||
Assertion: n,
|
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 nil
|
||||||
}
|
}
|
||||||
|
return newAssertion(fn, fmtMultiDescr("All", aa...), 0)
|
||||||
func (n not) Description() string {
|
|
||||||
return "not(" + fmtBlock(n.Assertion.Description()) + ")"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not negates an Assertion, so that it fails if the given Assertion does not,
|
// None asserts that all of the given Assertions fail.
|
||||||
// and vice-versa.
|
//
|
||||||
func Not(a Assertion) Assertion {
|
// NOTE this is functionally equivalent to doing `Not(Any(aa...))`, but the
|
||||||
return not{Assertion: a}
|
// 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1,24 +1,135 @@
|
|||||||
package massert
|
package massert
|
||||||
|
|
||||||
import . "testing"
|
import (
|
||||||
|
"errors"
|
||||||
|
. "testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestAssertions(t *T) {
|
func succeed() Assertion {
|
||||||
a := Equal(1, 1)
|
return newAssertion(func() error { return nil }, "Succeed", 0)
|
||||||
b := Equal(2, 2)
|
|
||||||
if err := (Assertions{a, b}).Assert(); err != nil {
|
|
||||||
t.Fatalf("first Assertions shouldn't return error, returned: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c := Comment(Equal(3, 3), "this part would succeed")
|
func fail() Assertion {
|
||||||
c = Comment(Not(c), "but it's being wrapped in a not, so it then won't")
|
return newAssertion(func() error { return errors.New("failure") }, "Fail", 0)
|
||||||
|
}
|
||||||
|
|
||||||
aa := New()
|
func TestNot(t *T) {
|
||||||
aa.Add(a)
|
if err := Not(succeed()).Assert(); err == nil {
|
||||||
aa.Add(b)
|
t.Fatal("Not(succeed()) should have failed")
|
||||||
aa.Add(c)
|
}
|
||||||
err := aa.Assert()
|
|
||||||
if err == nil {
|
if err := Not(fail()).Assert(); err != nil {
|
||||||
t.Fatalf("second Assertions should have returned an error, returned 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO pointers, structs, slices, maps, nils
|
||||||
|
func TestEqual(t *T) {
|
||||||
|
if err := All(
|
||||||
|
Equal(1, 1),
|
||||||
|
Equal(1, int64(1)),
|
||||||
|
Equal(1, uint64(1)),
|
||||||
|
Equal("foo", "foo"),
|
||||||
|
).Assert(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := None(
|
||||||
|
Equal(1, 2),
|
||||||
|
Equal(1, int64(2)),
|
||||||
|
Equal(1, uint64(2)),
|
||||||
|
Equal("foo", "bar"),
|
||||||
|
).Assert(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExactly(t *T) {
|
||||||
|
if err := All(
|
||||||
|
Exactly(1, 1),
|
||||||
|
Exactly("foo", "foo"),
|
||||||
|
).Assert(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := None(
|
||||||
|
Exactly(1, 2),
|
||||||
|
Exactly(1, int64(1)),
|
||||||
|
Exactly(1, uint64(1)),
|
||||||
|
Exactly("foo", "bar"),
|
||||||
|
).Assert(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Logf("got expected second Assertions error:\n%s", err)
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user