PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/pkg/go/typechecker/typechecker_test.go

http://github.com/ssrl/go
Go | 168 lines | 112 code | 24 blank | 32 comment | 33 complexity | ece4a702e2e8ec645b5fdd821fe51fba MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright 2010 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. // This file implements a simple typechecker test harness. Packages found
  5. // in the testDir directory are typechecked. Error messages reported by
  6. // the typechecker are compared against the error messages expected for
  7. // the test files.
  8. //
  9. // Expected errors are indicated in the test files by putting a comment
  10. // of the form /* ERROR "rx" */ immediately following an offending token.
  11. // The harness will verify that an error matching the regular expression
  12. // rx is reported at that source position. Consecutive comments may be
  13. // used to indicate multiple errors for the same token position.
  14. //
  15. // For instance, the following test file indicates that a "not declared"
  16. // error should be reported for the undeclared variable x:
  17. //
  18. // package P0
  19. // func f() {
  20. // _ = x /* ERROR "not declared" */ + 1
  21. // }
  22. //
  23. // If the -pkg flag is set, only packages with package names matching
  24. // the regular expression provided via the flag value are tested.
  25. package typechecker
  26. import (
  27. "flag"
  28. "fmt"
  29. "go/ast"
  30. "go/parser"
  31. "go/scanner"
  32. "go/token"
  33. "io/ioutil"
  34. "os"
  35. "regexp"
  36. "sort"
  37. "strings"
  38. "testing"
  39. )
  40. const testDir = "./testdata" // location of test packages
  41. var fset = token.NewFileSet()
  42. var (
  43. pkgPat = flag.String("pkg", ".*", "regular expression to select test packages by package name")
  44. trace = flag.Bool("trace", false, "print package names")
  45. )
  46. // ERROR comments must be of the form /* ERROR "rx" */ and rx is
  47. // a regular expression that matches the expected error message.
  48. var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)
  49. // expectedErrors collects the regular expressions of ERROR comments
  50. // found in the package files of pkg and returns them in sorted order
  51. // (by filename and position).
  52. func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) {
  53. // scan all package files
  54. for filename := range pkg.Files {
  55. src, err := ioutil.ReadFile(filename)
  56. if err != nil {
  57. t.Fatalf("expectedErrors(%s): %v", pkg.Name, err)
  58. }
  59. var s scanner.Scanner
  60. file := fset.AddFile(filename, fset.Base(), len(src))
  61. s.Init(file, src, nil, scanner.ScanComments)
  62. var prev token.Pos // position of last non-comment token
  63. loop:
  64. for {
  65. pos, tok, lit := s.Scan()
  66. switch tok {
  67. case token.EOF:
  68. break loop
  69. case token.COMMENT:
  70. s := errRx.FindStringSubmatch(lit)
  71. if len(s) == 2 {
  72. list = append(list, &scanner.Error{fset.Position(prev), string(s[1])})
  73. }
  74. default:
  75. prev = pos
  76. }
  77. }
  78. }
  79. sort.Sort(list) // multiple files may not be sorted
  80. return
  81. }
  82. func testFilter(f *os.FileInfo) bool {
  83. return strings.HasSuffix(f.Name, ".src") && f.Name[0] != '.'
  84. }
  85. func checkError(t *testing.T, expected, found *scanner.Error) {
  86. rx, err := regexp.Compile(expected.Msg)
  87. if err != nil {
  88. t.Errorf("%s: %v", expected.Pos, err)
  89. return
  90. }
  91. match := rx.MatchString(found.Msg)
  92. if expected.Pos.Offset != found.Pos.Offset {
  93. if match {
  94. t.Errorf("%s: expected error should have been at %s", expected.Pos, found.Pos)
  95. } else {
  96. t.Errorf("%s: error matching %q expected", expected.Pos, expected.Msg)
  97. return
  98. }
  99. }
  100. if !match {
  101. t.Errorf("%s: %q does not match %q", expected.Pos, expected.Msg, found.Msg)
  102. }
  103. }
  104. func TestTypeCheck(t *testing.T) {
  105. flag.Parse()
  106. pkgRx, err := regexp.Compile(*pkgPat)
  107. if err != nil {
  108. t.Fatalf("illegal flag value %q: %s", *pkgPat, err)
  109. }
  110. pkgs, err := parser.ParseDir(fset, testDir, testFilter, 0)
  111. if err != nil {
  112. scanner.PrintError(os.Stderr, err)
  113. t.Fatalf("packages in %s contain syntax errors", testDir)
  114. }
  115. for _, pkg := range pkgs {
  116. if !pkgRx.MatchString(pkg.Name) {
  117. continue // only test selected packages
  118. }
  119. if *trace {
  120. fmt.Println(pkg.Name)
  121. }
  122. xlist := expectedErrors(t, pkg)
  123. err := CheckPackage(fset, pkg, nil)
  124. if err != nil {
  125. if elist, ok := err.(scanner.ErrorList); ok {
  126. // verify that errors match
  127. for i := 0; i < len(xlist) && i < len(elist); i++ {
  128. checkError(t, xlist[i], elist[i])
  129. }
  130. // the correct number or errors must have been found
  131. if len(xlist) != len(elist) {
  132. fmt.Fprintf(os.Stderr, "%s\n", pkg.Name)
  133. scanner.PrintError(os.Stderr, elist)
  134. fmt.Fprintln(os.Stderr)
  135. t.Errorf("TypeCheck(%s): %d errors expected but %d reported", pkg.Name, len(xlist), len(elist))
  136. }
  137. } else {
  138. t.Errorf("TypeCheck(%s): %v", pkg.Name, err)
  139. }
  140. } else if len(xlist) > 0 {
  141. t.Errorf("TypeCheck(%s): %d errors expected but 0 reported", pkg.Name, len(xlist))
  142. }
  143. }
  144. }