diff options
Diffstat (limited to 'libgo/go/testing')
-rw-r--r-- | libgo/go/testing/benchmark.go | 195 | ||||
-rw-r--r-- | libgo/go/testing/iotest/logger.go | 55 | ||||
-rw-r--r-- | libgo/go/testing/iotest/reader.go | 69 | ||||
-rw-r--r-- | libgo/go/testing/iotest/writer.go | 38 | ||||
-rw-r--r-- | libgo/go/testing/quick/quick.go | 364 | ||||
-rw-r--r-- | libgo/go/testing/quick/quick_test.go | 147 | ||||
-rw-r--r-- | libgo/go/testing/script/script.go | 359 | ||||
-rw-r--r-- | libgo/go/testing/script/script_test.go | 75 | ||||
-rw-r--r-- | libgo/go/testing/testing.go | 174 |
9 files changed, 1476 insertions, 0 deletions
diff --git a/libgo/go/testing/benchmark.go b/libgo/go/testing/benchmark.go new file mode 100644 index 000000000..ad938027d --- /dev/null +++ b/libgo/go/testing/benchmark.go @@ -0,0 +1,195 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package testing + +import ( + "flag" + "fmt" + "os" + "time" +) + +var matchBenchmarks = flag.String("benchmarks", "", "regular expression to select benchmarks to run") + +// An internal type but exported because it is cross-package; part of the implementation +// of gotest. +type InternalBenchmark struct { + Name string + F func(b *B) +} + +// B is a type passed to Benchmark functions to manage benchmark +// timing and to specify the number of iterations to run. +type B struct { + N int + benchmark InternalBenchmark + ns int64 + bytes int64 + start int64 +} + +// StartTimer starts timing a test. This function is called automatically +// before a benchmark starts, but it can also used to resume timing after +// a call to StopTimer. +func (b *B) StartTimer() { b.start = time.Nanoseconds() } + +// StopTimer stops timing a test. This can be used to pause the timer +// while performing complex initialization that you don't +// want to measure. +func (b *B) StopTimer() { + if b.start > 0 { + b.ns += time.Nanoseconds() - b.start + } + b.start = 0 +} + +// ResetTimer stops the timer and sets the elapsed benchmark time to zero. +func (b *B) ResetTimer() { + b.start = 0 + b.ns = 0 +} + +// SetBytes records the number of bytes processed in a single operation. +// If this is called, the benchmark will report ns/op and MB/s. +func (b *B) SetBytes(n int64) { b.bytes = n } + +func (b *B) nsPerOp() int64 { + if b.N <= 0 { + return 0 + } + return b.ns / int64(b.N) +} + +// runN runs a single benchmark for the specified number of iterations. +func (b *B) runN(n int) { + b.N = n + b.ResetTimer() + b.StartTimer() + b.benchmark.F(b) + b.StopTimer() +} + +func min(x, y int) int { + if x > y { + return y + } + return x +} + +func max(x, y int) int { + if x < y { + return y + } + return x +} + +// roundDown10 rounds a number down to the nearest power of 10. +func roundDown10(n int) int { + var tens = 0 + // tens = floor(log_10(n)) + for n > 10 { + n = n / 10 + tens++ + } + // result = 10^tens + result := 1 + for i := 0; i < tens; i++ { + result *= 10 + } + return result +} + +// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX]. +func roundUp(n int) int { + base := roundDown10(n) + if n < (2 * base) { + return 2 * base + } + if n < (5 * base) { + return 5 * base + } + return 10 * base +} + +// run times the benchmark function. It gradually increases the number +// of benchmark iterations until the benchmark runs for a second in order +// to get a reasonable measurement. It prints timing information in this form +// testing.BenchmarkHello 100000 19 ns/op +func (b *B) run() BenchmarkResult { + // Run the benchmark for a single iteration in case it's expensive. + n := 1 + b.runN(n) + // Run the benchmark for at least a second. + for b.ns < 1e9 && n < 1e9 { + last := n + // Predict iterations/sec. + if b.nsPerOp() == 0 { + n = 1e9 + } else { + n = 1e9 / int(b.nsPerOp()) + } + // Run more iterations than we think we'll need for a second (1.5x). + // Don't grow too fast in case we had timing errors previously. + // Be sure to run at least one more than last time. + n = max(min(n+n/2, 100*last), last+1) + // Round up to something easy to read. + n = roundUp(n) + b.runN(n) + } + return BenchmarkResult{b.N, b.ns, b.bytes} + +} + +// The results of a benchmark run. +type BenchmarkResult struct { + N int // The number of iterations. + Ns int64 // The total time taken. + Bytes int64 // The total number of bytes processed. +} + +func (r BenchmarkResult) NsPerOp() int64 { + if r.N <= 0 { + return 0 + } + return r.Ns / int64(r.N) +} + +func (r BenchmarkResult) String() string { + ns := r.NsPerOp() + mb := "" + if ns > 0 && r.Bytes > 0 { + mb = fmt.Sprintf("\t%7.2f MB/s", (float64(r.Bytes)/1e6)/(float64(ns)/1e9)) + } + return fmt.Sprintf("%8d\t%10d ns/op%s", r.N, ns, mb) +} + +// An internal function but exported because it is cross-package; part of the implementation +// of gotest. +func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmarks []InternalBenchmark) { + // If no flag was specified, don't run benchmarks. + if len(*matchBenchmarks) == 0 { + return + } + for _, Benchmark := range benchmarks { + matched, err := matchString(*matchBenchmarks, Benchmark.Name) + if err != nil { + println("invalid regexp for -benchmarks:", err.String()) + os.Exit(1) + } + if !matched { + continue + } + b := &B{benchmark: Benchmark} + r := b.run() + fmt.Printf("%s\t%v\n", Benchmark.Name, r) + } +} + +// Benchmark benchmarks a single function. Useful for creating +// custom benchmarks that do not use gotest. +func Benchmark(f func(b *B)) BenchmarkResult { + b := &B{benchmark: InternalBenchmark{"", f}} + return b.run() +} diff --git a/libgo/go/testing/iotest/logger.go b/libgo/go/testing/iotest/logger.go new file mode 100644 index 000000000..c3bf5df3c --- /dev/null +++ b/libgo/go/testing/iotest/logger.go @@ -0,0 +1,55 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package iotest + +import ( + "io" + "log" + "os" +) + +type writeLogger struct { + prefix string + w io.Writer +} + +func (l *writeLogger) Write(p []byte) (n int, err os.Error) { + n, err = l.w.Write(p) + if err != nil { + log.Printf("%s %x: %v", l.prefix, p[0:n], err) + } else { + log.Printf("%s %x", l.prefix, p[0:n]) + } + return +} + +// NewWriteLogger returns a writer that behaves like w except +// that it logs (using log.Printf) each write to standard error, +// printing the prefix and the hexadecimal data written. +func NewWriteLogger(prefix string, w io.Writer) io.Writer { + return &writeLogger{prefix, w} +} + +type readLogger struct { + prefix string + r io.Reader +} + +func (l *readLogger) Read(p []byte) (n int, err os.Error) { + n, err = l.r.Read(p) + if err != nil { + log.Printf("%s %x: %v", l.prefix, p[0:n], err) + } else { + log.Printf("%s %x", l.prefix, p[0:n]) + } + return +} + +// NewReadLogger returns a reader that behaves like r except +// that it logs (using log.Print) each read to standard error, +// printing the prefix and the hexadecimal data written. +func NewReadLogger(prefix string, r io.Reader) io.Reader { + return &readLogger{prefix, r} +} diff --git a/libgo/go/testing/iotest/reader.go b/libgo/go/testing/iotest/reader.go new file mode 100644 index 000000000..647520a09 --- /dev/null +++ b/libgo/go/testing/iotest/reader.go @@ -0,0 +1,69 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The iotest package implements Readers and Writers +// useful only for testing. +package iotest + +import ( + "io" + "os" +) + +// OneByteReader returns a Reader that implements +// each non-empty Read by reading one byte from r. +func OneByteReader(r io.Reader) io.Reader { return &oneByteReader{r} } + +type oneByteReader struct { + r io.Reader +} + +func (r *oneByteReader) Read(p []byte) (int, os.Error) { + if len(p) == 0 { + return 0, nil + } + return r.r.Read(p[0:1]) +} + +// HalfReader returns a Reader that implements Read +// by reading half as many requested bytes from r. +func HalfReader(r io.Reader) io.Reader { return &halfReader{r} } + +type halfReader struct { + r io.Reader +} + +func (r *halfReader) Read(p []byte) (int, os.Error) { + return r.r.Read(p[0 : (len(p)+1)/2]) +} + + +// DataErrReader returns a Reader that returns the final +// error with the last data read, instead of by itself with +// zero bytes of data. +func DataErrReader(r io.Reader) io.Reader { return &dataErrReader{r, nil, make([]byte, 1024)} } + +type dataErrReader struct { + r io.Reader + unread []byte + data []byte +} + +func (r *dataErrReader) Read(p []byte) (n int, err os.Error) { + // loop because first call needs two reads: + // one to get data and a second to look for an error. + for { + if len(r.unread) == 0 { + n1, err1 := r.r.Read(r.data) + r.unread = r.data[0:n1] + err = err1 + } + if n > 0 { + break + } + n = copy(p, r.unread) + r.unread = r.unread[n:] + } + return +} diff --git a/libgo/go/testing/iotest/writer.go b/libgo/go/testing/iotest/writer.go new file mode 100644 index 000000000..71f504ce2 --- /dev/null +++ b/libgo/go/testing/iotest/writer.go @@ -0,0 +1,38 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package iotest + +import ( + "io" + "os" +) + +// TruncateWriter returns a Writer that writes to w +// but stops silently after n bytes. +func TruncateWriter(w io.Writer, n int64) io.Writer { + return &truncateWriter{w, n} +} + +type truncateWriter struct { + w io.Writer + n int64 +} + +func (t *truncateWriter) Write(p []byte) (n int, err os.Error) { + if t.n <= 0 { + return len(p), nil + } + // real write + n = len(p) + if int64(n) > t.n { + n = int(t.n) + } + n, err = t.w.Write(p[0:n]) + t.n -= int64(n) + if err == nil { + n = len(p) + } + return +} diff --git a/libgo/go/testing/quick/quick.go b/libgo/go/testing/quick/quick.go new file mode 100644 index 000000000..a5568b048 --- /dev/null +++ b/libgo/go/testing/quick/quick.go @@ -0,0 +1,364 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This package implements utility functions to help with black box testing. +package quick + +import ( + "flag" + "fmt" + "math" + "os" + "rand" + "reflect" + "strings" +) + +var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check") + +// A Generator can generate random values of its own type. +type Generator interface { + // Generate returns a random instance of the type on which it is a + // method using the size as a size hint. + Generate(rand *rand.Rand, size int) reflect.Value +} + +// randFloat32 generates a random float taking the full range of a float32. +func randFloat32(rand *rand.Rand) float32 { + f := rand.Float64() * math.MaxFloat32 + if rand.Int()&1 == 1 { + f = -f + } + return float32(f) +} + +// randFloat64 generates a random float taking the full range of a float64. +func randFloat64(rand *rand.Rand) float64 { + f := rand.Float64() + if rand.Int()&1 == 1 { + f = -f + } + return f +} + +// randInt64 returns a random integer taking half the range of an int64. +func randInt64(rand *rand.Rand) int64 { return rand.Int63() - 1<<62 } + +// complexSize is the maximum length of arbitrary values that contain other +// values. +const complexSize = 50 + +// Value returns an arbitrary value of the given type. +// If the type implements the Generator interface, that will be used. +// Note: in order to create arbitrary values for structs, all the members must be public. +func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) { + if m, ok := reflect.MakeZero(t).Interface().(Generator); ok { + return m.Generate(rand, complexSize), true + } + + switch concrete := t.(type) { + case *reflect.BoolType: + return reflect.NewValue(rand.Int()&1 == 0), true + case *reflect.FloatType, *reflect.IntType, *reflect.UintType, *reflect.ComplexType: + switch t.Kind() { + case reflect.Float32: + return reflect.NewValue(randFloat32(rand)), true + case reflect.Float64: + return reflect.NewValue(randFloat64(rand)), true + case reflect.Complex64: + return reflect.NewValue(complex(randFloat32(rand), randFloat32(rand))), true + case reflect.Complex128: + return reflect.NewValue(complex(randFloat64(rand), randFloat64(rand))), true + case reflect.Int16: + return reflect.NewValue(int16(randInt64(rand))), true + case reflect.Int32: + return reflect.NewValue(int32(randInt64(rand))), true + case reflect.Int64: + return reflect.NewValue(randInt64(rand)), true + case reflect.Int8: + return reflect.NewValue(int8(randInt64(rand))), true + case reflect.Int: + return reflect.NewValue(int(randInt64(rand))), true + case reflect.Uint16: + return reflect.NewValue(uint16(randInt64(rand))), true + case reflect.Uint32: + return reflect.NewValue(uint32(randInt64(rand))), true + case reflect.Uint64: + return reflect.NewValue(uint64(randInt64(rand))), true + case reflect.Uint8: + return reflect.NewValue(uint8(randInt64(rand))), true + case reflect.Uint: + return reflect.NewValue(uint(randInt64(rand))), true + case reflect.Uintptr: + return reflect.NewValue(uintptr(randInt64(rand))), true + } + case *reflect.MapType: + numElems := rand.Intn(complexSize) + m := reflect.MakeMap(concrete) + for i := 0; i < numElems; i++ { + key, ok1 := Value(concrete.Key(), rand) + value, ok2 := Value(concrete.Elem(), rand) + if !ok1 || !ok2 { + return nil, false + } + m.SetElem(key, value) + } + return m, true + case *reflect.PtrType: + v, ok := Value(concrete.Elem(), rand) + if !ok { + return nil, false + } + p := reflect.MakeZero(concrete) + p.(*reflect.PtrValue).PointTo(v) + return p, true + case *reflect.SliceType: + numElems := rand.Intn(complexSize) + s := reflect.MakeSlice(concrete, numElems, numElems) + for i := 0; i < numElems; i++ { + v, ok := Value(concrete.Elem(), rand) + if !ok { + return nil, false + } + s.Elem(i).SetValue(v) + } + return s, true + case *reflect.StringType: + numChars := rand.Intn(complexSize) + codePoints := make([]int, numChars) + for i := 0; i < numChars; i++ { + codePoints[i] = rand.Intn(0x10ffff) + } + return reflect.NewValue(string(codePoints)), true + case *reflect.StructType: + s := reflect.MakeZero(t).(*reflect.StructValue) + for i := 0; i < s.NumField(); i++ { + v, ok := Value(concrete.Field(i).Type, rand) + if !ok { + return nil, false + } + s.Field(i).SetValue(v) + } + return s, true + default: + return nil, false + } + + return +} + +// A Config structure contains options for running a test. +type Config struct { + // MaxCount sets the maximum number of iterations. If zero, + // MaxCountScale is used. + MaxCount int + // MaxCountScale is a non-negative scale factor applied to the default + // maximum. If zero, the default is unchanged. + MaxCountScale float64 + // If non-nil, rand is a source of random numbers. Otherwise a default + // pseudo-random source will be used. + Rand *rand.Rand + // If non-nil, Values is a function which generates a slice of arbitrary + // Values that are congruent with the arguments to the function being + // tested. Otherwise, Values is used to generate the values. + Values func([]reflect.Value, *rand.Rand) +} + +var defaultConfig Config + +// getRand returns the *rand.Rand to use for a given Config. +func (c *Config) getRand() *rand.Rand { + if c.Rand == nil { + return rand.New(rand.NewSource(0)) + } + return c.Rand +} + +// getMaxCount returns the maximum number of iterations to run for a given +// Config. +func (c *Config) getMaxCount() (maxCount int) { + maxCount = c.MaxCount + if maxCount == 0 { + if c.MaxCountScale != 0 { + maxCount = int(c.MaxCountScale * float64(*defaultMaxCount)) + } else { + maxCount = *defaultMaxCount + } + } + + return +} + +// A SetupError is the result of an error in the way that check is being +// used, independent of the functions being tested. +type SetupError string + +func (s SetupError) String() string { return string(s) } + +// A CheckError is the result of Check finding an error. +type CheckError struct { + Count int + In []interface{} +} + +func (s *CheckError) String() string { + return fmt.Sprintf("#%d: failed on input %s", s.Count, toString(s.In)) +} + +// A CheckEqualError is the result CheckEqual finding an error. +type CheckEqualError struct { + CheckError + Out1 []interface{} + Out2 []interface{} +} + +func (s *CheckEqualError) String() string { + return fmt.Sprintf("#%d: failed on input %s. Output 1: %s. Output 2: %s", s.Count, toString(s.In), toString(s.Out1), toString(s.Out2)) +} + +// Check looks for an input to f, any function that returns bool, +// such that f returns false. It calls f repeatedly, with arbitrary +// values for each argument. If f returns false on a given input, +// Check returns that input as a *CheckError. +// For example: +// +// func TestOddMultipleOfThree(t *testing.T) { +// f := func(x int) bool { +// y := OddMultipleOfThree(x) +// return y%2 == 1 && y%3 == 0 +// } +// if err := quick.Check(f, nil); err != nil { +// t.Error(err) +// } +// } +func Check(function interface{}, config *Config) (err os.Error) { + if config == nil { + config = &defaultConfig + } + + f, fType, ok := functionAndType(function) + if !ok { + err = SetupError("argument is not a function") + return + } + + if fType.NumOut() != 1 { + err = SetupError("function returns more than one value.") + return + } + if _, ok := fType.Out(0).(*reflect.BoolType); !ok { + err = SetupError("function does not return a bool") + return + } + + arguments := make([]reflect.Value, fType.NumIn()) + rand := config.getRand() + maxCount := config.getMaxCount() + + for i := 0; i < maxCount; i++ { + err = arbitraryValues(arguments, fType, config, rand) + if err != nil { + return + } + + if !f.Call(arguments)[0].(*reflect.BoolValue).Get() { + err = &CheckError{i + 1, toInterfaces(arguments)} + return + } + } + + return +} + +// CheckEqual looks for an input on which f and g return different results. +// It calls f and g repeatedly with arbitrary values for each argument. +// If f and g return different answers, CheckEqual returns a *CheckEqualError +// describing the input and the outputs. +func CheckEqual(f, g interface{}, config *Config) (err os.Error) { + if config == nil { + config = &defaultConfig + } + + x, xType, ok := functionAndType(f) + if !ok { + err = SetupError("f is not a function") + return + } + y, yType, ok := functionAndType(g) + if !ok { + err = SetupError("g is not a function") + return + } + + if xType != yType { + err = SetupError("functions have different types") + return + } + + arguments := make([]reflect.Value, xType.NumIn()) + rand := config.getRand() + maxCount := config.getMaxCount() + + for i := 0; i < maxCount; i++ { + err = arbitraryValues(arguments, xType, config, rand) + if err != nil { + return + } + + xOut := toInterfaces(x.Call(arguments)) + yOut := toInterfaces(y.Call(arguments)) + + if !reflect.DeepEqual(xOut, yOut) { + err = &CheckEqualError{CheckError{i + 1, toInterfaces(arguments)}, xOut, yOut} + return + } + } + + return +} + +// arbitraryValues writes Values to args such that args contains Values +// suitable for calling f. +func arbitraryValues(args []reflect.Value, f *reflect.FuncType, config *Config, rand *rand.Rand) (err os.Error) { + if config.Values != nil { + config.Values(args, rand) + return + } + + for j := 0; j < len(args); j++ { + var ok bool + args[j], ok = Value(f.In(j), rand) + if !ok { + err = SetupError(fmt.Sprintf("cannot create arbitrary value of type %s for argument %d", f.In(j), j)) + return + } + } + + return +} + +func functionAndType(f interface{}) (v *reflect.FuncValue, t *reflect.FuncType, ok bool) { + v, ok = reflect.NewValue(f).(*reflect.FuncValue) + if !ok { + return + } + t = v.Type().(*reflect.FuncType) + return +} + +func toInterfaces(values []reflect.Value) []interface{} { + ret := make([]interface{}, len(values)) + for i, v := range values { + ret[i] = v.Interface() + } + return ret +} + +func toString(interfaces []interface{}) string { + s := make([]string, len(interfaces)) + for i, v := range interfaces { + s[i] = fmt.Sprintf("%#v", v) + } + return strings.Join(s, ", ") +} diff --git a/libgo/go/testing/quick/quick_test.go b/libgo/go/testing/quick/quick_test.go new file mode 100644 index 000000000..b126e4a16 --- /dev/null +++ b/libgo/go/testing/quick/quick_test.go @@ -0,0 +1,147 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package quick + +import ( + "rand" + "reflect" + "testing" + "os" +) + +func fBool(a bool) bool { return a } + +func fFloat32(a float32) float32 { return a } + +func fFloat64(a float64) float64 { return a } + +func fComplex64(a complex64) complex64 { return a } + +func fComplex128(a complex128) complex128 { return a } + +func fInt16(a int16) int16 { return a } + +func fInt32(a int32) int32 { return a } + +func fInt64(a int64) int64 { return a } + +func fInt8(a int8) int8 { return a } + +func fInt(a int) int { return a } + +func fUInt8(a uint8) uint8 { return a } + +func fMap(a map[int]int) map[int]int { return a } + +func fSlice(a []byte) []byte { return a } + +func fString(a string) string { return a } + +type TestStruct struct { + A int + B string +} + +func fStruct(a TestStruct) TestStruct { return a } + +func fUint16(a uint16) uint16 { return a } + +func fUint32(a uint32) uint32 { return a } + +func fUint64(a uint64) uint64 { return a } + +func fUint8(a uint8) uint8 { return a } + +func fUint(a uint) uint { return a } + +func fUintptr(a uintptr) uintptr { return a } + +func fIntptr(a *int) *int { + b := *a + return &b +} + +func reportError(property string, err os.Error, t *testing.T) { + if err != nil { + t.Errorf("%s: %s", property, err) + } +} + +func TestCheckEqual(t *testing.T) { + reportError("fBool", CheckEqual(fBool, fBool, nil), t) + reportError("fFloat32", CheckEqual(fFloat32, fFloat32, nil), t) + reportError("fFloat64", CheckEqual(fFloat64, fFloat64, nil), t) + reportError("fComplex64", CheckEqual(fComplex64, fComplex64, nil), t) + reportError("fComplex128", CheckEqual(fComplex128, fComplex128, nil), t) + reportError("fInt16", CheckEqual(fInt16, fInt16, nil), t) + reportError("fInt32", CheckEqual(fInt32, fInt32, nil), t) + reportError("fInt64", CheckEqual(fInt64, fInt64, nil), t) + reportError("fInt8", CheckEqual(fInt8, fInt8, nil), t) + reportError("fInt", CheckEqual(fInt, fInt, nil), t) + reportError("fUInt8", CheckEqual(fUInt8, fUInt8, nil), t) + reportError("fInt32", CheckEqual(fInt32, fInt32, nil), t) + reportError("fMap", CheckEqual(fMap, fMap, nil), t) + reportError("fSlice", CheckEqual(fSlice, fSlice, nil), t) + reportError("fString", CheckEqual(fString, fString, nil), t) + reportError("fStruct", CheckEqual(fStruct, fStruct, nil), t) + reportError("fUint16", CheckEqual(fUint16, fUint16, nil), t) + reportError("fUint32", CheckEqual(fUint32, fUint32, nil), t) + reportError("fUint64", CheckEqual(fUint64, fUint64, nil), t) + reportError("fUint8", CheckEqual(fUint8, fUint8, nil), t) + reportError("fUint", CheckEqual(fUint, fUint, nil), t) + reportError("fUintptr", CheckEqual(fUintptr, fUintptr, nil), t) + reportError("fIntptr", CheckEqual(fIntptr, fIntptr, nil), t) +} + +// This tests that ArbitraryValue is working by checking that all the arbitrary +// values of type MyStruct have x = 42. +type myStruct struct { + x int +} + +func (m myStruct) Generate(r *rand.Rand, _ int) reflect.Value { + return reflect.NewValue(myStruct{x: 42}) +} + +func myStructProperty(in myStruct) bool { return in.x == 42 } + +func TestCheckProperty(t *testing.T) { + reportError("myStructProperty", Check(myStructProperty, nil), t) +} + +func TestFailure(t *testing.T) { + f := func(x int) bool { return false } + err := Check(f, nil) + if err == nil { + t.Errorf("Check didn't return an error") + } + if _, ok := err.(*CheckError); !ok { + t.Errorf("Error was not a CheckError: %s", err) + } + + err = CheckEqual(fUint, fUint32, nil) + if err == nil { + t.Errorf("#1 CheckEqual didn't return an error") + } + if _, ok := err.(SetupError); !ok { + t.Errorf("#1 Error was not a SetupError: %s", err) + } + + err = CheckEqual(func(x, y int) {}, func(x int) {}, nil) + if err == nil { + t.Errorf("#2 CheckEqual didn't return an error") + } + if _, ok := err.(SetupError); !ok { + t.Errorf("#2 Error was not a SetupError: %s", err) + } + + err = CheckEqual(func(x int) int { return 0 }, func(x int) int32 { return 0 }, nil) + if err == nil { + t.Errorf("#3 CheckEqual didn't return an error") + } + if _, ok := err.(SetupError); !ok { + t.Errorf("#3 Error was not a SetupError: %s", err) + } +} diff --git a/libgo/go/testing/script/script.go b/libgo/go/testing/script/script.go new file mode 100644 index 000000000..11f5a7425 --- /dev/null +++ b/libgo/go/testing/script/script.go @@ -0,0 +1,359 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This package aids in the testing of code that uses channels. +package script + +import ( + "fmt" + "os" + "rand" + "reflect" + "strings" +) + +// An Event is an element in a partially ordered set that either sends a value +// to a channel or expects a value from a channel. +type Event struct { + name string + occurred bool + predecessors []*Event + action action +} + +type action interface { + // getSend returns nil if the action is not a send action. + getSend() sendAction + // getRecv returns nil if the action is not a receive action. + getRecv() recvAction + // getChannel returns the channel that the action operates on. + getChannel() interface{} +} + +type recvAction interface { + recvMatch(interface{}) bool +} + +type sendAction interface { + send() +} + +// isReady returns true if all the predecessors of an Event have occurred. +func (e Event) isReady() bool { + for _, predecessor := range e.predecessors { + if !predecessor.occurred { + return false + } + } + + return true +} + +// A Recv action reads a value from a channel and uses reflect.DeepMatch to +// compare it with an expected value. +type Recv struct { + Channel interface{} + Expected interface{} +} + +func (r Recv) getRecv() recvAction { return r } + +func (Recv) getSend() sendAction { return nil } + +func (r Recv) getChannel() interface{} { return r.Channel } + +func (r Recv) recvMatch(chanEvent interface{}) bool { + c, ok := chanEvent.(channelRecv) + if !ok || c.channel != r.Channel { + return false + } + + return reflect.DeepEqual(c.value, r.Expected) +} + +// A RecvMatch action reads a value from a channel and calls a function to +// determine if the value matches. +type RecvMatch struct { + Channel interface{} + Match func(interface{}) bool +} + +func (r RecvMatch) getRecv() recvAction { return r } + +func (RecvMatch) getSend() sendAction { return nil } + +func (r RecvMatch) getChannel() interface{} { return r.Channel } + +func (r RecvMatch) recvMatch(chanEvent interface{}) bool { + c, ok := chanEvent.(channelRecv) + if !ok || c.channel != r.Channel { + return false + } + + return r.Match(c.value) +} + +// A Closed action matches if the given channel is closed. The closing is +// treated as an event, not a state, thus Closed will only match once for a +// given channel. +type Closed struct { + Channel interface{} +} + +func (r Closed) getRecv() recvAction { return r } + +func (Closed) getSend() sendAction { return nil } + +func (r Closed) getChannel() interface{} { return r.Channel } + +func (r Closed) recvMatch(chanEvent interface{}) bool { + c, ok := chanEvent.(channelClosed) + if !ok || c.channel != r.Channel { + return false + } + + return true +} + +// A Send action sends a value to a channel. The value must match the +// type of the channel exactly unless the channel if of type chan interface{}. +type Send struct { + Channel interface{} + Value interface{} +} + +func (Send) getRecv() recvAction { return nil } + +func (s Send) getSend() sendAction { return s } + +func (s Send) getChannel() interface{} { return s.Channel } + +type empty struct { + x interface{} +} + +func newEmptyInterface(e empty) reflect.Value { + return reflect.NewValue(e).(*reflect.StructValue).Field(0) +} + +func (s Send) send() { + // With reflect.ChanValue.Send, we must match the types exactly. So, if + // s.Channel is a chan interface{} we convert s.Value to an interface{} + // first. + c := reflect.NewValue(s.Channel).(*reflect.ChanValue) + var v reflect.Value + if iface, ok := c.Type().(*reflect.ChanType).Elem().(*reflect.InterfaceType); ok && iface.NumMethod() == 0 { + v = newEmptyInterface(empty{s.Value}) + } else { + v = reflect.NewValue(s.Value) + } + c.Send(v) +} + +// A Close action closes the given channel. +type Close struct { + Channel interface{} +} + +func (Close) getRecv() recvAction { return nil } + +func (s Close) getSend() sendAction { return s } + +func (s Close) getChannel() interface{} { return s.Channel } + +func (s Close) send() { reflect.NewValue(s.Channel).(*reflect.ChanValue).Close() } + +// A ReceivedUnexpected error results if no active Events match a value +// received from a channel. +type ReceivedUnexpected struct { + Value interface{} + ready []*Event +} + +func (r ReceivedUnexpected) String() string { + names := make([]string, len(r.ready)) + for i, v := range r.ready { + names[i] = v.name + } + return fmt.Sprintf("received unexpected value on one of the channels: %#v. Runnable events: %s", r.Value, strings.Join(names, ", ")) +} + +// A SetupError results if there is a error with the configuration of a set of +// Events. +type SetupError string + +func (s SetupError) String() string { return string(s) } + +func NewEvent(name string, predecessors []*Event, action action) *Event { + e := &Event{name, false, predecessors, action} + return e +} + +// Given a set of Events, Perform repeatedly iterates over the set and finds the +// subset of ready Events (that is, all of their predecessors have +// occurred). From that subset, it pseudo-randomly selects an Event to perform. +// If the Event is a send event, the send occurs and Perform recalculates the ready +// set. If the event is a receive event, Perform waits for a value from any of the +// channels that are contained in any of the events. That value is then matched +// against the ready events. The first event that matches is considered to +// have occurred and Perform recalculates the ready set. +// +// Perform continues this until all Events have occurred. +// +// Note that uncollected goroutines may still be reading from any of the +// channels read from after Perform returns. +// +// For example, consider the problem of testing a function that reads values on +// one channel and echos them to two output channels. To test this we would +// create three events: a send event and two receive events. Each of the +// receive events must list the send event as a predecessor but there is no +// ordering between the receive events. +// +// send := NewEvent("send", nil, Send{c, 1}) +// recv1 := NewEvent("recv 1", []*Event{send}, Recv{c, 1}) +// recv2 := NewEvent("recv 2", []*Event{send}, Recv{c, 1}) +// Perform(0, []*Event{send, recv1, recv2}) +// +// At first, only the send event would be in the ready set and thus Perform will +// send a value to the input channel. Now the two receive events are ready and +// Perform will match each of them against the values read from the output channels. +// +// It would be invalid to list one of the receive events as a predecessor of +// the other. At each receive step, all the receive channels are considered, +// thus Perform may see a value from a channel that is not in the current ready +// set and fail. +func Perform(seed int64, events []*Event) (err os.Error) { + r := rand.New(rand.NewSource(seed)) + + channels, err := getChannels(events) + if err != nil { + return + } + multiplex := make(chan interface{}) + for _, channel := range channels { + go recvValues(multiplex, channel) + } + +Outer: + for { + ready, err := readyEvents(events) + if err != nil { + return err + } + + if len(ready) == 0 { + // All events occurred. + break + } + + event := ready[r.Intn(len(ready))] + if send := event.action.getSend(); send != nil { + send.send() + event.occurred = true + continue + } + + v := <-multiplex + for _, event := range ready { + if recv := event.action.getRecv(); recv != nil && recv.recvMatch(v) { + event.occurred = true + continue Outer + } + } + + return ReceivedUnexpected{v, ready} + } + + return nil +} + +// getChannels returns all the channels listed in any receive events. +func getChannels(events []*Event) ([]interface{}, os.Error) { + channels := make([]interface{}, len(events)) + + j := 0 + for _, event := range events { + if recv := event.action.getRecv(); recv == nil { + continue + } + c := event.action.getChannel() + if _, ok := reflect.NewValue(c).(*reflect.ChanValue); !ok { + return nil, SetupError("one of the channel values is not a channel") + } + + duplicate := false + for _, other := range channels[0:j] { + if c == other { + duplicate = true + break + } + } + + if !duplicate { + channels[j] = c + j++ + } + } + + return channels[0:j], nil +} + +// recvValues is a multiplexing helper function. It reads values from the given +// channel repeatedly, wrapping them up as either a channelRecv or +// channelClosed structure, and forwards them to the multiplex channel. +func recvValues(multiplex chan<- interface{}, channel interface{}) { + c := reflect.NewValue(channel).(*reflect.ChanValue) + + for { + v := c.Recv() + if c.Closed() { + multiplex <- channelClosed{channel} + return + } + + multiplex <- channelRecv{channel, v.Interface()} + } +} + +type channelClosed struct { + channel interface{} +} + +type channelRecv struct { + channel interface{} + value interface{} +} + +// readyEvents returns the subset of events that are ready. +func readyEvents(events []*Event) ([]*Event, os.Error) { + ready := make([]*Event, len(events)) + + j := 0 + eventsWaiting := false + for _, event := range events { + if event.occurred { + continue + } + + eventsWaiting = true + if event.isReady() { + ready[j] = event + j++ + } + } + + if j == 0 && eventsWaiting { + names := make([]string, len(events)) + for _, event := range events { + if event.occurred { + continue + } + names[j] = event.name + } + + return nil, SetupError("dependency cycle in events. These events are waiting to run but cannot: " + strings.Join(names, ", ")) + } + + return ready[0:j], nil +} diff --git a/libgo/go/testing/script/script_test.go b/libgo/go/testing/script/script_test.go new file mode 100644 index 000000000..e9ab142c2 --- /dev/null +++ b/libgo/go/testing/script/script_test.go @@ -0,0 +1,75 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package script + +import ( + "testing" +) + +func TestNoop(t *testing.T) { + err := Perform(0, nil) + if err != nil { + t.Errorf("Got error: %s", err) + } +} + +func TestSimple(t *testing.T) { + c := make(chan int) + defer close(c) + + a := NewEvent("send", nil, Send{c, 1}) + b := NewEvent("recv", []*Event{a}, Recv{c, 1}) + + err := Perform(0, []*Event{a, b}) + if err != nil { + t.Errorf("Got error: %s", err) + } +} + +func TestFail(t *testing.T) { + c := make(chan int) + defer close(c) + + a := NewEvent("send", nil, Send{c, 2}) + b := NewEvent("recv", []*Event{a}, Recv{c, 1}) + + err := Perform(0, []*Event{a, b}) + if err == nil { + t.Errorf("Failed to get expected error") + } else if _, ok := err.(ReceivedUnexpected); !ok { + t.Errorf("Error returned was of the wrong type: %s", err) + } +} + +func TestClose(t *testing.T) { + c := make(chan int) + + a := NewEvent("close", nil, Close{c}) + b := NewEvent("closed", []*Event{a}, Closed{c}) + + err := Perform(0, []*Event{a, b}) + if err != nil { + t.Errorf("Got error: %s", err) + } +} + +func matchOne(v interface{}) bool { + if i, ok := v.(int); ok && i == 1 { + return true + } + return false +} + +func TestRecvMatch(t *testing.T) { + c := make(chan int) + + a := NewEvent("send", nil, Send{c, 1}) + b := NewEvent("recv", []*Event{a}, RecvMatch{c, matchOne}) + + err := Perform(0, []*Event{a, b}) + if err != nil { + t.Errorf("Got error: %s", err) + } +} diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go new file mode 100644 index 000000000..0e04935ce --- /dev/null +++ b/libgo/go/testing/testing.go @@ -0,0 +1,174 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The testing package provides support for automated testing of Go packages. +// It is intended to be used in concert with the ``gotest'' utility, which automates +// execution of any function of the form +// func TestXxx(*testing.T) +// where Xxx can be any alphanumeric string (but the first letter must not be in +// [a-z]) and serves to identify the test routine. +// These TestXxx routines should be declared within the package they are testing. +// +// Functions of the form +// func BenchmarkXxx(*testing.B) +// are considered benchmarks, and are executed by gotest when the -benchmarks +// flag is provided. +// +// A sample benchmark function looks like this: +// func BenchmarkHello(b *testing.B) { +// for i := 0; i < b.N; i++ { +// fmt.Sprintf("hello") +// } +// } +// The benchmark package will vary b.N until the benchmark function lasts +// long enough to be timed reliably. The output +// testing.BenchmarkHello 500000 4076 ns/op +// means that the loop ran 500000 times at a speed of 4076 ns per loop. +// +// If a benchmark needs some expensive setup before running, the timer +// may be stopped: +// func BenchmarkBigLen(b *testing.B) { +// b.StopTimer() +// big := NewBig() +// b.StartTimer() +// for i := 0; i < b.N; i++ { +// big.Len() +// } +// } +package testing + +import ( + "flag" + "fmt" + "os" + "runtime" +) + +// Report as tests are run; default is silent for success. +var chatty = flag.Bool("v", false, "verbose: print additional output") +var match = flag.String("match", "", "regular expression to select tests to run") + + +// Insert final newline if needed and tabs after internal newlines. +func tabify(s string) string { + n := len(s) + if n > 0 && s[n-1] != '\n' { + s += "\n" + n++ + } + for i := 0; i < n-1; i++ { // -1 to avoid final newline + if s[i] == '\n' { + return s[0:i+1] + "\t" + tabify(s[i+1:n]) + } + } + return s +} + +// T is a type passed to Test functions to manage test state and support formatted test logs. +// Logs are accumulated during execution and dumped to standard error when done. +type T struct { + errors string + failed bool + ch chan *T +} + +// Fail marks the Test function as having failed but continues execution. +func (t *T) Fail() { t.failed = true } + +// Failed returns whether the Test function has failed. +func (t *T) Failed() bool { return t.failed } + +// FailNow marks the Test function as having failed and stops its execution. +// Execution will continue at the next Test. +func (t *T) FailNow() { + t.Fail() + t.ch <- t + runtime.Goexit() +} + +// Log formats its arguments using default formatting, analogous to Print(), +// and records the text in the error log. +func (t *T) Log(args ...interface{}) { t.errors += "\t" + tabify(fmt.Sprintln(args...)) } + +// Log formats its arguments according to the format, analogous to Printf(), +// and records the text in the error log. +func (t *T) Logf(format string, args ...interface{}) { + t.errors += "\t" + tabify(fmt.Sprintf(format, args...)) +} + +// Error is equivalent to Log() followed by Fail(). +func (t *T) Error(args ...interface{}) { + t.Log(args...) + t.Fail() +} + +// Errorf is equivalent to Logf() followed by Fail(). +func (t *T) Errorf(format string, args ...interface{}) { + t.Logf(format, args...) + t.Fail() +} + +// Fatal is equivalent to Log() followed by FailNow(). +func (t *T) Fatal(args ...interface{}) { + t.Log(args...) + t.FailNow() +} + +// Fatalf is equivalent to Logf() followed by FailNow(). +func (t *T) Fatalf(format string, args ...interface{}) { + t.Logf(format, args...) + t.FailNow() +} + +// An internal type but exported because it is cross-package; part of the implementation +// of gotest. +type InternalTest struct { + Name string + F func(*T) +} + +func tRunner(t *T, test *InternalTest) { + test.F(t) + t.ch <- t +} + +// An internal function but exported because it is cross-package; part of the implementation +// of gotest. +func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTest) { + flag.Parse() + ok := true + if len(tests) == 0 { + println("testing: warning: no tests to run") + } + for i := 0; i < len(tests); i++ { + matched, err := matchString(*match, tests[i].Name) + if err != nil { + println("invalid regexp for -match:", err.String()) + os.Exit(1) + } + if !matched { + continue + } + if *chatty { + println("=== RUN ", tests[i].Name) + } + t := new(T) + t.ch = make(chan *T) + go tRunner(t, &tests[i]) + <-t.ch + if t.failed { + println("--- FAIL:", tests[i].Name) + print(t.errors) + ok = false + } else if *chatty { + println("--- PASS:", tests[i].Name) + print(t.errors) + } + } + if !ok { + println("FAIL") + os.Exit(1) + } + println("PASS") +} |