github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/doc/progs/run.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // run runs the docs tests found in this directory.
     6  package main
     7  
     8  import (
     9  	"bytes"
    10  	"flag"
    11  	"fmt"
    12  	"os"
    13  	"os/exec"
    14  	"regexp"
    15  	"runtime"
    16  	"strings"
    17  )
    18  
    19  const usage = `go run run.go [tests]
    20  
    21  run.go runs the docs tests in this directory.
    22  If no tests are provided, it runs all tests.
    23  Tests may be specified without their .go suffix.
    24  `
    25  
    26  func main() {
    27  	flag.Usage = func() {
    28  		fmt.Fprintf(os.Stderr, usage)
    29  		flag.PrintDefaults()
    30  		os.Exit(2)
    31  	}
    32  
    33  	flag.Parse()
    34  	if flag.NArg() == 0 {
    35  		// run all tests
    36  		fixcgo()
    37  	} else {
    38  		// run specified tests
    39  		onlyTest(flag.Args()...)
    40  	}
    41  
    42  	// ratec limits the number of tests running concurrently.
    43  	// None of the tests are intensive, so don't bother
    44  	// trying to manually adjust for slow builders.
    45  	ratec := make(chan bool, runtime.NumCPU())
    46  	errc := make(chan error, len(tests))
    47  
    48  	for _, tt := range tests {
    49  		tt := tt
    50  		ratec <- true
    51  		go func() {
    52  			errc <- test(tt.file, tt.want)
    53  			<-ratec
    54  		}()
    55  	}
    56  
    57  	var rc int
    58  	for range tests {
    59  		if err := <-errc; err != nil {
    60  			fmt.Fprintln(os.Stderr, err)
    61  			rc = 1
    62  		}
    63  	}
    64  	os.Exit(rc)
    65  }
    66  
    67  // test builds the test in the given file.
    68  // If want is non-empty, test also runs the test
    69  // and checks that the output matches the regexp want.
    70  func test(file, want string) error {
    71  	// Build the program.
    72  	cmd := exec.Command("go", "build", file+".go")
    73  	out, err := cmd.CombinedOutput()
    74  	if err != nil {
    75  		return fmt.Errorf("go build %s.go failed: %v\nOutput:\n%s", file, err, out)
    76  	}
    77  	defer os.Remove(file)
    78  
    79  	// Only run the test if we have output to check.
    80  	if want == "" {
    81  		return nil
    82  	}
    83  
    84  	cmd = exec.Command("./" + file)
    85  	out, err = cmd.CombinedOutput()
    86  	if err != nil {
    87  		return fmt.Errorf("./%s failed: %v\nOutput:\n%s", file, err, out)
    88  	}
    89  
    90  	// Canonicalize output.
    91  	out = bytes.TrimRight(out, "\n")
    92  	out = bytes.Replace(out, []byte{'\n'}, []byte{' '}, -1)
    93  
    94  	// Check the result.
    95  	match, err := regexp.Match(want, out)
    96  	if err != nil {
    97  		return fmt.Errorf("failed to parse regexp %q: %v", want, err)
    98  	}
    99  	if !match {
   100  		return fmt.Errorf("%s.go:\n%q\ndoes not match %s", file, out, want)
   101  	}
   102  
   103  	return nil
   104  }
   105  
   106  type testcase struct {
   107  	file string
   108  	want string
   109  }
   110  
   111  var tests = []testcase{
   112  	// defer_panic_recover
   113  	{"defer", `^0 3210 2$`},
   114  	{"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.$`},
   115  
   116  	// effective_go
   117  	{"eff_bytesize", `^1.00YB 9.09TB$`},
   118  	{"eff_qr", ""},
   119  	{"eff_sequence", `^\[-1 2 6 16 44\]$`},
   120  	{"eff_unused2", ""},
   121  
   122  	// error_handling
   123  	{"error", ""},
   124  	{"error2", ""},
   125  	{"error3", ""},
   126  	{"error4", ""},
   127  
   128  	// law_of_reflection
   129  	{"interface", ""},
   130  	{"interface2", `^type: float64$`},
   131  
   132  	// c_go_cgo
   133  	{"cgo1", ""},
   134  	{"cgo2", ""},
   135  	{"cgo3", ""},
   136  	{"cgo4", ""},
   137  
   138  	// timeout
   139  	{"timeout1", ""},
   140  	{"timeout2", ""},
   141  
   142  	// gobs
   143  	{"gobs1", ""},
   144  	{"gobs2", ""},
   145  
   146  	// json
   147  	{"json1", `^$`},
   148  	{"json2", `the reciprocal of i is`},
   149  	{"json3", `Age is int 6`},
   150  	{"json4", `^$`},
   151  	{"json5", ""},
   152  
   153  	// image_package
   154  	{"image_package1", `^X is 2 Y is 1$`},
   155  	{"image_package2", `^3 4 false$`},
   156  	{"image_package3", `^3 4 true$`},
   157  	{"image_package4", `^image.Point{X:2, Y:1}$`},
   158  	{"image_package5", `^{255 0 0 255}$`},
   159  	{"image_package6", `^8 4 true$`},
   160  
   161  	// other
   162  	{"go1", `^Christmas is a holiday: true Sleeping for 0.123s.*go1.go already exists$`},
   163  	{"slices", ""},
   164  }
   165  
   166  func onlyTest(files ...string) {
   167  	var new []testcase
   168  NextFile:
   169  	for _, file := range files {
   170  		file = strings.TrimSuffix(file, ".go")
   171  		for _, tt := range tests {
   172  			if tt.file == file {
   173  				new = append(new, tt)
   174  				continue NextFile
   175  			}
   176  		}
   177  		fmt.Fprintf(os.Stderr, "test %s.go not found\n", file)
   178  		os.Exit(1)
   179  	}
   180  	tests = new
   181  }
   182  
   183  func skipTest(file string) {
   184  	for i, tt := range tests {
   185  		if tt.file == file {
   186  			copy(tests[i:], tests[i+1:])
   187  			tests = tests[:len(tests)-1]
   188  			return
   189  		}
   190  	}
   191  	panic("delete(" + file + "): not found")
   192  }
   193  
   194  func fixcgo() {
   195  	if os.Getenv("CGO_ENABLED") != "1" {
   196  		skipTest("cgo1")
   197  		skipTest("cgo2")
   198  		skipTest("cgo3")
   199  		skipTest("cgo4")
   200  		return
   201  	}
   202  
   203  	switch runtime.GOOS {
   204  	case "freebsd":
   205  		// cgo1 and cgo2 don't run on freebsd, srandom has a different signature
   206  		skipTest("cgo1")
   207  		skipTest("cgo2")
   208  	case "netbsd":
   209  		// cgo1 and cgo2 don't run on netbsd, srandom has a different signature
   210  		skipTest("cgo1")
   211  		skipTest("cgo2")
   212  		// cgo3 and cgo4 don't run on netbsd, since cgo cannot handle stdout correctly
   213  		skipTest("cgo3")
   214  		skipTest("cgo4")
   215  	case "openbsd":
   216  		// cgo3 and cgo4 don't run on openbsd and solaris, since cgo cannot handle stdout correctly
   217  		skipTest("cgo3")
   218  		skipTest("cgo4")
   219  	}
   220  }