summaryrefslogtreecommitdiff
path: root/libgo/go/go/typechecker/typechecker_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/go/typechecker/typechecker_test.go')
-rw-r--r--libgo/go/go/typechecker/typechecker_test.go168
1 files changed, 168 insertions, 0 deletions
diff --git a/libgo/go/go/typechecker/typechecker_test.go b/libgo/go/go/typechecker/typechecker_test.go
new file mode 100644
index 000000000..33f4a6223
--- /dev/null
+++ b/libgo/go/go/typechecker/typechecker_test.go
@@ -0,0 +1,168 @@
+// Copyright 2010 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.
+
+// This file implements a simple typechecker test harness. Packages found
+// in the testDir directory are typechecked. Error messages reported by
+// the typechecker are compared against the error messages expected for
+// the test files.
+//
+// Expected errors are indicated in the test files by putting a comment
+// of the form /* ERROR "rx" */ immediately following an offending token.
+// The harness will verify that an error matching the regular expression
+// rx is reported at that source position. Consecutive comments may be
+// used to indicate multiple errors for the same token position.
+//
+// For instance, the following test file indicates that a "not declared"
+// error should be reported for the undeclared variable x:
+//
+// package P0
+// func f() {
+// _ = x /* ERROR "not declared" */ + 1
+// }
+//
+// If the -pkg flag is set, only packages with package names matching
+// the regular expression provided via the flag value are tested.
+
+package typechecker
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "io/ioutil"
+ "os"
+ "regexp"
+ "sort"
+ "strings"
+ "testing"
+)
+
+
+const testDir = "./testdata" // location of test packages
+
+var fset = token.NewFileSet()
+
+var (
+ pkgPat = flag.String("pkg", ".*", "regular expression to select test packages by package name")
+ trace = flag.Bool("trace", false, "print package names")
+)
+
+
+// ERROR comments must be of the form /* ERROR "rx" */ and rx is
+// a regular expression that matches the expected error message.
+var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)
+
+// expectedErrors collects the regular expressions of ERROR comments
+// found in the package files of pkg and returns them in sorted order
+// (by filename and position).
+func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) {
+ // scan all package files
+ for filename := range pkg.Files {
+ src, err := ioutil.ReadFile(filename)
+ if err != nil {
+ t.Fatalf("expectedErrors(%s): %v", pkg.Name, err)
+ }
+
+ var s scanner.Scanner
+ file := fset.AddFile(filename, fset.Base(), len(src))
+ s.Init(file, src, nil, scanner.ScanComments)
+ var prev token.Pos // position of last non-comment token
+ loop:
+ for {
+ pos, tok, lit := s.Scan()
+ switch tok {
+ case token.EOF:
+ break loop
+ case token.COMMENT:
+ s := errRx.FindSubmatch(lit)
+ if len(s) == 2 {
+ list = append(list, &scanner.Error{fset.Position(prev), string(s[1])})
+ }
+ default:
+ prev = pos
+ }
+ }
+ }
+ sort.Sort(list) // multiple files may not be sorted
+ return
+}
+
+
+func testFilter(f *os.FileInfo) bool {
+ return strings.HasSuffix(f.Name, ".go") && f.Name[0] != '.'
+}
+
+
+func checkError(t *testing.T, expected, found *scanner.Error) {
+ rx, err := regexp.Compile(expected.Msg)
+ if err != nil {
+ t.Errorf("%s: %v", expected.Pos, err)
+ return
+ }
+
+ match := rx.MatchString(found.Msg)
+
+ if expected.Pos.Offset != found.Pos.Offset {
+ if match {
+ t.Errorf("%s: expected error should have been at %s", expected.Pos, found.Pos)
+ } else {
+ t.Errorf("%s: error matching %q expected", expected.Pos, expected.Msg)
+ return
+ }
+ }
+
+ if !match {
+ t.Errorf("%s: %q does not match %q", expected.Pos, expected.Msg, found.Msg)
+ }
+}
+
+
+func TestTypeCheck(t *testing.T) {
+ flag.Parse()
+ pkgRx, err := regexp.Compile(*pkgPat)
+ if err != nil {
+ t.Fatalf("illegal flag value %q: %s", *pkgPat, err)
+ }
+
+ pkgs, err := parser.ParseDir(fset, testDir, testFilter, 0)
+ if err != nil {
+ scanner.PrintError(os.Stderr, err)
+ t.Fatalf("packages in %s contain syntax errors", testDir)
+ }
+
+ for _, pkg := range pkgs {
+ if !pkgRx.MatchString(pkg.Name) {
+ continue // only test selected packages
+ }
+
+ if *trace {
+ fmt.Println(pkg.Name)
+ }
+
+ xlist := expectedErrors(t, pkg)
+ err := CheckPackage(fset, pkg, nil)
+ if err != nil {
+ if elist, ok := err.(scanner.ErrorList); ok {
+ // verify that errors match
+ for i := 0; i < len(xlist) && i < len(elist); i++ {
+ checkError(t, xlist[i], elist[i])
+ }
+ // the correct number or errors must have been found
+ if len(xlist) != len(elist) {
+ fmt.Fprintf(os.Stderr, "%s\n", pkg.Name)
+ scanner.PrintError(os.Stderr, elist)
+ fmt.Fprintln(os.Stderr)
+ t.Errorf("TypeCheck(%s): %d errors expected but %d reported", pkg.Name, len(xlist), len(elist))
+ }
+ } else {
+ t.Errorf("TypeCheck(%s): %v", pkg.Name, err)
+ }
+ } else if len(xlist) > 0 {
+ t.Errorf("TypeCheck(%s): %d errors expected but 0 reported", pkg.Name, len(xlist))
+ }
+ }
+}