// Package tt supports table-driven tests with little boilerplate.//// See the test case for this package for example usage.
package ttimport ()// Table represents a test table.typeTable []*Case// Case represents a test case. It is created by the C function, and offers// setters that augment and return itself; those calls can be chained like// C(...).Rets(...).typeCasestruct {args []interface{}retsMatchers [][]interface{}}// Args returns a new Case with the given arguments.func ( ...interface{}) *Case {return &Case{args: }}// Rets modifies the test case so that it requires the return values to match// the given values. It returns the receiver. The arguments may implement the// Matcher interface, in which case its Match method is called with the actual// return value. Otherwise, reflect.DeepEqual is used to determine matches.func ( *Case) ( ...interface{}) *Case { .retsMatchers = append(.retsMatchers, )return}// FnToTest describes a function to test.typeFnToTeststruct {namestringbodyinterface{}argsFmtstringretsFmtstring}// Fn makes a new FnToTest with the given function name and body.func ( string, interface{}) *FnToTest {return &FnToTest{name: , body: }}// ArgsFmt sets the string for formatting arguments in test error messages, and// return fn itself.func ( *FnToTest) ( string) *FnToTest { .argsFmt = return}// RetsFmt sets the string for formatting return values in test error messages,// and return fn itself.func ( *FnToTest) ( string) *FnToTest { .retsFmt = return}// T is the interface for accessing testing.T.typeTinterface {Helper()Errorf(format string, args ...interface{})}// Test tests a function against test cases.func ( T, *FnToTest, Table) { .Helper()for , := range { := call(.body, .args)for , := range .retsMatchers {if !match(, ) {var , , stringif .argsFmt == "" { = sprintArgs(.args...) } else { = fmt.Sprintf(.argsFmt, .args...) }if .retsFmt == "" { = sprintRets(...) = sprintRets(...) } else { = fmt.Sprintf(.retsFmt, ...) = fmt.Sprintf(.retsFmt, ...) } .Errorf("%s(%s) -> %s, want %s", .name, , , ) } } }}// RetValue is an empty interface used in the Matcher interface.typeRetValueinterface{}// Matcher wraps the Match method.typeMatcherinterface {// Match reports whether a return value is considered a match. The argument // is of type RetValue so that it cannot be implemented accidentally.Match(RetValue) bool}// Any is a Matcher that matches any value.varAnyMatcher = anyMatcher{}typeanyMatcherstruct{}func (anyMatcher) (RetValue) bool { returntrue }func (, []interface{}) bool {for , := range {if !matchOne(, []) {returnfalse } }returntrue}func (, interface{}) bool {if , := .(Matcher); {return .Match() }returnreflect.DeepEqual(, )}func ( ...interface{}) string {returnsprintCommaDelimited(...)}func ( ...interface{}) string {iflen() == 1 {returnfmt.Sprint([0]) }return"(" + sprintCommaDelimited(...) + ")"}func ( ...interface{}) string {varbytes.Bufferfor , := range {if > 0 { .WriteString(", ") }fmt.Fprint(&, ) }return .String()}func ( interface{}, []interface{}) []interface{} { := make([]reflect.Value, len())for , := range {if == nil {// reflect.ValueOf(nil) returns a zero Value, but this is not what // we want. Work around this by taking the ValueOf a pointer to nil // and then get the Elem. // TODO(xiaq): This is now always using a nil value with type // interface{}. For more usability, inspect the type of fn to see // which type of nil this argument should be.varinterface{} [] = reflect.ValueOf(&).Elem() } else { [] = reflect.ValueOf() } } := reflect.ValueOf().Call() := make([]interface{}, len())for , := range { [] = .Interface() }return}
The pages are generated with Goldsv0.2.8-preview. (GOOS=darwin GOARCH=arm64)