github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/go/types/stdlib_test.go (about) 1 // Copyright 2013 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 // This file tests types.Check by using it to 6 // typecheck the standard library and tests. 7 8 package types_test 9 10 import ( 11 "fmt" 12 "go/ast" 13 "go/build" 14 "go/importer" 15 "go/parser" 16 "go/scanner" 17 "go/token" 18 "io/ioutil" 19 "os" 20 "path/filepath" 21 "runtime" 22 "strings" 23 "testing" 24 "time" 25 26 . "go/types" 27 ) 28 29 var ( 30 pkgCount int // number of packages processed 31 start time.Time 32 33 // Use the same importer for all std lib tests to 34 // avoid repeated importing of the same packages. 35 stdLibImporter = importer.Default() 36 ) 37 38 func TestStdlib(t *testing.T) { 39 skipSpecialPlatforms(t) 40 41 start = time.Now() 42 walkDirs(t, filepath.Join(runtime.GOROOT(), "src")) 43 if testing.Verbose() { 44 fmt.Println(pkgCount, "packages typechecked in", time.Since(start)) 45 } 46 } 47 48 // firstComment returns the contents of the first comment in 49 // the given file, assuming there's one within the first KB. 50 func firstComment(filename string) string { 51 f, err := os.Open(filename) 52 if err != nil { 53 return "" 54 } 55 defer f.Close() 56 57 var src [1 << 10]byte // read at most 1KB 58 n, _ := f.Read(src[:]) 59 60 var s scanner.Scanner 61 s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil, scanner.ScanComments) 62 for { 63 _, tok, lit := s.Scan() 64 switch tok { 65 case token.COMMENT: 66 // remove trailing */ of multi-line comment 67 if lit[1] == '*' { 68 lit = lit[:len(lit)-2] 69 } 70 return strings.TrimSpace(lit[2:]) 71 case token.EOF: 72 return "" 73 } 74 } 75 } 76 77 func testTestDir(t *testing.T, path string, ignore ...string) { 78 files, err := ioutil.ReadDir(path) 79 if err != nil { 80 t.Fatal(err) 81 } 82 83 excluded := make(map[string]bool) 84 for _, filename := range ignore { 85 excluded[filename] = true 86 } 87 88 fset := token.NewFileSet() 89 for _, f := range files { 90 // filter directory contents 91 if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] { 92 continue 93 } 94 95 // get per-file instructions 96 expectErrors := false 97 filename := filepath.Join(path, f.Name()) 98 if cmd := firstComment(filename); cmd != "" { 99 switch cmd { 100 case "skip", "compiledir": 101 continue // ignore this file 102 case "errorcheck": 103 expectErrors = true 104 } 105 } 106 107 // parse and type-check file 108 file, err := parser.ParseFile(fset, filename, nil, 0) 109 if err == nil { 110 conf := Config{Importer: stdLibImporter} 111 _, err = conf.Check(filename, fset, []*ast.File{file}, nil) 112 } 113 114 if expectErrors { 115 if err == nil { 116 t.Errorf("expected errors but found none in %s", filename) 117 } 118 } else { 119 if err != nil { 120 t.Error(err) 121 } 122 } 123 } 124 } 125 126 func TestStdTest(t *testing.T) { 127 skipSpecialPlatforms(t) 128 129 // test/recover4.go is only built for Linux and Darwin. 130 // TODO(gri) Remove once tests consider +build tags (issue 10370). 131 if runtime.GOOS != "linux" || runtime.GOOS != "darwin" { 132 return 133 } 134 135 testTestDir(t, filepath.Join(runtime.GOROOT(), "test"), 136 "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore 137 "sigchld.go", // don't work on Windows; testTestDir should consult build tags 138 ) 139 } 140 141 func TestStdFixed(t *testing.T) { 142 skipSpecialPlatforms(t) 143 144 testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"), 145 "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore 146 "bug459.go", // possibly incorrect test - see issue 6703 (pending spec clarification) 147 "issue3924.go", // possibly incorrect test - see issue 6671 (pending spec clarification) 148 "issue6889.go", // gc-specific test 149 ) 150 } 151 152 func TestStdKen(t *testing.T) { 153 skipSpecialPlatforms(t) 154 155 testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken")) 156 } 157 158 // Package paths of excluded packages. 159 var excluded = map[string]bool{ 160 "builtin": true, 161 } 162 163 // typecheck typechecks the given package files. 164 func typecheck(t *testing.T, path string, filenames []string) { 165 fset := token.NewFileSet() 166 167 // parse package files 168 var files []*ast.File 169 for _, filename := range filenames { 170 file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors) 171 if err != nil { 172 // the parser error may be a list of individual errors; report them all 173 if list, ok := err.(scanner.ErrorList); ok { 174 for _, err := range list { 175 t.Error(err) 176 } 177 return 178 } 179 t.Error(err) 180 return 181 } 182 183 if testing.Verbose() { 184 if len(files) == 0 { 185 fmt.Println("package", file.Name.Name) 186 } 187 fmt.Println("\t", filename) 188 } 189 190 files = append(files, file) 191 } 192 193 // typecheck package files 194 conf := Config{ 195 Error: func(err error) { t.Error(err) }, 196 Importer: stdLibImporter, 197 } 198 info := Info{Uses: make(map[*ast.Ident]Object)} 199 conf.Check(path, fset, files, &info) 200 pkgCount++ 201 202 // Perform checks of API invariants. 203 204 // All Objects have a package, except predeclared ones. 205 errorError := Universe.Lookup("error").Type().Underlying().(*Interface).ExplicitMethod(0) // (error).Error 206 for id, obj := range info.Uses { 207 predeclared := obj == Universe.Lookup(obj.Name()) || obj == errorError 208 if predeclared == (obj.Pkg() != nil) { 209 posn := fset.Position(id.Pos()) 210 if predeclared { 211 t.Errorf("%s: predeclared object with package: %s", posn, obj) 212 } else { 213 t.Errorf("%s: user-defined object without package: %s", posn, obj) 214 } 215 } 216 } 217 } 218 219 // pkgFilenames returns the list of package filenames for the given directory. 220 func pkgFilenames(dir string) ([]string, error) { 221 ctxt := build.Default 222 ctxt.CgoEnabled = false 223 pkg, err := ctxt.ImportDir(dir, 0) 224 if err != nil { 225 if _, nogo := err.(*build.NoGoError); nogo { 226 return nil, nil // no *.go files, not an error 227 } 228 return nil, err 229 } 230 if excluded[pkg.ImportPath] { 231 return nil, nil 232 } 233 var filenames []string 234 for _, name := range pkg.GoFiles { 235 filenames = append(filenames, filepath.Join(pkg.Dir, name)) 236 } 237 for _, name := range pkg.TestGoFiles { 238 filenames = append(filenames, filepath.Join(pkg.Dir, name)) 239 } 240 return filenames, nil 241 } 242 243 // Note: Could use filepath.Walk instead of walkDirs but that wouldn't 244 // necessarily be shorter or clearer after adding the code to 245 // terminate early for -short tests. 246 247 func walkDirs(t *testing.T, dir string) { 248 // limit run time for short tests 249 if testing.Short() && time.Since(start) >= 750*time.Millisecond { 250 return 251 } 252 253 fis, err := ioutil.ReadDir(dir) 254 if err != nil { 255 t.Error(err) 256 return 257 } 258 259 // typecheck package in directory 260 files, err := pkgFilenames(dir) 261 if err != nil { 262 t.Error(err) 263 return 264 } 265 if files != nil { 266 typecheck(t, dir, files) 267 } 268 269 // traverse subdirectories, but don't walk into testdata 270 for _, fi := range fis { 271 if fi.IsDir() && fi.Name() != "testdata" { 272 walkDirs(t, filepath.Join(dir, fi.Name())) 273 } 274 } 275 }