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