| 
									
										
										
										
											2012-03-22 02:14:44 +08:00
										 |  |  | // skip | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Copyright 2012 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 tests in the test directory. | 
					
						
							|  |  |  | //  | 
					
						
							|  |  |  | // TODO(bradfitz): docs of some sort, once we figure out how we're changing | 
					
						
							|  |  |  | // headers of files | 
					
						
							|  |  |  | package main | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"flag" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"go/build" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"log" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"os/exec" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"regexp" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"sort" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	verbose     = flag.Bool("v", false, "verbose. if set, parallelism is set to 1.") | 
					
						
							| 
									
										
										
										
											2012-03-07 12:43:25 +08:00
										 |  |  | 	numParallel = flag.Int("n", runtime.NumCPU(), "number of parallel tests to run") | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 	summary     = flag.Bool("summary", false, "show summary of results") | 
					
						
							| 
									
										
										
										
											2012-02-24 12:52:15 +11:00
										 |  |  | 	showSkips   = flag.Bool("show_skips", false, "show skipped tests") | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	// gc and ld are [568][gl]. | 
					
						
							|  |  |  | 	gc, ld string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// letter is the build.ArchChar | 
					
						
							|  |  |  | 	letter string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// dirs are the directories to look for *.go files in. | 
					
						
							|  |  |  | 	// TODO(bradfitz): just use all directories? | 
					
						
							|  |  |  | 	dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "bugs"} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// ratec controls the max number of tests running at a time. | 
					
						
							|  |  |  | 	ratec chan bool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// toRun is the channel of tests to run. | 
					
						
							|  |  |  | 	// It is nil until the first test is started. | 
					
						
							|  |  |  | 	toRun chan *test | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // maxTests is an upper bound on the total number of tests. | 
					
						
							|  |  |  | // It is used as a channel buffer size to make sure sends don't block. | 
					
						
							|  |  |  | const maxTests = 5000 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func main() { | 
					
						
							|  |  |  | 	flag.Parse() | 
					
						
							| 
									
										
										
										
											2012-03-07 12:43:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Disable parallelism if printing | 
					
						
							|  |  |  | 	if *verbose { | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		*numParallel = 1 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ratec = make(chan bool, *numParallel) | 
					
						
							|  |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2012-03-06 03:34:53 +08:00
										 |  |  | 	letter, err = build.ArchChar(build.Default.GOARCH) | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 	check(err) | 
					
						
							|  |  |  | 	gc = letter + "g" | 
					
						
							|  |  |  | 	ld = letter + "l" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var tests []*test | 
					
						
							|  |  |  | 	if flag.NArg() > 0 { | 
					
						
							|  |  |  | 		for _, arg := range flag.Args() { | 
					
						
							|  |  |  | 			if arg == "-" || arg == "--" { | 
					
						
							|  |  |  | 				// Permit running either: | 
					
						
							|  |  |  | 				// $ go run run.go - env.go | 
					
						
							|  |  |  | 				// $ go run run.go -- env.go | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if !strings.HasSuffix(arg, ".go") { | 
					
						
							|  |  |  | 				log.Fatalf("can't yet deal with non-go file %q", arg) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			dir, file := filepath.Split(arg) | 
					
						
							|  |  |  | 			tests = append(tests, startTest(dir, file)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		for _, dir := range dirs { | 
					
						
							|  |  |  | 			for _, baseGoFile := range goFiles(dir) { | 
					
						
							|  |  |  | 				tests = append(tests, startTest(dir, baseGoFile)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	failed := false | 
					
						
							|  |  |  | 	resCount := map[string]int{} | 
					
						
							|  |  |  | 	for _, test := range tests { | 
					
						
							|  |  |  | 		<-test.donec | 
					
						
							|  |  |  | 		_, isSkip := test.err.(skipError) | 
					
						
							| 
									
										
										
										
											2012-02-24 12:52:15 +11:00
										 |  |  | 		errStr := "pass" | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		if isSkip { | 
					
						
							| 
									
										
										
										
											2012-02-24 12:52:15 +11:00
										 |  |  | 			errStr = "skip" | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if test.err != nil { | 
					
						
							|  |  |  | 			errStr = test.err.Error() | 
					
						
							|  |  |  | 			if !isSkip { | 
					
						
							|  |  |  | 				failed = true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		resCount[errStr]++ | 
					
						
							| 
									
										
										
										
											2012-02-24 12:52:15 +11:00
										 |  |  | 		if isSkip && !*verbose && !*showSkips { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		if !*verbose && test.err == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2012-02-24 12:52:15 +11:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		fmt.Printf("%-10s %-20s: %s\n", test.action, test.goFileName(), errStr) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if *summary { | 
					
						
							|  |  |  | 		for k, v := range resCount { | 
					
						
							|  |  |  | 			fmt.Printf("%5d %s\n", v, k) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if failed { | 
					
						
							|  |  |  | 		os.Exit(1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func toolPath(name string) string { | 
					
						
							|  |  |  | 	p := filepath.Join(os.Getenv("GOROOT"), "bin", "tool", name) | 
					
						
							|  |  |  | 	if _, err := os.Stat(p); err != nil { | 
					
						
							|  |  |  | 		log.Fatalf("didn't find binary at %s", p) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return p | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func goFiles(dir string) []string { | 
					
						
							|  |  |  | 	f, err := os.Open(dir) | 
					
						
							|  |  |  | 	check(err) | 
					
						
							|  |  |  | 	dirnames, err := f.Readdirnames(-1) | 
					
						
							|  |  |  | 	check(err) | 
					
						
							|  |  |  | 	names := []string{} | 
					
						
							|  |  |  | 	for _, name := range dirnames { | 
					
						
							| 
									
										
										
										
											2012-03-08 14:03:40 -05:00
										 |  |  | 		if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") { | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 			names = append(names, name) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sort.Strings(names) | 
					
						
							|  |  |  | 	return names | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // skipError describes why a test was skipped. | 
					
						
							|  |  |  | type skipError string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s skipError) Error() string { return string(s) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func check(err error) { | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		log.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // test holds the state of a test. | 
					
						
							|  |  |  | type test struct { | 
					
						
							|  |  |  | 	dir, gofile string | 
					
						
							|  |  |  | 	donec       chan bool // closed when done | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	src    string | 
					
						
							| 
									
										
										
										
											2012-04-20 23:45:43 +08:00
										 |  |  | 	action string // "compile", "build", "run", "errorcheck", "skip", "runoutput" | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	tempDir string | 
					
						
							|  |  |  | 	err     error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // startTest  | 
					
						
							|  |  |  | func startTest(dir, gofile string) *test { | 
					
						
							|  |  |  | 	t := &test{ | 
					
						
							|  |  |  | 		dir:    dir, | 
					
						
							|  |  |  | 		gofile: gofile, | 
					
						
							|  |  |  | 		donec:  make(chan bool, 1), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if toRun == nil { | 
					
						
							|  |  |  | 		toRun = make(chan *test, maxTests) | 
					
						
							|  |  |  | 		go runTests() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case toRun <- t: | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		panic("toRun buffer size (maxTests) is too small") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return t | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // runTests runs tests in parallel, but respecting the order they | 
					
						
							|  |  |  | // were enqueued on the toRun channel. | 
					
						
							|  |  |  | func runTests() { | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		ratec <- true | 
					
						
							|  |  |  | 		t := <-toRun | 
					
						
							|  |  |  | 		go func() { | 
					
						
							|  |  |  | 			t.run() | 
					
						
							|  |  |  | 			<-ratec | 
					
						
							|  |  |  | 		}() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | var cwd, _ = os.Getwd() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | func (t *test) goFileName() string { | 
					
						
							|  |  |  | 	return filepath.Join(t.dir, t.gofile) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-30 21:12:05 +02:00
										 |  |  | func (t *test) goDirName() string { | 
					
						
							|  |  |  | 	return filepath.Join(t.dir, strings.Replace(t.gofile, ".go", ".dir", -1)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | // run runs a test. | 
					
						
							|  |  |  | func (t *test) run() { | 
					
						
							|  |  |  | 	defer close(t.donec) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	srcBytes, err := ioutil.ReadFile(t.goFileName()) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.err = err | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	t.src = string(srcBytes) | 
					
						
							|  |  |  | 	if t.src[0] == '\n' { | 
					
						
							|  |  |  | 		t.err = skipError("starts with newline") | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pos := strings.Index(t.src, "\n\n") | 
					
						
							|  |  |  | 	if pos == -1 { | 
					
						
							|  |  |  | 		t.err = errors.New("double newline not found") | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	action := t.src[:pos] | 
					
						
							|  |  |  | 	if strings.HasPrefix(action, "//") { | 
					
						
							|  |  |  | 		action = action[2:] | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-03-08 14:03:40 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 	var args []string | 
					
						
							|  |  |  | 	f := strings.Fields(action) | 
					
						
							|  |  |  | 	if len(f) > 0 { | 
					
						
							|  |  |  | 		action = f[0] | 
					
						
							|  |  |  | 		args = f[1:] | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch action { | 
					
						
							| 
									
										
										
										
											2012-02-24 13:17:26 +11:00
										 |  |  | 	case "cmpout": | 
					
						
							|  |  |  | 		action = "run" // the run case already looks for <dir>/<test>.out files | 
					
						
							|  |  |  | 		fallthrough | 
					
						
							| 
									
										
										
										
											2012-07-30 21:12:05 +02:00
										 |  |  | 	case "compile", "compiledir", "build", "run", "errorcheck", "runoutput": | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		t.action = action | 
					
						
							| 
									
										
										
										
											2012-03-22 02:14:44 +08:00
										 |  |  | 	case "skip": | 
					
						
							|  |  |  | 		t.action = "skip" | 
					
						
							|  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		t.err = skipError("skipped; unknown pattern: " + action) | 
					
						
							|  |  |  | 		t.action = "??" | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.makeTempDir() | 
					
						
							|  |  |  | 	defer os.RemoveAll(t.tempDir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644) | 
					
						
							|  |  |  | 	check(err) | 
					
						
							| 
									
										
										
										
											2012-03-08 14:03:40 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-07 02:22:08 -05:00
										 |  |  | 	// A few tests (of things like the environment) require these to be set. | 
					
						
							|  |  |  | 	os.Setenv("GOOS", runtime.GOOS) | 
					
						
							|  |  |  | 	os.Setenv("GOARCH", runtime.GOARCH) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 	useTmp := true | 
					
						
							|  |  |  | 	runcmd := func(args ...string) ([]byte, error) { | 
					
						
							|  |  |  | 		cmd := exec.Command(args[0], args[1:]...) | 
					
						
							|  |  |  | 		var buf bytes.Buffer | 
					
						
							|  |  |  | 		cmd.Stdout = &buf | 
					
						
							|  |  |  | 		cmd.Stderr = &buf | 
					
						
							|  |  |  | 		if useTmp { | 
					
						
							|  |  |  | 			cmd.Dir = t.tempDir | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		err := cmd.Run() | 
					
						
							|  |  |  | 		return buf.Bytes(), err | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 	long := filepath.Join(cwd, t.goFileName()) | 
					
						
							| 
									
										
										
										
											2012-03-08 14:03:40 -05:00
										 |  |  | 	switch action { | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		t.err = fmt.Errorf("unimplemented action %q", action) | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 	case "errorcheck": | 
					
						
							|  |  |  | 		out, _ := runcmd("go", "tool", gc, "-e", "-o", "a."+letter, long) | 
					
						
							|  |  |  | 		t.err = t.errorCheck(string(out), long, t.gofile) | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		return | 
					
						
							| 
									
										
										
										
											2012-03-08 14:03:40 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 	case "compile": | 
					
						
							|  |  |  | 		out, err := runcmd("go", "tool", gc, "-e", "-o", "a."+letter, long) | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 			t.err = fmt.Errorf("%s\n%s", err, out) | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-03-08 14:03:40 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-30 21:12:05 +02:00
										 |  |  | 	case "compiledir": | 
					
						
							|  |  |  | 		// Compile all files in the directory in lexicographic order. | 
					
						
							|  |  |  | 		longdir := filepath.Join(cwd, t.goDirName()) | 
					
						
							|  |  |  | 		files, dirErr := ioutil.ReadDir(longdir) | 
					
						
							|  |  |  | 		if dirErr != nil { | 
					
						
							|  |  |  | 			t.err = dirErr | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for _, gofile := range files { | 
					
						
							|  |  |  | 			afile := strings.Replace(gofile.Name(), ".go", "."+letter, -1) | 
					
						
							|  |  |  | 			out, err := runcmd("go", "tool", gc, "-e", "-o", afile, filepath.Join(longdir, gofile.Name())) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				t.err = fmt.Errorf("%s\n%s", err, out) | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 	case "build": | 
					
						
							|  |  |  | 		out, err := runcmd("go", "build", "-o", "a.exe", long) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.err = fmt.Errorf("%s\n%s", err, out) | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-03-08 14:03:40 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 	case "run": | 
					
						
							|  |  |  | 		useTmp = false | 
					
						
							|  |  |  | 		out, err := runcmd(append([]string{"go", "run", t.goFileName()}, args...)...) | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 			t.err = fmt.Errorf("%s\n%s", err, out) | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 		if string(out) != t.expectedOutput() { | 
					
						
							|  |  |  | 			t.err = fmt.Errorf("incorrect output\n%s", out) | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-04-20 23:45:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	case "runoutput": | 
					
						
							|  |  |  | 		useTmp = false | 
					
						
							|  |  |  | 		out, err := runcmd("go", "run", t.goFileName()) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.err = fmt.Errorf("%s\n%s", err, out) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		tfile := filepath.Join(t.tempDir, "tmp__.go") | 
					
						
							|  |  |  | 		err = ioutil.WriteFile(tfile, out, 0666) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.err = fmt.Errorf("write tempfile:%s", err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		out, err = runcmd("go", "run", tfile) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.err = fmt.Errorf("%s\n%s", err, out) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if string(out) != t.expectedOutput() { | 
					
						
							|  |  |  | 			t.err = fmt.Errorf("incorrect output\n%s", out) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *test) String() string { | 
					
						
							|  |  |  | 	return filepath.Join(t.dir, t.gofile) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *test) makeTempDir() { | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	t.tempDir, err = ioutil.TempDir("", "") | 
					
						
							|  |  |  | 	check(err) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *test) expectedOutput() string { | 
					
						
							|  |  |  | 	filename := filepath.Join(t.dir, t.gofile) | 
					
						
							|  |  |  | 	filename = filename[:len(filename)-len(".go")] | 
					
						
							|  |  |  | 	filename += ".out" | 
					
						
							|  |  |  | 	b, _ := ioutil.ReadFile(filename) | 
					
						
							|  |  |  | 	return string(b) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | func (t *test) errorCheck(outStr string, full, short string) (err error) { | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if *verbose && err != nil { | 
					
						
							|  |  |  | 			log.Printf("%s gc output:\n%s", t, outStr) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	var errs []error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var out []string | 
					
						
							|  |  |  | 	// 6g error messages continue onto additional lines with leading tabs. | 
					
						
							|  |  |  | 	// Split the output at the beginning of each line that doesn't begin with a tab. | 
					
						
							|  |  |  | 	for _, line := range strings.Split(outStr, "\n") { | 
					
						
							|  |  |  | 		if strings.HasPrefix(line, "\t") { | 
					
						
							|  |  |  | 			out[len(out)-1] += "\n" + line | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			out = append(out, line) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 	// Cut directory name. | 
					
						
							|  |  |  | 	for i := range out { | 
					
						
							|  |  |  | 		out[i] = strings.Replace(out[i], full, short, -1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 	for _, we := range t.wantedErrors() { | 
					
						
							|  |  |  | 		var errmsgs []string | 
					
						
							|  |  |  | 		errmsgs, out = partitionStrings(we.filterRe, out) | 
					
						
							|  |  |  | 		if len(errmsgs) == 0 { | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 			errs = append(errs, fmt.Errorf("%s:%d: missing error %q", we.file, we.lineNum, we.reStr)) | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		matched := false | 
					
						
							|  |  |  | 		for _, errmsg := range errmsgs { | 
					
						
							|  |  |  | 			if we.re.MatchString(errmsg) { | 
					
						
							|  |  |  | 				matched = true | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				out = append(out, errmsg) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !matched { | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 			errs = append(errs, fmt.Errorf("%s:%d: no match for %q in%s", we.file, we.lineNum, we.reStr, strings.Join(out, "\n"))) | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(errs) == 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(errs) == 1 { | 
					
						
							|  |  |  | 		return errs[0] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var buf bytes.Buffer | 
					
						
							| 
									
										
										
										
											2012-03-07 01:54:39 -05:00
										 |  |  | 	fmt.Fprintf(&buf, "\n") | 
					
						
							| 
									
										
										
										
											2012-02-21 14:28:49 +11:00
										 |  |  | 	for _, err := range errs { | 
					
						
							|  |  |  | 		fmt.Fprintf(&buf, "%s\n", err.Error()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return errors.New(buf.String()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func partitionStrings(rx *regexp.Regexp, strs []string) (matched, unmatched []string) { | 
					
						
							|  |  |  | 	for _, s := range strs { | 
					
						
							|  |  |  | 		if rx.MatchString(s) { | 
					
						
							|  |  |  | 			matched = append(matched, s) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			unmatched = append(unmatched, s) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type wantedError struct { | 
					
						
							|  |  |  | 	reStr    string | 
					
						
							|  |  |  | 	re       *regexp.Regexp | 
					
						
							|  |  |  | 	lineNum  int | 
					
						
							|  |  |  | 	file     string | 
					
						
							|  |  |  | 	filterRe *regexp.Regexp // /^file:linenum\b/m | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	errRx       = regexp.MustCompile(`// (?:GC_)?ERROR (.*)`) | 
					
						
							|  |  |  | 	errQuotesRx = regexp.MustCompile(`"([^"]*)"`) | 
					
						
							|  |  |  | 	lineRx      = regexp.MustCompile(`LINE(([+-])([0-9]+))?`) | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *test) wantedErrors() (errs []wantedError) { | 
					
						
							|  |  |  | 	for i, line := range strings.Split(t.src, "\n") { | 
					
						
							|  |  |  | 		lineNum := i + 1 | 
					
						
							|  |  |  | 		if strings.Contains(line, "////") { | 
					
						
							|  |  |  | 			// double comment disables ERROR | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		m := errRx.FindStringSubmatch(line) | 
					
						
							|  |  |  | 		if m == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		all := m[1] | 
					
						
							|  |  |  | 		mm := errQuotesRx.FindAllStringSubmatch(all, -1) | 
					
						
							|  |  |  | 		if mm == nil { | 
					
						
							|  |  |  | 			log.Fatalf("invalid errchk line in %s: %s", t.goFileName(), line) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for _, m := range mm { | 
					
						
							|  |  |  | 			rx := lineRx.ReplaceAllStringFunc(m[1], func(m string) string { | 
					
						
							|  |  |  | 				n := lineNum | 
					
						
							|  |  |  | 				if strings.HasPrefix(m, "LINE+") { | 
					
						
							|  |  |  | 					delta, _ := strconv.Atoi(m[5:]) | 
					
						
							|  |  |  | 					n += delta | 
					
						
							|  |  |  | 				} else if strings.HasPrefix(m, "LINE-") { | 
					
						
							|  |  |  | 					delta, _ := strconv.Atoi(m[5:]) | 
					
						
							|  |  |  | 					n -= delta | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return fmt.Sprintf("%s:%d", t.gofile, n) | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			filterPattern := fmt.Sprintf(`^(\w+/)?%s:%d[:[]`, t.gofile, lineNum) | 
					
						
							|  |  |  | 			errs = append(errs, wantedError{ | 
					
						
							|  |  |  | 				reStr:    rx, | 
					
						
							|  |  |  | 				re:       regexp.MustCompile(rx), | 
					
						
							|  |  |  | 				filterRe: regexp.MustCompile(filterPattern), | 
					
						
							|  |  |  | 				lineNum:  lineNum, | 
					
						
							|  |  |  | 				file:     t.gofile, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } |