github.com/epfl-dcsl/gotee@v0.0.0-20200909122901-014b35f5e5e9/src/cmd/cover/cover_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 package main_test 6 7 import ( 8 "bytes" 9 "flag" 10 "fmt" 11 "go/ast" 12 "go/parser" 13 "go/token" 14 "internal/testenv" 15 "io/ioutil" 16 "os" 17 "os/exec" 18 "path/filepath" 19 "regexp" 20 "strings" 21 "testing" 22 ) 23 24 const ( 25 // Data directory, also the package directory for the test. 26 testdata = "testdata" 27 28 // Binaries we compile. 29 testcover = "./testcover.exe" 30 ) 31 32 var ( 33 // Files we use. 34 testMain = filepath.Join(testdata, "main.go") 35 testTest = filepath.Join(testdata, "test.go") 36 coverInput = filepath.Join(testdata, "test_line.go") 37 coverOutput = filepath.Join(testdata, "test_cover.go") 38 coverProfile = filepath.Join(testdata, "profile.cov") 39 ) 40 41 var debug = flag.Bool("debug", false, "keep rewritten files for debugging") 42 43 // Run this shell script, but do it in Go so it can be run by "go test". 44 // 45 // replace the word LINE with the line number < testdata/test.go > testdata/test_line.go 46 // go build -o ./testcover 47 // ./testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go 48 // go run ./testdata/main.go ./testdata/test.go 49 // 50 func TestCover(t *testing.T) { 51 testenv.MustHaveGoBuild(t) 52 53 // Read in the test file (testTest) and write it, with LINEs specified, to coverInput. 54 file, err := ioutil.ReadFile(testTest) 55 if err != nil { 56 t.Fatal(err) 57 } 58 lines := bytes.Split(file, []byte("\n")) 59 for i, line := range lines { 60 lines[i] = bytes.Replace(line, []byte("LINE"), []byte(fmt.Sprint(i+1)), -1) 61 } 62 63 // Add a function that is not gofmt'ed. This used to cause a crash. 64 // We don't put it in test.go because then we would have to gofmt it. 65 // Issue 23927. 66 lines = append(lines, []byte("func unFormatted() {"), 67 []byte("\tif true {"), 68 []byte("\t}else{"), 69 []byte("\t}"), 70 []byte("}")) 71 lines = append(lines, []byte("func unFormatted2(b bool) {if b{}else{}}")) 72 73 if err := ioutil.WriteFile(coverInput, bytes.Join(lines, []byte("\n")), 0666); err != nil { 74 t.Fatal(err) 75 } 76 77 // defer removal of test_line.go 78 if !*debug { 79 defer os.Remove(coverInput) 80 } 81 82 // go build -o testcover 83 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", testcover) 84 run(cmd, t) 85 86 // defer removal of testcover 87 defer os.Remove(testcover) 88 89 // ./testcover -mode=count -var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest -o ./testdata/test_cover.go testdata/test_line.go 90 cmd = exec.Command(testcover, "-mode=count", "-var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest", "-o", coverOutput, coverInput) 91 run(cmd, t) 92 93 // defer removal of ./testdata/test_cover.go 94 if !*debug { 95 defer os.Remove(coverOutput) 96 } 97 98 // go run ./testdata/main.go ./testdata/test.go 99 cmd = exec.Command(testenv.GoToolPath(t), "run", testMain, coverOutput) 100 run(cmd, t) 101 102 file, err = ioutil.ReadFile(coverOutput) 103 if err != nil { 104 t.Fatal(err) 105 } 106 // compiler directive must appear right next to function declaration. 107 if got, err := regexp.MatchString(".*\n//go:nosplit\nfunc someFunction().*", string(file)); err != nil || !got { 108 t.Error("misplaced compiler directive") 109 } 110 // "go:linkname" compiler directive should be present. 111 if got, err := regexp.MatchString(`.*go\:linkname some\_name some\_name.*`, string(file)); err != nil || !got { 112 t.Error("'go:linkname' compiler directive not found") 113 } 114 115 // Other comments should be preserved too. 116 c := ".*// This comment didn't appear in generated go code.*" 117 if got, err := regexp.MatchString(c, string(file)); err != nil || !got { 118 t.Errorf("non compiler directive comment %q not found", c) 119 } 120 } 121 122 // TestDirectives checks that compiler directives are preserved and positioned 123 // correctly. Directives that occur before top-level declarations should remain 124 // above those declarations, even if they are not part of the block of 125 // documentation comments. 126 func TestDirectives(t *testing.T) { 127 // Read the source file and find all the directives. We'll keep 128 // track of whether each one has been seen in the output. 129 testDirectives := filepath.Join(testdata, "directives.go") 130 source, err := ioutil.ReadFile(testDirectives) 131 if err != nil { 132 t.Fatal(err) 133 } 134 sourceDirectives := findDirectives(source) 135 136 // go tool cover -mode=atomic ./testdata/directives.go 137 cmd := exec.Command(testenv.GoToolPath(t), "tool", "cover", "-mode=atomic", testDirectives) 138 cmd.Stderr = os.Stderr 139 output, err := cmd.Output() 140 if err != nil { 141 t.Fatal(err) 142 } 143 144 // Check that all directives are present in the output. 145 outputDirectives := findDirectives(output) 146 foundDirective := make(map[string]bool) 147 for _, p := range sourceDirectives { 148 foundDirective[p.name] = false 149 } 150 for _, p := range outputDirectives { 151 if found, ok := foundDirective[p.name]; !ok { 152 t.Errorf("unexpected directive in output: %s", p.text) 153 } else if found { 154 t.Errorf("directive found multiple times in output: %s", p.text) 155 } 156 foundDirective[p.name] = true 157 } 158 for name, found := range foundDirective { 159 if !found { 160 t.Errorf("missing directive: %s", name) 161 } 162 } 163 164 // Check that directives that start with the name of top-level declarations 165 // come before the beginning of the named declaration and after the end 166 // of the previous declaration. 167 fset := token.NewFileSet() 168 astFile, err := parser.ParseFile(fset, testDirectives, output, 0) 169 if err != nil { 170 t.Fatal(err) 171 } 172 173 prevEnd := 0 174 for _, decl := range astFile.Decls { 175 var name string 176 switch d := decl.(type) { 177 case *ast.FuncDecl: 178 name = d.Name.Name 179 case *ast.GenDecl: 180 if len(d.Specs) == 0 { 181 // An empty group declaration. We still want to check that 182 // directives can be associated with it, so we make up a name 183 // to match directives in the test data. 184 name = "_empty" 185 } else if spec, ok := d.Specs[0].(*ast.TypeSpec); ok { 186 name = spec.Name.Name 187 } 188 } 189 pos := fset.Position(decl.Pos()).Offset 190 end := fset.Position(decl.End()).Offset 191 if name == "" { 192 prevEnd = end 193 continue 194 } 195 for _, p := range outputDirectives { 196 if !strings.HasPrefix(p.name, name) { 197 continue 198 } 199 if p.offset < prevEnd || pos < p.offset { 200 t.Errorf("directive %s does not appear before definition %s", p.text, name) 201 } 202 } 203 prevEnd = end 204 } 205 } 206 207 type directiveInfo struct { 208 text string // full text of the comment, not including newline 209 name string // text after //go: 210 offset int // byte offset of first slash in comment 211 } 212 213 func findDirectives(source []byte) []directiveInfo { 214 var directives []directiveInfo 215 directivePrefix := []byte("\n//go:") 216 offset := 0 217 for { 218 i := bytes.Index(source[offset:], directivePrefix) 219 if i < 0 { 220 break 221 } 222 i++ // skip newline 223 p := source[offset+i:] 224 j := bytes.IndexByte(p, '\n') 225 if j < 0 { 226 // reached EOF 227 j = len(p) 228 } 229 directive := directiveInfo{ 230 text: string(p[:j]), 231 name: string(p[len(directivePrefix)-1 : j]), 232 offset: offset + i, 233 } 234 directives = append(directives, directive) 235 offset += i + j 236 } 237 return directives 238 } 239 240 // Makes sure that `cover -func=profile.cov` reports accurate coverage. 241 // Issue #20515. 242 func TestCoverFunc(t *testing.T) { 243 // go tool cover -func ./testdata/profile.cov 244 cmd := exec.Command(testenv.GoToolPath(t), "tool", "cover", "-func", coverProfile) 245 out, err := cmd.Output() 246 if err != nil { 247 if ee, ok := err.(*exec.ExitError); ok { 248 t.Logf("%s", ee.Stderr) 249 } 250 t.Fatal(err) 251 } 252 253 if got, err := regexp.Match(".*total:.*100.0.*", out); err != nil || !got { 254 t.Logf("%s", out) 255 t.Errorf("invalid coverage counts. got=(%v, %v); want=(true; nil)", got, err) 256 } 257 } 258 259 func run(c *exec.Cmd, t *testing.T) { 260 t.Helper() 261 c.Stdout = os.Stdout 262 c.Stderr = os.Stderr 263 err := c.Run() 264 if err != nil { 265 t.Fatal(err) 266 } 267 }