Created
July 13, 2011 03:13
-
-
Save zaphar/1079641 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
go-tap is a simple Test Anything Protocol unittest framework for Golang. | |
package main | |
import "tap" | |
type MyTest struct {} | |
func (t *MyTest)Tests() [] []func(*tap.Recorder) { | |
f := func(r *tap.Recorder) { | |
Ok(r, true, "this is a message for %s", true) | |
Diag(r, "this is a diagnostic message\non %s lines", 2) | |
} | |
return []func(*tap.Recorder){f} | |
} | |
func init() { tap.Register(new(MyTest)) } | |
func main() { tap.Main() } | |
If you want to run setup or cleanup code then implement as many of the 4 | |
(Pre|Post)(Suite|Test)Runner interfaces as appropriate. | |
*/ | |
package tap | |
import ( | |
"fmt" | |
"io" | |
"os" | |
"strings" | |
) | |
var suites []Suite | |
// TODO(jwall): a default consumer that examines output and informs as to | |
// overall failure or success | |
// A Tap Stream recorder. It handles outputting the Tap Stream for | |
// your test Suite | |
type Recorder struct { | |
expected int | |
count int | |
out io.Writer | |
diagOut io.Writer | |
} | |
// A Constructor for a Recorder allowing you setup your own consumer | |
// for a tap stream. | |
func NewRecorder(out, diagOut io.Writer) *Recorder { | |
return &Recorder{out: out, diagOut: diagOut} | |
} | |
// The default recorder for easy test setup | |
var DefaultRecorder = NewRecorder(os.Stdout, os.Stdout) | |
func (r *Recorder) reset() { | |
r.expected = 0 | |
r.count = 0 | |
} | |
func (r *Recorder) start() { | |
if r.expected > 0 { | |
fmt.Fprintf(r.out, "1..%d\n", r.expected) | |
} | |
} | |
func (r *Recorder) diag(msg string, args ...interface{}) { | |
for _, m := range strings.Split(fmt.Sprintf(msg, args...), "\n", -1) { | |
fmt.Fprintln(r.diagOut, "# "+m) | |
} | |
} | |
func (r *Recorder) ok(result bool, msg string, args ...interface{}) bool { | |
r.count++ | |
start := fmt.Sprintf("ok %d - ", r.count) | |
if result { | |
fmt.Fprintf(r.out, start+msg+"\n", args...) | |
} else { | |
fmt.Fprintf(r.out, "not "+start+msg+"\n", args...) | |
} | |
return result | |
} | |
// Primitive Diag function for printing diagnostic messages | |
func Diag(r *Recorder, msg string, args ...interface{}) { | |
r.diag(msg, args...) | |
} | |
// Primitive Assertion test for boolean true with informative message | |
func Ok(r *Recorder, result bool, msg string, args ...interface{}) bool { | |
return r.ok(result, msg, args...) | |
} | |
// Base interface for a Test Suite | |
type Suite interface { | |
// Should return the TAP plan for the test suite | |
Plan() int | |
// return the test functions | |
Tests() []func(t *Recorder) | |
} | |
// Interface for a Suite with Pre Test setup code | |
type PreTestRunner interface { | |
Suite | |
// Run once for each test in suite at the beginning | |
PreTest(r *Recorder) | |
} | |
// Interface for a Suite with Post Test cleanup code | |
type PostTestRunner interface { | |
Suite | |
// Run once for each test in suite at the end | |
PostTest(r *Recorder) | |
} | |
// Interface for a Suite with Pre Suite setup code | |
type PreSuiteRunner interface { | |
Suite | |
// Run once for the suite at the beginning | |
PreSuite(r *Recorder) | |
} | |
// Interface for a Suite with Post Suite cleanup code | |
type PostSuiteRunner interface { | |
Suite | |
// Run once for the suite at the end | |
PostSuite(r *Recorder) | |
} | |
// Register a Suite with the default test harness | |
func Register(t... Suite) { | |
suites = append(suites, t...) | |
} | |
// Run a Suite with a *Recorder | |
func RunSuite(recorder *Recorder, t Suite) { | |
recorder.reset() | |
recorder.expected = t.Plan() | |
recorder.start() | |
// if we a PreSuite run it. | |
if pre, ok := t.(PreSuiteRunner); ok { | |
pre.PreSuite(recorder) | |
} | |
for _, tst := range t.Tests() { | |
if pre, ok := t.(PreTestRunner); ok { | |
pre.PreTest(recorder) | |
} | |
tst(recorder) | |
if post, ok := t.(PostTestRunner); ok { | |
post.PostTest(recorder) | |
} | |
} | |
if post, ok := t.(PostSuiteRunner); ok { | |
post.PostSuite(recorder) | |
} | |
} | |
// Run a List of Suites with a *Recorder | |
func RunSuites(r *Recorder, suites... Suite) { | |
for _, suite := range suites { | |
RunSuite(r, suite) | |
} | |
} | |
// Main entry point for the test harness with default *Recorder | |
func Main() { | |
WithRecorder(DefaultRecorder) | |
} | |
// Entry point for test harness for a custom *Recorder | |
func WithRecorder(r *Recorder) { | |
RunSuites(r, suites...) | |
} | |
// Copyright (C) 2011 Jeremy Wall ([email protected]) | |
// Contents Available under the terms of the Artistic License 2.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
. "tap" | |
"os" | |
"strings" | |
) | |
type TapSelfTests struct{} // implements just TapSuite | |
func (s *TapSelfTests) Plan() int { return 2 } | |
func (s *TapSelfTests) Tests() []func(*Recorder) { | |
f := func(r *Recorder) { | |
Ok(r, true, "this is %t", true) | |
Diag(r, "a diagnostic %s", "message") | |
Ok(r, false, "this is %t", false) | |
Diag(r, "a multiline diagnostic %s\nYay!!", "message") | |
} | |
return []func(*Recorder){f} | |
} | |
type TapSelfTestPreSuites struct { // implements TapPostSuiteRunner | |
TapSelfTests | |
} | |
func (t *TapSelfTestPreSuites) PreSuite(r *Recorder) { | |
Diag(r, "Ran PreSuite") | |
} | |
type TapSelfTestPostSuites struct { // implements TapPreSuiteRunner | |
TapSelfTests | |
} | |
func (t *TapSelfTestPostSuites) PostSuite(r *Recorder) { | |
Diag(r, "Ran PostSuite") | |
} | |
type TapSelfTestPreTests struct { // implements TapPostTestRunner | |
TapSelfTests | |
} | |
func (t *TapSelfTestPreTests) PreTest(r *Recorder) { | |
Diag(r, "Ran PreTest") | |
} | |
type TapSelfTestPostTests struct { // implements TapPreTestRunner | |
TapSelfTests | |
} | |
func (t *TapSelfTestPostTests) PostTest(r *Recorder) { | |
Diag(r, "Ran PostTest") | |
} | |
var SelfTests = new(TapSelfTests) | |
var PreSuiteTest = new(TapSelfTestPreSuites) | |
var PostSuiteTest = new(TapSelfTestPostSuites) | |
var PreTestTest = new(TapSelfTestPreTests) | |
var PostTestTest = new(TapSelfTestPostTests) | |
type TapTestStream struct {} | |
func (s *TapTestStream) Plan() int { return 14 } | |
type StringWriter struct { | |
contents []byte | |
} | |
func (s *StringWriter) Write(bs []byte) (n int, err os.Error) { | |
s.contents = append(s.contents, bs...) | |
return len(bs), nil | |
} | |
func (s *StringWriter) String() string { | |
return string(s.contents) | |
} | |
func (s *TapTestStream) Tests() []func(*Recorder) { | |
f := func(r *Recorder) { | |
out := new(StringWriter) | |
testRecorder := NewRecorder(out, out) | |
RunSuite(testRecorder, SelfTests) | |
prefix := "1..2\n" | |
output := out.String() | |
Diag(r, "Output:") | |
Diag(r, output) | |
Ok(r, strings.HasPrefix(output, prefix), | |
"output has prefix: %s", prefix[:4]) | |
list := strings.Split(output, "\n", -1) | |
Ok(r, list[1] == "ok 1 - this is true", | |
"Second line is a diagnostic message") | |
Ok(r, list[2] == "# a diagnostic message", | |
"Third line is a diagnostic message") | |
Ok(r, list[3] == "not ok 2 - this is false", | |
"fourth line failed test") | |
Ok(r, list[5] == "# Yay!!", | |
"Last line is last part of multiline diagnostic") | |
lines := len(list) | |
Ok(r, lines == 7, "output was %d lines", lines) | |
} | |
f2 := func(r *Recorder) { | |
out := new(StringWriter) | |
testRecorder := NewRecorder(out, out) | |
RunSuite(testRecorder, PreSuiteTest) | |
output := out.String() | |
Diag(r, "Output:") | |
Diag(r, output) | |
list := strings.Split(output, "\n", -1) | |
lines := len(list) | |
Ok(r, list[1] == "# Ran PreSuite", | |
"second line was %s", list[1]) | |
Ok(r, lines == 8, "output was %d lines", lines) | |
} | |
f3 := func(r *Recorder) { | |
out := new(StringWriter) | |
testRecorder := NewRecorder(out, out) | |
RunSuite(testRecorder, PostSuiteTest) | |
output := out.String() | |
Diag(r, "Output:") | |
Diag(r, output) | |
list := strings.Split(output, "\n", -1) | |
lines := len(list) | |
Ok(r, list[6] == "# Ran PostSuite", | |
"second line was %s", list[6]) | |
Ok(r, lines == 8, "output was %d lines", lines) | |
} | |
f4 := func(r *Recorder) { | |
out := new(StringWriter) | |
testRecorder := NewRecorder(out, out) | |
RunSuite(testRecorder, PreTestTest) | |
output := out.String() | |
Diag(r, "Output:") | |
Diag(r, output) | |
list := strings.Split(output, "\n", -1) | |
lines := len(list) | |
Ok(r, list[1] == "# Ran PreTest", | |
"second line was %s", list[1]) | |
Ok(r, lines == 8, "output was %d lines", lines) | |
} | |
f5 := func(r *Recorder) { | |
out := new(StringWriter) | |
testRecorder := NewRecorder(out, out) | |
RunSuite(testRecorder, PostTestTest) | |
output := out.String() | |
Diag(r, "Output:") | |
Diag(r, output) | |
list := strings.Split(output, "\n", -1) | |
lines := len(list) | |
Ok(r, list[6] == "# Ran PostTest", | |
"second line was %s", list[6]) | |
Ok(r, lines == 8, "output was %d lines", lines) | |
} | |
return []func(*Recorder){f, f2, f3, f4, f5} | |
} | |
func init() { | |
Register(new(TapTestStream)) | |
} | |
// TODO(jwall): actually test the output. | |
func main() { | |
Main() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment