Source file doc/progs/run.go

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

View as plain text