Begin work on "is"

First steps toward much simpler testify. See #84
is
Tyler Bunnell 2014-11-28 11:54:40 -07:00
parent de7fcff264
commit 9b60631791
4 changed files with 195 additions and 0 deletions

43
is/decorate.go Normal file
View File

@ -0,0 +1,43 @@
package is
import (
"bytes"
"fmt"
"runtime"
"strings"
)
// decorate prefixes the string with the file and line of the call site
// and inserts the final newline if needed and indentation tabs for formatting.
// this function was copied from the testing framework.
func decorate(s string) string {
_, file, line, ok := runtime.Caller(3) // decorate + log + public function.
if ok {
// Truncate file name at last file name separator.
if index := strings.LastIndex(file, "/"); index >= 0 {
file = file[index+1:]
} else if index = strings.LastIndex(file, "\\"); index >= 0 {
file = file[index+1:]
}
} else {
file = "???"
line = 1
}
buf := new(bytes.Buffer)
// Every line is indented at least one tab.
buf.WriteByte('\t')
fmt.Fprintf(buf, "%s:%d: ", file, line)
lines := strings.Split(s, "\n")
if l := len(lines); l > 1 && lines[l-1] == "" {
lines = lines[:l-1]
}
for i, line := range lines {
if i > 0 {
// Second and subsequent lines are indented an extra tab.
buf.WriteString("\n\t\t")
}
buf.WriteString(line)
}
buf.WriteByte('\n')
return buf.String()
}

71
is/is.go Normal file
View File

@ -0,0 +1,71 @@
package is
import (
"fmt"
"reflect"
"testing"
)
// TB is an interface that defines an assertion-style
// testing syntax to condense and make more efficient
// the writing of tests. The output of TB is identical
// to the output of the testing framework, allowing any
// scripts, parsers, etc to parse it appropriately.
type TB interface {
// Equal determines if the two arguments are equal.
// If not, a failure is printed.
Equal(actual, expected interface{}) Result
// NotEqual determines if the two arguments are not equal.
// If they are, a failure is printed.
NotEqual(actual, expected interface{}) Result
}
// tb implements TB
type tb struct {
tb testing.TB
}
// ensure tb implements TB
var _ TB = (*tb)(nil)
// New creates a new object that satisfies the TB interface.
func New(testObj testing.TB) TB {
return &tb{tb: testObj}
}
// equal tests for equality of two objects.
func equal(actual, expected interface{}) bool {
return reflect.DeepEqual(actual, expected)
}
// printFailure assembles a string from the arguments
// and decorates it, then prints it.
func printFailure(args ...interface{}) {
fmt.Println(decorate(fmt.Sprintln(args...)))
}
// Equal determines if the two arguments are equal.
// If not, a failure is printed.
func (t *tb) Equal(actual, expected interface{}) Result {
r := &result{tb: t.tb}
if equal(actual, expected) {
r.success = true
} else {
r.success = false
printFailure(actual, "should be equal to", expected)
}
return r
}
// NotEqual determines if the two arguments are not equal.
// If they are, a failure is printed.
func (t *tb) NotEqual(actual, expected interface{}) Result {
r := &result{tb: t.tb}
if !equal(actual, expected) {
r.success = true
} else {
r.success = false
printFailure(actual, "should not be equal to", expected)
}
return r
}

45
is/is_test.go Normal file
View File

@ -0,0 +1,45 @@
package is
import "testing"
func TestIsEqual(t *testing.T) {
mockT := &testing.T{}
is := New(mockT)
if is == nil {
t.Fatal("is should not be nil")
}
r := is.Equal(1, 1)
if r == nil {
t.Fatal("Equal should return a result object")
}
if !r.Success() {
t.Fatal("should be equal")
}
if is.Equal(1, 2).Success() {
t.Fatal("1 is not 2")
}
// is.Equal(1,2).Require() cannot be tested as it
// either fails the test or causes a panic and we cannot
// mock testing.TB as there is a private method to prevent that.
}
func TestIsNotEqual(t *testing.T) {
mockT := &testing.T{}
is := New(mockT)
if is == nil {
t.Fatal("is should not be nil")
}
r := is.NotEqual(1, 2)
if r == nil {
t.Fatal("NotEqual should return a result object")
}
if !r.Success() {
t.Fatal("should not be equal")
}
if is.NotEqual(1, 1).Success() {
t.Fatal("1 is 1")
}
// is.Equal(1,2).Require() cannot be tested as it
// either fails the test or causes a panic and we cannot
// mock testing.TB as there is a private method to prevent that.
}

36
is/result.go Normal file
View File

@ -0,0 +1,36 @@
package is
import "testing"
// Result is an interface that defines methods for
// acting on the result of a test.
type Result interface {
// Success returns the success value of the last test.
Success() bool
// Require causes the testing procedure to abort
// if the test failed.
Require()
}
// result implements Result
type result struct {
success bool
tb testing.TB
}
// ensure result implements Result
var _ Result = (*result)(nil)
// Success returns the success value of the test that
// returned it.
func (r *result) Success() bool {
return r.success
}
// Require causes the testing procedure to abort
// if the test failed.
func (r *result) Require() {
if r.success == false {
r.tb.FailNow()
}
}