diff options
Diffstat (limited to 'libgo/go/testing')
-rw-r--r-- | libgo/go/testing/allocs.go | 8 | ||||
-rw-r--r-- | libgo/go/testing/benchmark.go | 13 | ||||
-rw-r--r-- | libgo/go/testing/benchmark_test.go | 58 | ||||
-rw-r--r-- | libgo/go/testing/cover.go | 86 | ||||
-rw-r--r-- | libgo/go/testing/export_test.go | 10 | ||||
-rw-r--r-- | libgo/go/testing/quick/quick.go | 66 | ||||
-rw-r--r-- | libgo/go/testing/quick/quick_test.go | 107 | ||||
-rw-r--r-- | libgo/go/testing/testing.go | 133 |
8 files changed, 408 insertions, 73 deletions
diff --git a/libgo/go/testing/allocs.go b/libgo/go/testing/allocs.go index d142a330b08..9ec47bd4602 100644 --- a/libgo/go/testing/allocs.go +++ b/libgo/go/testing/allocs.go @@ -9,6 +9,7 @@ import ( ) // AllocsPerRun returns the average number of allocations during calls to f. +// Although the return value has type float64, it will always be an integral value. // // To compute the number of allocations, the function will first be run once as // a warm-up. The average number of allocations over the specified number of @@ -36,6 +37,9 @@ func AllocsPerRun(runs int, f func()) (avg float64) { runtime.ReadMemStats(&memstats) mallocs += memstats.Mallocs - // Average the mallocs over the runs (not counting the warm-up) - return float64(mallocs) / float64(runs) + // Average the mallocs over the runs (not counting the warm-up). + // We are forced to return a float64 because the API is silly, but do + // the division as integers so we can ask if AllocsPerRun()==1 + // instead of AllocsPerRun()<2. + return float64(mallocs / uint64(runs)) } diff --git a/libgo/go/testing/benchmark.go b/libgo/go/testing/benchmark.go index 25fb2d61918..3473c5b2cbf 100644 --- a/libgo/go/testing/benchmark.go +++ b/libgo/go/testing/benchmark.go @@ -138,7 +138,7 @@ func max(x, y int) int { func roundDown10(n int) int { var tens = 0 // tens = floor(log_10(n)) - for n > 10 { + for n >= 10 { n = n / 10 tens++ } @@ -153,13 +153,16 @@ func roundDown10(n int) int { // 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) { + switch { + case n <= base: + return base + case n <= (2 * base): return 2 * base - } - if n < (5 * base) { + case n <= (5 * base): return 5 * base + default: + return 10 * base } - return 10 * base } // run times the benchmark function in a separate goroutine. diff --git a/libgo/go/testing/benchmark_test.go b/libgo/go/testing/benchmark_test.go new file mode 100644 index 00000000000..94e994dfae0 --- /dev/null +++ b/libgo/go/testing/benchmark_test.go @@ -0,0 +1,58 @@ +// Copyright 2013 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_test + +import ( + "testing" +) + +var roundDownTests = []struct { + v, expected int +}{ + {1, 1}, + {9, 1}, + {10, 10}, + {11, 10}, + {100, 100}, + {101, 100}, + {999, 100}, + {1000, 1000}, + {1001, 1000}, +} + +func TestRoundDown10(t *testing.T) { + for _, tt := range roundDownTests { + actual := testing.RoundDown10(tt.v) + if tt.expected != actual { + t.Errorf("roundDown10(%d): expected %d, actual %d", tt.v, tt.expected, actual) + } + } +} + +var roundUpTests = []struct { + v, expected int +}{ + {0, 1}, + {1, 1}, + {2, 2}, + {5, 5}, + {9, 10}, + {999, 1000}, + {1000, 1000}, + {1400, 2000}, + {1700, 2000}, + {4999, 5000}, + {5000, 5000}, + {5001, 10000}, +} + +func TestRoundUp(t *testing.T) { + for _, tt := range roundUpTests { + actual := testing.RoundUp(tt.v) + if tt.expected != actual { + t.Errorf("roundUp(%d): expected %d, actual %d", tt.v, tt.expected, actual) + } + } +} diff --git a/libgo/go/testing/cover.go b/libgo/go/testing/cover.go new file mode 100644 index 00000000000..dd29364d87e --- /dev/null +++ b/libgo/go/testing/cover.go @@ -0,0 +1,86 @@ +// Copyright 2013 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. + +// Support for test coverage. + +package testing + +import ( + "fmt" + "os" +) + +// CoverBlock records the coverage data for a single basic block. +// NOTE: This struct is internal to the testing infrastructure and may change. +// It is not covered (yet) by the Go 1 compatibility guidelines. +type CoverBlock struct { + Line0 uint32 + Col0 uint16 + Line1 uint32 + Col1 uint16 + Stmts uint16 +} + +var cover Cover + +// Cover records information about test coverage checking. +// NOTE: This struct is internal to the testing infrastructure and may change. +// It is not covered (yet) by the Go 1 compatibility guidelines. +type Cover struct { + Mode string + Counters map[string][]uint32 + Blocks map[string][]CoverBlock + CoveredPackages string +} + +// RegisterCover records the coverage data accumulators for the tests. +// NOTE: This function is internal to the testing infrastructure and may change. +// It is not covered (yet) by the Go 1 compatibility guidelines. +func RegisterCover(c Cover) { + cover = c +} + +// mustBeNil checks the error and, if present, reports it and exits. +func mustBeNil(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "testing: %s\n", err) + os.Exit(2) + } +} + +// coverReport reports the coverage percentage and writes a coverage profile if requested. +func coverReport() { + var f *os.File + var err error + if *coverProfile != "" { + f, err = os.Create(toOutputDir(*coverProfile)) + mustBeNil(err) + fmt.Fprintf(f, "mode: %s\n", cover.Mode) + defer func() { mustBeNil(f.Close()) }() + } + + var active, total int64 + for name, counts := range cover.Counters { + blocks := cover.Blocks[name] + for i, count := range counts { + stmts := int64(blocks[i].Stmts) + total += stmts + if count > 0 { + active += stmts + } + if f != nil { + _, err := fmt.Fprintf(f, "%s:%d.%d,%d.%d %d %d\n", name, + blocks[i].Line0, blocks[i].Col0, + blocks[i].Line1, blocks[i].Col1, + stmts, + count) + mustBeNil(err) + } + } + } + if total == 0 { + total = 1 + } + fmt.Printf("coverage: %.1f%% of statements%s\n", 100*float64(active)/float64(total), cover.CoveredPackages) +} diff --git a/libgo/go/testing/export_test.go b/libgo/go/testing/export_test.go new file mode 100644 index 00000000000..89781b439f4 --- /dev/null +++ b/libgo/go/testing/export_test.go @@ -0,0 +1,10 @@ +// Copyright 2013 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 + +var ( + RoundDown10 = roundDown10 + RoundUp = roundUp +) diff --git a/libgo/go/testing/quick/quick.go b/libgo/go/testing/quick/quick.go index 761a6471b57..bc79cc32922 100644 --- a/libgo/go/testing/quick/quick.go +++ b/libgo/go/testing/quick/quick.go @@ -34,7 +34,7 @@ func randFloat32(rand *rand.Rand) float32 { // randFloat64 generates a random float taking the full range of a float64. func randFloat64(rand *rand.Rand) float64 { - f := rand.Float64() + f := rand.Float64() * math.MaxFloat64 if rand.Int()&1 == 1 { f = -f } @@ -56,90 +56,88 @@ func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) { return m.Generate(rand, complexSize), true } + v := reflect.New(t).Elem() switch concrete := t; concrete.Kind() { case reflect.Bool: - return reflect.ValueOf(rand.Int()&1 == 0), true + v.SetBool(rand.Int()&1 == 0) case reflect.Float32: - return reflect.ValueOf(randFloat32(rand)), true + v.SetFloat(float64(randFloat32(rand))) case reflect.Float64: - return reflect.ValueOf(randFloat64(rand)), true + v.SetFloat(randFloat64(rand)) case reflect.Complex64: - return reflect.ValueOf(complex(randFloat32(rand), randFloat32(rand))), true + v.SetComplex(complex(float64(randFloat32(rand)), float64(randFloat32(rand)))) case reflect.Complex128: - return reflect.ValueOf(complex(randFloat64(rand), randFloat64(rand))), true + v.SetComplex(complex(randFloat64(rand), randFloat64(rand))) case reflect.Int16: - return reflect.ValueOf(int16(randInt64(rand))), true + v.SetInt(randInt64(rand)) case reflect.Int32: - return reflect.ValueOf(int32(randInt64(rand))), true + v.SetInt(randInt64(rand)) case reflect.Int64: - return reflect.ValueOf(randInt64(rand)), true + v.SetInt(randInt64(rand)) case reflect.Int8: - return reflect.ValueOf(int8(randInt64(rand))), true + v.SetInt(randInt64(rand)) case reflect.Int: - return reflect.ValueOf(int(randInt64(rand))), true + v.SetInt(randInt64(rand)) case reflect.Uint16: - return reflect.ValueOf(uint16(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Uint32: - return reflect.ValueOf(uint32(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Uint64: - return reflect.ValueOf(uint64(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Uint8: - return reflect.ValueOf(uint8(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Uint: - return reflect.ValueOf(uint(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Uintptr: - return reflect.ValueOf(uintptr(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Map: numElems := rand.Intn(complexSize) - m := reflect.MakeMap(concrete) + v.Set(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 reflect.Value{}, false } - m.SetMapIndex(key, value) + v.SetMapIndex(key, value) } - return m, true case reflect.Ptr: - v, ok := Value(concrete.Elem(), rand) + elem, ok := Value(concrete.Elem(), rand) if !ok { return reflect.Value{}, false } - p := reflect.New(concrete.Elem()) - p.Elem().Set(v) - return p, true + v.Set(reflect.New(concrete.Elem())) + v.Elem().Set(elem) case reflect.Slice: numElems := rand.Intn(complexSize) - s := reflect.MakeSlice(concrete, numElems, numElems) + v.Set(reflect.MakeSlice(concrete, numElems, numElems)) for i := 0; i < numElems; i++ { - v, ok := Value(concrete.Elem(), rand) + elem, ok := Value(concrete.Elem(), rand) if !ok { return reflect.Value{}, false } - s.Index(i).Set(v) + v.Index(i).Set(elem) } - return s, true case reflect.String: numChars := rand.Intn(complexSize) codePoints := make([]rune, numChars) for i := 0; i < numChars; i++ { codePoints[i] = rune(rand.Intn(0x10ffff)) } - return reflect.ValueOf(string(codePoints)), true + v.SetString(string(codePoints)) case reflect.Struct: - s := reflect.New(t).Elem() - for i := 0; i < s.NumField(); i++ { - v, ok := Value(concrete.Field(i).Type, rand) + for i := 0; i < v.NumField(); i++ { + elem, ok := Value(concrete.Field(i).Type, rand) if !ok { return reflect.Value{}, false } - s.Field(i).Set(v) + v.Field(i).Set(elem) } - return s, true default: return reflect.Value{}, false } + + return v, true } // A Config structure contains options for running a test. diff --git a/libgo/go/testing/quick/quick_test.go b/libgo/go/testing/quick/quick_test.go index a178ec28e69..36745ae2aba 100644 --- a/libgo/go/testing/quick/quick_test.go +++ b/libgo/go/testing/quick/quick_test.go @@ -13,32 +13,82 @@ import ( func fBool(a bool) bool { return a } +type TestBoolAlias bool + +func fBoolAlias(a TestBoolAlias) TestBoolAlias { return a } + func fFloat32(a float32) float32 { return a } +type TestFloat32Alias float32 + +func fFloat32Alias(a TestFloat32Alias) TestFloat32Alias { return a } + func fFloat64(a float64) float64 { return a } +type TestFloat64Alias float64 + +func fFloat64Alias(a TestFloat64Alias) TestFloat64Alias { return a } + func fComplex64(a complex64) complex64 { return a } +type TestComplex64Alias complex64 + +func fComplex64Alias(a TestComplex64Alias) TestComplex64Alias { return a } + func fComplex128(a complex128) complex128 { return a } +type TestComplex128Alias complex128 + +func fComplex128Alias(a TestComplex128Alias) TestComplex128Alias { return a } + func fInt16(a int16) int16 { return a } +type TestInt16Alias int16 + +func fInt16Alias(a TestInt16Alias) TestInt16Alias { return a } + func fInt32(a int32) int32 { return a } +type TestInt32Alias int32 + +func fInt32Alias(a TestInt32Alias) TestInt32Alias { return a } + func fInt64(a int64) int64 { return a } +type TestInt64Alias int64 + +func fInt64Alias(a TestInt64Alias) TestInt64Alias { return a } + func fInt8(a int8) int8 { return a } +type TestInt8Alias int8 + +func fInt8Alias(a TestInt8Alias) TestInt8Alias { return a } + func fInt(a int) int { return a } -func fUInt8(a uint8) uint8 { return a } +type TestIntAlias int + +func fIntAlias(a TestIntAlias) TestIntAlias { return a } func fMap(a map[int]int) map[int]int { return a } +type TestMapAlias map[int]int + +func fMapAlias(a TestMapAlias) TestMapAlias { return a } + func fSlice(a []byte) []byte { return a } +type TestSliceAlias []byte + +func fSliceAlias(a TestSliceAlias) TestSliceAlias { return a } + func fString(a string) string { return a } +type TestStringAlias string + +func fStringAlias(a TestStringAlias) TestStringAlias { return a } + type TestStruct struct { A int B string @@ -46,23 +96,55 @@ type TestStruct struct { func fStruct(a TestStruct) TestStruct { return a } +type TestStructAlias TestStruct + +func fStructAlias(a TestStructAlias) TestStructAlias { return a } + func fUint16(a uint16) uint16 { return a } +type TestUint16Alias uint16 + +func fUint16Alias(a TestUint16Alias) TestUint16Alias { return a } + func fUint32(a uint32) uint32 { return a } +type TestUint32Alias uint32 + +func fUint32Alias(a TestUint32Alias) TestUint32Alias { return a } + func fUint64(a uint64) uint64 { return a } +type TestUint64Alias uint64 + +func fUint64Alias(a TestUint64Alias) TestUint64Alias { return a } + func fUint8(a uint8) uint8 { return a } +type TestUint8Alias uint8 + +func fUint8Alias(a TestUint8Alias) TestUint8Alias { return a } + func fUint(a uint) uint { return a } +type TestUintAlias uint + +func fUintAlias(a TestUintAlias) TestUintAlias { return a } + func fUintptr(a uintptr) uintptr { return a } +type TestUintptrAlias uintptr + +func fUintptrAlias(a TestUintptrAlias) TestUintptrAlias { return a } + func fIntptr(a *int) *int { b := *a return &b } +type TestIntptrAlias *int + +func fIntptrAlias(a TestIntptrAlias) TestIntptrAlias { return a } + func reportError(property string, err error, t *testing.T) { if err != nil { t.Errorf("%s: %s", property, err) @@ -71,30 +153,51 @@ func reportError(property string, err error, t *testing.T) { func TestCheckEqual(t *testing.T) { reportError("fBool", CheckEqual(fBool, fBool, nil), t) + reportError("fBoolAlias", CheckEqual(fBoolAlias, fBoolAlias, nil), t) reportError("fFloat32", CheckEqual(fFloat32, fFloat32, nil), t) + reportError("fFloat32Alias", CheckEqual(fFloat32Alias, fFloat32Alias, nil), t) reportError("fFloat64", CheckEqual(fFloat64, fFloat64, nil), t) + reportError("fFloat64Alias", CheckEqual(fFloat64Alias, fFloat64Alias, nil), t) if runtime.GOARCH != "alpha" { reportError("fComplex64", CheckEqual(fComplex64, fComplex64, nil), t) + reportError("fComplex64Alias", CheckEqual(fComplex64Alias, fComplex64Alias, nil), t) reportError("fComplex128", CheckEqual(fComplex128, fComplex128, nil), t) + reportError("fComplex128Alias", CheckEqual(fComplex128Alias, fComplex128Alias, nil), t) } reportError("fInt16", CheckEqual(fInt16, fInt16, nil), t) + reportError("fInt16Alias", CheckEqual(fInt16Alias, fInt16Alias, nil), t) reportError("fInt32", CheckEqual(fInt32, fInt32, nil), t) + reportError("fInt32Alias", CheckEqual(fInt32Alias, fInt32Alias, nil), t) reportError("fInt64", CheckEqual(fInt64, fInt64, nil), t) + reportError("fInt64Alias", CheckEqual(fInt64Alias, fInt64Alias, nil), t) reportError("fInt8", CheckEqual(fInt8, fInt8, nil), t) + reportError("fInt8Alias", CheckEqual(fInt8Alias, fInt8Alias, nil), t) reportError("fInt", CheckEqual(fInt, fInt, nil), t) - reportError("fUInt8", CheckEqual(fUInt8, fUInt8, nil), t) + reportError("fIntAlias", CheckEqual(fIntAlias, fIntAlias, nil), t) reportError("fInt32", CheckEqual(fInt32, fInt32, nil), t) + reportError("fInt32Alias", CheckEqual(fInt32Alias, fInt32Alias, nil), t) reportError("fMap", CheckEqual(fMap, fMap, nil), t) + reportError("fMapAlias", CheckEqual(fMapAlias, fMapAlias, nil), t) reportError("fSlice", CheckEqual(fSlice, fSlice, nil), t) + reportError("fSliceAlias", CheckEqual(fSliceAlias, fSliceAlias, nil), t) reportError("fString", CheckEqual(fString, fString, nil), t) + reportError("fStringAlias", CheckEqual(fStringAlias, fStringAlias, nil), t) reportError("fStruct", CheckEqual(fStruct, fStruct, nil), t) + reportError("fStructAlias", CheckEqual(fStructAlias, fStructAlias, nil), t) reportError("fUint16", CheckEqual(fUint16, fUint16, nil), t) + reportError("fUint16Alias", CheckEqual(fUint16Alias, fUint16Alias, nil), t) reportError("fUint32", CheckEqual(fUint32, fUint32, nil), t) + reportError("fUint32Alias", CheckEqual(fUint32Alias, fUint32Alias, nil), t) reportError("fUint64", CheckEqual(fUint64, fUint64, nil), t) + reportError("fUint64Alias", CheckEqual(fUint64Alias, fUint64Alias, nil), t) reportError("fUint8", CheckEqual(fUint8, fUint8, nil), t) + reportError("fUint8Alias", CheckEqual(fUint8Alias, fUint8Alias, nil), t) reportError("fUint", CheckEqual(fUint, fUint, nil), t) + reportError("fUintAlias", CheckEqual(fUintAlias, fUintAlias, nil), t) reportError("fUintptr", CheckEqual(fUintptr, fUintptr, nil), t) + reportError("fUintptrAlias", CheckEqual(fUintptrAlias, fUintptrAlias, nil), t) reportError("fIntptr", CheckEqual(fIntptr, fIntptr, nil), t) + reportError("fIntptrAlias", CheckEqual(fIntptrAlias, fIntptrAlias, nil), t) } // This tests that ArbitraryValue is working by checking that all the arbitrary diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go index 312d2873296..5019e076269 100644 --- a/libgo/go/testing/testing.go +++ b/libgo/go/testing/testing.go @@ -23,10 +23,10 @@ // Functions of the form // func BenchmarkXxx(*testing.B) // are considered benchmarks, and are executed by the "go test" command when -// the -test.bench flag is provided. Benchmarks are run sequentially. +// its -bench flag is provided. Benchmarks are run sequentially. // // For a description of the testing flags, see -// http://golang.org/cmd/go/#Description_of_testing_flags. +// http://golang.org/cmd/go/#hdr-Description_of_testing_flags. // // A sample benchmark function looks like this: // func BenchmarkHello(b *testing.B) { @@ -114,8 +114,15 @@ var ( // full test of the package. short = flag.Bool("test.short", false, "run smaller test suite to save time") + // The directory in which to create profile files and the like. When run from + // "go test", the binary always runs in the source directory for the package; + // this flag lets "go test" tell the binary to write the files in the directory where + // the "go test" command is run. + outputDir = flag.String("test.outputdir", "", "directory in which to write profiles") + // Report as tests are run; default is silent for success. chatty = flag.Bool("test.v", false, "verbose: print additional output") + coverProfile = flag.String("test.coverprofile", "", "write a coverage profile to the named file after execution") match = flag.String("test.run", "", "regular expression to select tests and examples to run") memProfile = flag.String("test.memprofile", "", "write a memory profile to the named file after execution") memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate") @@ -189,6 +196,31 @@ func decorate(s string) string { return buf.String() } +// TB is the interface common to T and B. +type TB interface { + Error(args ...interface{}) + Errorf(format string, args ...interface{}) + Fail() + FailNow() + Failed() bool + Fatal(args ...interface{}) + Fatalf(format string, args ...interface{}) + Log(args ...interface{}) + Logf(format string, args ...interface{}) + Skip(args ...interface{}) + SkipNow() + Skipf(format string, args ...interface{}) + Skipped() bool + + // A private method to prevent users implementing the + // interface and so future additions to it will not + // violate Go 1 compatibility. + private() +} + +var _ TB = (*T)(nil) +var _ TB = (*B)(nil) + // 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 { @@ -197,6 +229,8 @@ type T struct { startParallel chan bool // Parallel tests will wait on this. } +func (c *common) private() {} + // Fail marks the function as having failed but continues execution. func (c *common) Fail() { c.mu.Lock() @@ -323,6 +357,9 @@ func (c *common) Skipped() bool { func (t *T) Parallel() { t.signal <- (*T)(nil) // Release main testing loop <-t.startParallel // Wait for serial tests to finish + // Assuming Parallel is the first thing a test does, which is reasonable, + // reinitialize the test's start time because it's actually starting now. + t.start = time.Now() } // An internal type but exported because it is cross-package; part of the implementation @@ -333,8 +370,6 @@ type InternalTest struct { } func tRunner(t *T, test *InternalTest) { - t.start = time.Now() - // When this goroutine is done, either because test.F(t) // returned normally or because a test failure triggered // a call to runtime.Goexit, record the duration and send @@ -350,6 +385,7 @@ func tRunner(t *T, test *InternalTest) { t.signal <- t }() + t.start = time.Now() test.F(t) } @@ -364,12 +400,12 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, haveExamples = len(examples) > 0 testOk := RunTests(matchString, tests) exampleOk := RunExamples(matchString, examples) + stopAlarm() if !testOk || !exampleOk { fmt.Println("FAIL") os.Exit(1) } fmt.Println("PASS") - stopAlarm() RunBenchmarks(matchString, benchmarks) after() } @@ -466,7 +502,7 @@ func before() { runtime.MemProfileRate = *memProfileRate } if *cpuProfile != "" { - f, err := os.Create(*cpuProfile) + f, err := os.Create(toOutputDir(*cpuProfile)) if err != nil { fmt.Fprintf(os.Stderr, "testing: %s", err) return @@ -481,6 +517,10 @@ func before() { if *blockProfile != "" && *blockProfileRate >= 0 { runtime.SetBlockProfileRate(*blockProfileRate) } + if *coverProfile != "" && cover.Mode == "" { + fmt.Fprintf(os.Stderr, "testing: cannot use -test.coverprofile because test binary was not built with coverage enabled\n") + os.Exit(2) + } } // after runs after all testing. @@ -489,27 +529,60 @@ func after() { pprof.StopCPUProfile() // flushes profile to disk } if *memProfile != "" { - f, err := os.Create(*memProfile) + f, err := os.Create(toOutputDir(*memProfile)) if err != nil { - fmt.Fprintf(os.Stderr, "testing: %s", err) - return + fmt.Fprintf(os.Stderr, "testing: %s\n", err) + os.Exit(2) } if err = pprof.WriteHeapProfile(f); err != nil { - fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", *memProfile, err) + fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *memProfile, err) + os.Exit(2) } f.Close() } if *blockProfile != "" && *blockProfileRate >= 0 { - f, err := os.Create(*blockProfile) + f, err := os.Create(toOutputDir(*blockProfile)) if err != nil { - fmt.Fprintf(os.Stderr, "testing: %s", err) - return + fmt.Fprintf(os.Stderr, "testing: %s\n", err) + os.Exit(2) } if err = pprof.Lookup("block").WriteTo(f, 0); err != nil { - fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", *blockProfile, err) + fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *blockProfile, err) + os.Exit(2) } f.Close() } + if cover.Mode != "" { + coverReport() + } +} + +// toOutputDir returns the file name relocated, if required, to outputDir. +// Simple implementation to avoid pulling in path/filepath. +func toOutputDir(path string) string { + if *outputDir == "" || path == "" { + return path + } + if runtime.GOOS == "windows" { + // On Windows, it's clumsy, but we can be almost always correct + // by just looking for a drive letter and a colon. + // Absolute paths always have a drive letter (ignoring UNC). + // Problem: if path == "C:A" and outputdir == "C:\Go" it's unclear + // what to do, but even then path/filepath doesn't help. + // TODO: Worth doing better? Probably not, because we're here only + // under the management of go test. + if len(path) >= 2 { + letter, colon := path[0], path[1] + if ('a' <= letter && letter <= 'z' || 'A' <= letter && letter <= 'Z') && colon == ':' { + // If path starts with a drive letter we're stuck with it regardless. + return path + } + } + } + if os.IsPathSeparator(path[0]) { + return path + } + return fmt.Sprintf("%s%c%s", *outputDir, os.PathSeparator, path) } var timer *time.Timer @@ -517,7 +590,9 @@ var timer *time.Timer // startAlarm starts an alarm if requested. func startAlarm() { if *timeout > 0 { - timer = time.AfterFunc(*timeout, alarm) + timer = time.AfterFunc(*timeout, func() { + panic(fmt.Sprintf("test timed out after %v", *timeout)) + }) } } @@ -528,22 +603,20 @@ func stopAlarm() { } } -// alarm is called if the timeout expires. -func alarm() { - panic("test timed out") -} - func parseCpuList() { - if len(*cpuListStr) == 0 { - cpuList = append(cpuList, runtime.GOMAXPROCS(-1)) - } else { - for _, val := range strings.Split(*cpuListStr, ",") { - cpu, err := strconv.Atoi(val) - if err != nil || cpu <= 0 { - fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu", val) - os.Exit(1) - } - cpuList = append(cpuList, cpu) + for _, val := range strings.Split(*cpuListStr, ",") { + val = strings.TrimSpace(val) + if val == "" { + continue + } + cpu, err := strconv.Atoi(val) + if err != nil || cpu <= 0 { + fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu\n", val) + os.Exit(1) } + cpuList = append(cpuList, cpu) + } + if cpuList == nil { + cpuList = append(cpuList, runtime.GOMAXPROCS(-1)) } } |