2015-04-02 09:53:27 -06:00
// Copyright 2015 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.
// run runs the docs tests found in this directory.
package main
import (
"bytes"
"flag"
"fmt"
2015-04-17 22:04:39 -06:00
"io/ioutil"
2015-04-02 09:53:27 -06:00
"os"
"os/exec"
2015-04-17 22:04:39 -06:00
"path/filepath"
2015-04-02 09:53:27 -06:00
"regexp"
"runtime"
"strings"
2019-11-14 12:32:24 -07:00
"time"
2015-04-02 09:53:27 -06:00
)
const usage = ` go run run . go [ tests ]
run . go runs the docs tests in this directory .
If no tests are provided , it runs all tests .
Tests may be specified without their . go suffix .
`
func main ( ) {
2019-11-14 12:32:24 -07:00
start := time . Now ( )
2015-04-02 09:53:27 -06:00
flag . Usage = func ( ) {
fmt . Fprintf ( os . Stderr , usage )
flag . PrintDefaults ( )
os . Exit ( 2 )
}
flag . Parse ( )
if flag . NArg ( ) == 0 {
// run all tests
fixcgo ( )
} else {
// run specified tests
onlyTest ( flag . Args ( ) ... )
}
2015-04-17 22:04:39 -06:00
tmpdir , err := ioutil . TempDir ( "" , "go-progs" )
if err != nil {
fmt . Fprintln ( os . Stderr , err )
os . Exit ( 1 )
}
2015-04-02 09:53:27 -06:00
// ratec limits the number of tests running concurrently.
// None of the tests are intensive, so don't bother
// trying to manually adjust for slow builders.
ratec := make ( chan bool , runtime . NumCPU ( ) )
errc := make ( chan error , len ( tests ) )
for _ , tt := range tests {
tt := tt
ratec <- true
go func ( ) {
2015-04-17 22:04:39 -06:00
errc <- test ( tmpdir , tt . file , tt . want )
2015-04-02 09:53:27 -06:00
<- ratec
} ( )
}
var rc int
for range tests {
if err := <- errc ; err != nil {
fmt . Fprintln ( os . Stderr , err )
rc = 1
}
}
2015-04-17 22:04:39 -06:00
os . Remove ( tmpdir )
2019-11-14 12:32:24 -07:00
if rc == 0 {
fmt . Printf ( "ok\t%s\t%s\n" , filepath . Base ( os . Args [ 0 ] ) , time . Since ( start ) . Round ( time . Millisecond ) )
}
2015-04-02 09:53:27 -06:00
os . Exit ( rc )
}
// test builds the test in the given file.
// If want is non-empty, test also runs the test
// and checks that the output matches the regexp want.
2015-04-17 22:04:39 -06:00
func test ( tmpdir , file , want string ) error {
2015-04-02 09:53:27 -06:00
// Build the program.
2019-11-14 12:32:24 -07:00
prog := filepath . Join ( tmpdir , file + ".exe" )
2015-04-17 22:04:39 -06:00
cmd := exec . Command ( "go" , "build" , "-o" , prog , file + ".go" )
2015-04-02 09:53:27 -06:00
out , err := cmd . CombinedOutput ( )
if err != nil {
return fmt . Errorf ( "go build %s.go failed: %v\nOutput:\n%s" , file , err , out )
}
2015-04-17 22:04:39 -06:00
defer os . Remove ( prog )
2015-04-02 09:53:27 -06:00
// Only run the test if we have output to check.
if want == "" {
return nil
}
2015-04-17 22:04:39 -06:00
cmd = exec . Command ( prog )
2015-04-02 09:53:27 -06:00
out , err = cmd . CombinedOutput ( )
if err != nil {
2015-04-17 22:04:39 -06:00
return fmt . Errorf ( "%s failed: %v\nOutput:\n%s" , file , err , out )
2015-04-02 09:53:27 -06:00
}
// Canonicalize output.
out = bytes . TrimRight ( out , "\n" )
out = bytes . Replace ( out , [ ] byte { '\n' } , [ ] byte { ' ' } , - 1 )
// Check the result.
match , err := regexp . Match ( want , out )
if err != nil {
return fmt . Errorf ( "failed to parse regexp %q: %v" , want , err )
}
if ! match {
return fmt . Errorf ( "%s.go:\n%q\ndoes not match %s" , file , out , want )
}
return nil
}
type testcase struct {
file string
want string
}
var tests = [ ] testcase {
// defer_panic_recover
{ "defer" , ` ^0 3210 2$ ` } ,
{ "defer2" , ` ^Calling g. Printing in g 0 Printing in g 1 Printing in g 2 Printing in g 3 Panicking! Defer in g 3 Defer in g 2 Defer in g 1 Defer in g 0 Recovered in f 4 Returned normally from f.$ ` } ,
// effective_go
{ "eff_bytesize" , ` ^1.00YB 9.09TB$ ` } ,
{ "eff_qr" , "" } ,
{ "eff_sequence" , ` ^\[-1 2 6 16 44\]$ ` } ,
{ "eff_unused2" , "" } ,
// error_handling
{ "error" , "" } ,
{ "error2" , "" } ,
{ "error3" , "" } ,
{ "error4" , "" } ,
// law_of_reflection
{ "interface" , "" } ,
{ "interface2" , ` ^type: float64$ ` } ,
// c_go_cgo
{ "cgo1" , "" } ,
{ "cgo2" , "" } ,
{ "cgo3" , "" } ,
{ "cgo4" , "" } ,
// timeout
{ "timeout1" , "" } ,
{ "timeout2" , "" } ,
// gobs
{ "gobs1" , "" } ,
{ "gobs2" , "" } ,
// json
{ "json1" , ` ^$ ` } ,
{ "json2" , ` the reciprocal of i is ` } ,
{ "json3" , ` Age is int 6 ` } ,
{ "json4" , ` ^$ ` } ,
{ "json5" , "" } ,
// image_package
{ "image_package1" , ` ^X is 2 Y is 1$ ` } ,
{ "image_package2" , ` ^3 4 false$ ` } ,
{ "image_package3" , ` ^3 4 true$ ` } ,
{ "image_package4" , ` ^image.Point { X:2, Y:1}$ ` } ,
{ "image_package5" , ` ^ { 255 0 0 255}$ ` } ,
{ "image_package6" , ` ^8 4 true$ ` } ,
// other
2015-04-30 04:01:22 -06:00
{ "go1" , ` ^Christmas is a holiday: true .*go1.go already exists$ ` } ,
2015-04-02 09:53:27 -06:00
{ "slices" , "" } ,
}
func onlyTest ( files ... string ) {
var new [ ] testcase
NextFile :
for _ , file := range files {
file = strings . TrimSuffix ( file , ".go" )
for _ , tt := range tests {
if tt . file == file {
new = append ( new , tt )
continue NextFile
}
}
fmt . Fprintf ( os . Stderr , "test %s.go not found\n" , file )
os . Exit ( 1 )
}
tests = new
}
func skipTest ( file string ) {
for i , tt := range tests {
if tt . file == file {
copy ( tests [ i : ] , tests [ i + 1 : ] )
tests = tests [ : len ( tests ) - 1 ]
return
}
}
panic ( "delete(" + file + "): not found" )
}
func fixcgo ( ) {
if os . Getenv ( "CGO_ENABLED" ) != "1" {
skipTest ( "cgo1" )
skipTest ( "cgo2" )
skipTest ( "cgo3" )
skipTest ( "cgo4" )
return
}
switch runtime . GOOS {
case "freebsd" :
// cgo1 and cgo2 don't run on freebsd, srandom has a different signature
skipTest ( "cgo1" )
skipTest ( "cgo2" )
case "netbsd" :
// cgo1 and cgo2 don't run on netbsd, srandom has a different signature
skipTest ( "cgo1" )
skipTest ( "cgo2" )
}
}