Source file
src/cmd/cover/cover_test.go
1
2
3
4
5 package main_test
6
7 import (
8 "bufio"
9 "bytes"
10 cmdcover "cmd/cover"
11 "cmp"
12 "flag"
13 "fmt"
14 "go/ast"
15 "go/parser"
16 "go/token"
17 "internal/testenv"
18 "log"
19 "os"
20 "os/exec"
21 "path/filepath"
22 "regexp"
23 "slices"
24 "strconv"
25 "strings"
26 "sync"
27 "testing"
28 )
29
30 const (
31
32 testdata = "testdata"
33 )
34
35
36
37
38 func testcover(t testing.TB) string {
39 return testenv.Executable(t)
40 }
41
42
43 var testTempDir string
44
45
46 var debug = flag.Bool("debug", false, "keep tmpdir files for debugging")
47
48
49
50
51 func TestMain(m *testing.M) {
52 if os.Getenv("CMDCOVER_TOOLEXEC") != "" {
53
54
55 tool := strings.TrimSuffix(filepath.Base(os.Args[1]), ".exe")
56 if tool == "cover" {
57
58
59
60
61 os.Args = os.Args[1:]
62 cmdcover.Main()
63 } else {
64 cmd := exec.Command(os.Args[1], os.Args[2:]...)
65 cmd.Stdout = os.Stdout
66 cmd.Stderr = os.Stderr
67 if err := cmd.Run(); err != nil {
68 os.Exit(1)
69 }
70 }
71 os.Exit(0)
72 }
73 if os.Getenv("CMDCOVER_TEST_RUN_MAIN") != "" {
74
75
76
77
78 cmdcover.Main()
79 os.Exit(0)
80 }
81 flag.Parse()
82 topTmpdir, err := os.MkdirTemp("", "cmd-cover-test-")
83 if err != nil {
84 log.Fatal(err)
85 }
86 testTempDir = topTmpdir
87 if !*debug {
88 defer os.RemoveAll(topTmpdir)
89 } else {
90 fmt.Fprintf(os.Stderr, "debug: preserving tmpdir %s\n", topTmpdir)
91 }
92 os.Setenv("CMDCOVER_TEST_RUN_MAIN", "normal")
93 m.Run()
94 }
95
96 var tdmu sync.Mutex
97 var tdcount int
98
99 func tempDir(t *testing.T) string {
100 tdmu.Lock()
101 dir := filepath.Join(testTempDir, fmt.Sprintf("%03d", tdcount))
102 tdcount++
103 if err := os.Mkdir(dir, 0777); err != nil {
104 t.Fatal(err)
105 }
106 defer tdmu.Unlock()
107 return dir
108 }
109
110
111
112
113 func TestCoverWithToolExec(t *testing.T) {
114 toolexecArg := "-toolexec=" + testcover(t)
115
116 t.Run("CoverHTML", func(t *testing.T) {
117 testCoverHTML(t, toolexecArg)
118 })
119 t.Run("HtmlUnformatted", func(t *testing.T) {
120 testHtmlUnformatted(t, toolexecArg)
121 })
122 t.Run("FuncWithDuplicateLines", func(t *testing.T) {
123 testFuncWithDuplicateLines(t, toolexecArg)
124 })
125 t.Run("MissingTrailingNewlineIssue58370", func(t *testing.T) {
126 testMissingTrailingNewlineIssue58370(t, toolexecArg)
127 })
128 }
129
130
131
132
133
134
135 func TestCover(t *testing.T) {
136 testenv.MustHaveGoRun(t)
137 t.Parallel()
138 dir := tempDir(t)
139
140
141 testTest := filepath.Join(testdata, "test.go")
142 file, err := os.ReadFile(testTest)
143 if err != nil {
144 t.Fatal(err)
145 }
146 lines := bytes.Split(file, []byte("\n"))
147 for i, line := range lines {
148 lines[i] = bytes.ReplaceAll(line, []byte("LINE"), []byte(fmt.Sprint(i+1)))
149 }
150
151
152
153
154 lines = append(lines, []byte("func unFormatted() {"),
155 []byte("\tif true {"),
156 []byte("\t}else{"),
157 []byte("\t}"),
158 []byte("}"))
159 lines = append(lines, []byte("func unFormatted2(b bool) {if b{}else{}}"))
160
161 coverInput := filepath.Join(dir, "test_line.go")
162 if err := os.WriteFile(coverInput, bytes.Join(lines, []byte("\n")), 0666); err != nil {
163 t.Fatal(err)
164 }
165
166
167 coverOutput := filepath.Join(dir, "test_cover.go")
168 cmd := testenv.Command(t, testcover(t), "-mode=count", "-var=thisNameMustBeVeryLongToCauseOverflowOfCounterIncrementStatementOntoNextLineForTest", "-o", coverOutput, coverInput)
169 run(cmd, t)
170
171 cmd = testenv.Command(t, testcover(t), "-mode=set", "-var=Not_an-identifier", "-o", coverOutput, coverInput)
172 err = cmd.Run()
173 if err == nil {
174 t.Error("Expected cover to fail with an error")
175 }
176
177
178
179 testMain := filepath.Join(testdata, "main.go")
180 b, err := os.ReadFile(testMain)
181 if err != nil {
182 t.Fatal(err)
183 }
184 tmpTestMain := filepath.Join(dir, "main.go")
185 if err := os.WriteFile(tmpTestMain, b, 0444); err != nil {
186 t.Fatal(err)
187 }
188
189
190 cmd = testenv.Command(t, testenv.GoToolPath(t), "run", tmpTestMain, coverOutput)
191 run(cmd, t)
192
193 file, err = os.ReadFile(coverOutput)
194 if err != nil {
195 t.Fatal(err)
196 }
197
198 if got, err := regexp.MatchString(".*\n//go:nosplit\nfunc someFunction().*", string(file)); err != nil || !got {
199 t.Error("misplaced compiler directive")
200 }
201
202 if got, err := regexp.MatchString(`.*go\:linkname some\_name some\_name.*`, string(file)); err != nil || !got {
203 t.Error("'go:linkname' compiler directive not found")
204 }
205
206
207 c := ".*// This comment didn't appear in generated go code.*"
208 if got, err := regexp.MatchString(c, string(file)); err != nil || !got {
209 t.Errorf("non compiler directive comment %q not found", c)
210 }
211 }
212
213
214
215
216
217 func TestDirectives(t *testing.T) {
218 testenv.MustHaveExec(t)
219 t.Parallel()
220
221
222
223 testDirectives := filepath.Join(testdata, "directives.go")
224 source, err := os.ReadFile(testDirectives)
225 if err != nil {
226 t.Fatal(err)
227 }
228 sourceDirectives := findDirectives(source)
229
230
231 cmd := testenv.Command(t, testcover(t), "-mode=atomic", testDirectives)
232 cmd.Stderr = os.Stderr
233 output, err := cmd.Output()
234 if err != nil {
235 t.Fatal(err)
236 }
237
238
239 outputDirectives := findDirectives(output)
240 foundDirective := make(map[string]bool)
241 for _, p := range sourceDirectives {
242 foundDirective[p.name] = false
243 }
244 for _, p := range outputDirectives {
245 if found, ok := foundDirective[p.name]; !ok {
246 t.Errorf("unexpected directive in output: %s", p.text)
247 } else if found {
248 t.Errorf("directive found multiple times in output: %s", p.text)
249 }
250 foundDirective[p.name] = true
251 }
252 for name, found := range foundDirective {
253 if !found {
254 t.Errorf("missing directive: %s", name)
255 }
256 }
257
258
259
260
261 fset := token.NewFileSet()
262 astFile, err := parser.ParseFile(fset, testDirectives, output, 0)
263 if err != nil {
264 t.Fatal(err)
265 }
266
267 prevEnd := 0
268 for _, decl := range astFile.Decls {
269 var name string
270 switch d := decl.(type) {
271 case *ast.FuncDecl:
272 name = d.Name.Name
273 case *ast.GenDecl:
274 if len(d.Specs) == 0 {
275
276
277
278 name = "_empty"
279 } else if spec, ok := d.Specs[0].(*ast.TypeSpec); ok {
280 name = spec.Name.Name
281 }
282 }
283 pos := fset.Position(decl.Pos()).Offset
284 end := fset.Position(decl.End()).Offset
285 if name == "" {
286 prevEnd = end
287 continue
288 }
289 for _, p := range outputDirectives {
290 if !strings.HasPrefix(p.name, name) {
291 continue
292 }
293 if p.offset < prevEnd || pos < p.offset {
294 t.Errorf("directive %s does not appear before definition %s", p.text, name)
295 }
296 }
297 prevEnd = end
298 }
299 }
300
301 type directiveInfo struct {
302 text string
303 name string
304 offset int
305 }
306
307 func findDirectives(source []byte) []directiveInfo {
308 var directives []directiveInfo
309 directivePrefix := []byte("\n//go:")
310 offset := 0
311 for {
312 i := bytes.Index(source[offset:], directivePrefix)
313 if i < 0 {
314 break
315 }
316 i++
317 p := source[offset+i:]
318 j := bytes.IndexByte(p, '\n')
319 if j < 0 {
320
321 j = len(p)
322 }
323 directive := directiveInfo{
324 text: string(p[:j]),
325 name: string(p[len(directivePrefix)-1 : j]),
326 offset: offset + i,
327 }
328 directives = append(directives, directive)
329 offset += i + j
330 }
331 return directives
332 }
333
334
335
336 func TestCoverFunc(t *testing.T) {
337
338 coverProfile := filepath.Join(testdata, "profile.cov")
339 cmd := testenv.Command(t, testcover(t), "-func", coverProfile)
340 out, err := cmd.Output()
341 if err != nil {
342 if ee, ok := err.(*exec.ExitError); ok {
343 t.Logf("%s", ee.Stderr)
344 }
345 t.Fatal(err)
346 }
347
348 if got, err := regexp.Match(".*total:.*100.0.*", out); err != nil || !got {
349 t.Logf("%s", out)
350 t.Errorf("invalid coverage counts. got=(%v, %v); want=(true; nil)", got, err)
351 }
352 }
353
354
355
356 func testCoverHTML(t *testing.T, toolexecArg string) {
357 testenv.MustHaveGoRun(t)
358 dir := tempDir(t)
359
360 t.Parallel()
361
362
363 htmlProfile := filepath.Join(dir, "html.cov")
364 cmd := testenv.Command(t, testenv.GoToolPath(t), "test", toolexecArg, "-coverprofile", htmlProfile, "cmd/cover/testdata/html")
365 cmd.Env = append(cmd.Environ(), "CMDCOVER_TOOLEXEC=true")
366 run(cmd, t)
367
368 htmlHTML := filepath.Join(dir, "html.html")
369 cmd = testenv.Command(t, testcover(t), "-html", htmlProfile, "-o", htmlHTML)
370 run(cmd, t)
371
372
373
374 entireHTML, err := os.ReadFile(htmlHTML)
375 if err != nil {
376 t.Fatal(err)
377 }
378 var out strings.Builder
379 scan := bufio.NewScanner(bytes.NewReader(entireHTML))
380 in := false
381 for scan.Scan() {
382 line := scan.Text()
383 if strings.Contains(line, "// START") {
384 in = true
385 }
386 if in {
387 fmt.Fprintln(&out, line)
388 }
389 if strings.Contains(line, "// END") {
390 in = false
391 }
392 }
393 if scan.Err() != nil {
394 t.Error(scan.Err())
395 }
396 htmlGolden := filepath.Join(testdata, "html", "html.golden")
397 golden, err := os.ReadFile(htmlGolden)
398 if err != nil {
399 t.Fatalf("reading golden file: %v", err)
400 }
401
402
403 goldenLines := strings.Split(string(golden), "\n")
404 outLines := strings.Split(out.String(), "\n")
405
406
407 for i, goldenLine := range goldenLines {
408 if i >= len(outLines) {
409 t.Fatalf("output shorter than golden; stops before line %d: %s\n", i+1, goldenLine)
410 }
411
412 goldenLine = strings.Join(strings.Fields(goldenLine), " ")
413 outLine := strings.Join(strings.Fields(outLines[i]), " ")
414 if outLine != goldenLine {
415 t.Fatalf("line %d differs: got:\n\t%s\nwant:\n\t%s", i+1, outLine, goldenLine)
416 }
417 }
418 if len(goldenLines) != len(outLines) {
419 t.Fatalf("output longer than golden; first extra output line %d: %q\n", len(goldenLines)+1, outLines[len(goldenLines)])
420 }
421 }
422
423
424
425 func testHtmlUnformatted(t *testing.T, toolexecArg string) {
426 testenv.MustHaveGoRun(t)
427 dir := tempDir(t)
428
429 t.Parallel()
430
431 htmlUDir := filepath.Join(dir, "htmlunformatted")
432 htmlU := filepath.Join(htmlUDir, "htmlunformatted.go")
433 htmlUTest := filepath.Join(htmlUDir, "htmlunformatted_test.go")
434 htmlUProfile := filepath.Join(htmlUDir, "htmlunformatted.cov")
435 htmlUHTML := filepath.Join(htmlUDir, "htmlunformatted.html")
436
437 if err := os.Mkdir(htmlUDir, 0777); err != nil {
438 t.Fatal(err)
439 }
440
441 if err := os.WriteFile(filepath.Join(htmlUDir, "go.mod"), []byte("module htmlunformatted\n"), 0666); err != nil {
442 t.Fatal(err)
443 }
444
445 const htmlUContents = `
446 package htmlunformatted
447
448 var g int
449
450 func F() {
451 //line x.go:1
452 { { F(); goto lab } }
453 lab:
454 }`
455
456 const htmlUTestContents = `package htmlunformatted`
457
458 if err := os.WriteFile(htmlU, []byte(htmlUContents), 0444); err != nil {
459 t.Fatal(err)
460 }
461 if err := os.WriteFile(htmlUTest, []byte(htmlUTestContents), 0444); err != nil {
462 t.Fatal(err)
463 }
464
465
466 cmd := testenv.Command(t, testenv.GoToolPath(t), "test", "-test.v", toolexecArg, "-covermode=count", "-coverprofile", htmlUProfile)
467 cmd.Env = append(cmd.Environ(), "CMDCOVER_TOOLEXEC=true")
468 cmd.Dir = htmlUDir
469 run(cmd, t)
470
471
472 cmd = testenv.Command(t, testcover(t), "-html", htmlUProfile, "-o", htmlUHTML)
473 cmd.Dir = htmlUDir
474 run(cmd, t)
475 }
476
477
478 const lineDupContents = `
479 package linedup
480
481 var G int
482
483 func LineDup(c int) {
484 for i := 0; i < c; i++ {
485 //line ld.go:100
486 if i % 2 == 0 {
487 G++
488 }
489 if i % 3 == 0 {
490 G++; G++
491 }
492 //line ld.go:100
493 if i % 4 == 0 {
494 G++; G++; G++
495 }
496 if i % 5 == 0 {
497 G++; G++; G++; G++
498 }
499 }
500 }
501 `
502
503
504 const lineDupTestContents = `
505 package linedup
506
507 import "testing"
508
509 func TestLineDup(t *testing.T) {
510 LineDup(100)
511 }
512 `
513
514
515
516 func testFuncWithDuplicateLines(t *testing.T, toolexecArg string) {
517 testenv.MustHaveGoRun(t)
518 dir := tempDir(t)
519
520 t.Parallel()
521
522 lineDupDir := filepath.Join(dir, "linedup")
523 lineDupGo := filepath.Join(lineDupDir, "linedup.go")
524 lineDupTestGo := filepath.Join(lineDupDir, "linedup_test.go")
525 lineDupProfile := filepath.Join(lineDupDir, "linedup.out")
526
527 if err := os.Mkdir(lineDupDir, 0777); err != nil {
528 t.Fatal(err)
529 }
530
531 if err := os.WriteFile(filepath.Join(lineDupDir, "go.mod"), []byte("module linedup\n"), 0666); err != nil {
532 t.Fatal(err)
533 }
534 if err := os.WriteFile(lineDupGo, []byte(lineDupContents), 0444); err != nil {
535 t.Fatal(err)
536 }
537 if err := os.WriteFile(lineDupTestGo, []byte(lineDupTestContents), 0444); err != nil {
538 t.Fatal(err)
539 }
540
541
542 cmd := testenv.Command(t, testenv.GoToolPath(t), "test", toolexecArg, "-cover", "-covermode", "count", "-coverprofile", lineDupProfile)
543 cmd.Env = append(cmd.Environ(), "CMDCOVER_TOOLEXEC=true")
544 cmd.Dir = lineDupDir
545 run(cmd, t)
546
547
548 cmd = testenv.Command(t, testcover(t), "-func", lineDupProfile)
549 cmd.Dir = lineDupDir
550 run(cmd, t)
551 }
552
553 func run(c *exec.Cmd, t *testing.T) {
554 t.Helper()
555 t.Log("running", c.Args)
556 out, err := c.CombinedOutput()
557 if len(out) > 0 {
558 t.Logf("%s", out)
559 }
560 if err != nil {
561 t.Fatal(err)
562 }
563 }
564
565 func runExpectingError(c *exec.Cmd, t *testing.T) string {
566 t.Helper()
567 t.Log("running", c.Args)
568 out, err := c.CombinedOutput()
569 if err == nil {
570 return fmt.Sprintf("unexpected pass for %+v", c.Args)
571 }
572 return string(out)
573 }
574
575
576
577 func testMissingTrailingNewlineIssue58370(t *testing.T, toolexecArg string) {
578 testenv.MustHaveGoBuild(t)
579 dir := tempDir(t)
580
581 t.Parallel()
582
583 noeolDir := filepath.Join(dir, "issue58370")
584 noeolGo := filepath.Join(noeolDir, "noeol.go")
585 noeolTestGo := filepath.Join(noeolDir, "noeol_test.go")
586
587 if err := os.Mkdir(noeolDir, 0777); err != nil {
588 t.Fatal(err)
589 }
590
591 if err := os.WriteFile(filepath.Join(noeolDir, "go.mod"), []byte("module noeol\n"), 0666); err != nil {
592 t.Fatal(err)
593 }
594 const noeolContents = `package noeol`
595 if err := os.WriteFile(noeolGo, []byte(noeolContents), 0444); err != nil {
596 t.Fatal(err)
597 }
598 const noeolTestContents = `
599 package noeol
600 import "testing"
601 func TestCoverage(t *testing.T) { }
602 `
603 if err := os.WriteFile(noeolTestGo, []byte(noeolTestContents), 0444); err != nil {
604 t.Fatal(err)
605 }
606
607
608 cmd := testenv.Command(t, testenv.GoToolPath(t), "test", toolexecArg, "-covermode", "atomic")
609 cmd.Env = append(cmd.Environ(), "CMDCOVER_TOOLEXEC=true")
610 cmd.Dir = noeolDir
611 run(cmd, t)
612 }
613
614 func TestSrcPathWithNewline(t *testing.T) {
615 testenv.MustHaveExec(t)
616 t.Parallel()
617
618
619
620 srcPath := t.TempDir() + string(filepath.Separator) + "\npackage main\nfunc main() { panic(string([]rune{'u', 'h', '-', 'o', 'h'}))\n/*/main.go"
621 mainSrc := ` package main
622
623 func main() {
624 /* nothing here */
625 println("ok")
626 }
627 `
628 if err := os.MkdirAll(filepath.Dir(srcPath), 0777); err != nil {
629 t.Skipf("creating directory with bogus path: %v", err)
630 }
631 if err := os.WriteFile(srcPath, []byte(mainSrc), 0666); err != nil {
632 t.Skipf("writing file with bogus directory: %v", err)
633 }
634
635 cmd := testenv.Command(t, testcover(t), "-mode=atomic", srcPath)
636 cmd.Stderr = new(bytes.Buffer)
637 out, err := cmd.Output()
638 t.Logf("%v:\n%s", cmd, out)
639 t.Logf("stderr:\n%s", cmd.Stderr)
640 if err == nil {
641 t.Errorf("unexpected success; want failure due to newline in file path")
642 }
643 }
644
645 func TestAlignment(t *testing.T) {
646
647 testenv.MustHaveGoRun(t)
648 t.Parallel()
649
650 cmd := testenv.Command(t, testenv.GoToolPath(t), "test", "-cover", filepath.Join(testdata, "align.go"), filepath.Join(testdata, "align_test.go"))
651 run(cmd, t)
652 }
653
654
655 type lineRange struct {
656 start, end int
657 }
658
659
660
661
662 func parseBrackets(src []byte) ([]byte, []lineRange) {
663 const (
664 open = "\u00ab"
665 close = "\u00bb"
666 )
667 var (
668 cleaned []byte
669 ranges []lineRange
670 stack []int
671 )
672 i := 0
673 for i < len(src) {
674 if bytes.HasPrefix(src[i:], []byte(open)) {
675 stack = append(stack, len(cleaned))
676 i += len(open)
677 } else if bytes.HasPrefix(src[i:], []byte(close)) {
678 if len(stack) == 0 {
679 panic("unmatched close bracket at offset " + strconv.Itoa(i))
680 }
681 startPos := stack[len(stack)-1]
682 stack = stack[:len(stack)-1]
683 endPos := len(cleaned)
684
685 startLine := 1 + bytes.Count(cleaned[:startPos], []byte{'\n'})
686 endLine := 1 + bytes.Count(cleaned[:endPos], []byte{'\n'})
687
688
689 if endPos > 0 && cleaned[endPos-1] == '\n' {
690 endLine--
691 }
692 if endLine < startLine {
693 endLine = startLine
694 }
695 ranges = append(ranges, lineRange{startLine, endLine})
696 i += len(close)
697 } else {
698 cleaned = append(cleaned, src[i])
699 i++
700 }
701 }
702 if len(stack) != 0 {
703 panic("unmatched open bracket(s)")
704 }
705 return cleaned, ranges
706 }
707
708
709
710 func coverRanges(t *testing.T, src []byte) []lineRange {
711 t.Helper()
712 tmpdir := t.TempDir()
713 srcPath := filepath.Join(tmpdir, "test.go")
714 if err := os.WriteFile(srcPath, src, 0666); err != nil {
715 t.Fatalf("writing test file: %v", err)
716 }
717
718 cmd := testenv.Command(t, testcover(t), "-mode=set", srcPath)
719 out, err := cmd.Output()
720 if err != nil {
721 t.Fatalf("cover failed: %v\nOutput: %s", err, out)
722 }
723
724 outStr := string(out)
725
726 if _, after, ok := strings.Cut(outStr, "\n"); ok {
727 outStr = after
728 }
729
730
731
732 re := regexp.MustCompile(`(\d+), (\d+), (0x[0-9a-f]+), // \[\d+\]`)
733 matches := re.FindAllStringSubmatch(outStr, -1)
734
735 var result []lineRange
736 for _, m := range matches {
737 startLine, _ := strconv.Atoi(m[1])
738 endLine, _ := strconv.Atoi(m[2])
739 var packed int
740 fmt.Sscanf(m[3], "0x%x", &packed)
741 endCol := (packed >> 16) & 0xFFFF
742 startCol := packed & 0xFFFF
743
744 if startLine == endLine && startCol == endCol {
745 continue
746 }
747
748
749
750 if endCol == 1 && endLine > startLine {
751 endLine--
752 }
753 result = append(result, lineRange{startLine, endLine})
754 }
755 return result
756 }
757
758
759
760
761 func compareRanges(t *testing.T, src []byte, got, want []lineRange) {
762 t.Helper()
763 lines := strings.Split(string(src), "\n")
764 snippet := func(r lineRange) string {
765 start, end := r.start-1, r.end
766 if start < 0 {
767 start = 0
768 }
769 if end > len(lines) {
770 end = len(lines)
771 }
772 s := strings.Join(lines[start:end], "\n")
773 if len(s) > 120 {
774 s = s[:117] + "..."
775 }
776 return s
777 }
778
779 sortRanges := func(rs []lineRange) []lineRange {
780 s := append([]lineRange(nil), rs...)
781 slices.SortFunc(s, func(a, b lineRange) int {
782 if c := cmp.Compare(a.start, b.start); c != 0 {
783 return c
784 }
785 return cmp.Compare(a.end, b.end)
786 })
787 return s
788 }
789
790 gotSorted := sortRanges(got)
791 wantSorted := sortRanges(want)
792
793 if len(gotSorted) != len(wantSorted) {
794 t.Errorf("got %d ranges, want %d", len(gotSorted), len(wantSorted))
795 for i, r := range gotSorted {
796 t.Logf(" got[%d]: lines %d-%d %q", i, r.start, r.end, snippet(r))
797 }
798 for i, r := range wantSorted {
799 t.Logf(" want[%d]: lines %d-%d %q", i, r.start, r.end, snippet(r))
800 }
801 return
802 }
803 for i := range wantSorted {
804 if gotSorted[i] != wantSorted[i] {
805 t.Errorf("range %d: got lines %d-%d %q, want lines %d-%d %q",
806 i, gotSorted[i].start, gotSorted[i].end, snippet(gotSorted[i]),
807 wantSorted[i].start, wantSorted[i].end, snippet(wantSorted[i]))
808 }
809 }
810 }
811
812
813
814 func TestCommentedOutCodeExclusion(t *testing.T) {
815 testenv.MustHaveGoBuild(t)
816
817 markedSrc, err := os.ReadFile(filepath.Join(testdata, "ranges", "ranges.go"))
818 if err != nil {
819 t.Fatal(err)
820 }
821 src, want := parseBrackets(markedSrc)
822 got := coverRanges(t, src)
823 compareRanges(t, src, got, want)
824 }
825
826
827
828 func TestLineDirective(t *testing.T) {
829 testenv.MustHaveGoBuild(t)
830
831
832 testSrc := `package main
833
834 import "fmt"
835
836 func main() {
837 //line other.go:100
838 « x := 1
839 » // comment that should be excluded
840 « y := 2
841 » //line other.go:200
842 « fmt.Println(x, y)
843 »}`
844
845 src, want := parseBrackets([]byte(testSrc))
846 got := coverRanges(t, src)
847 compareRanges(t, src, got, want)
848 }
849
850
851
852 func TestCommentExclusionBasic(t *testing.T) {
853 testenv.MustHaveGoBuild(t)
854
855 testSrc := `package main
856
857 func main() {
858 « x := 1
859 » // this comment should split the block
860 « y := 2
861 »}`
862
863 src, want := parseBrackets([]byte(testSrc))
864 got := coverRanges(t, src)
865 compareRanges(t, src, got, want)
866 }
867
View as plain text