diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/covdata/tool_test.go | |
parent | Initial commit. (diff) | |
download | golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip |
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/covdata/tool_test.go')
-rw-r--r-- | src/cmd/covdata/tool_test.go | 944 |
1 files changed, 944 insertions, 0 deletions
diff --git a/src/cmd/covdata/tool_test.go b/src/cmd/covdata/tool_test.go new file mode 100644 index 0000000..42334ea --- /dev/null +++ b/src/cmd/covdata/tool_test.go @@ -0,0 +1,944 @@ +// Copyright 2022 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. + +package main_test + +import ( + cmdcovdata "cmd/covdata" + "flag" + "fmt" + "internal/coverage/pods" + "internal/goexperiment" + "internal/testenv" + "log" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "sync" + "testing" +) + +// testcovdata returns the path to the unit test executable to be used as +// standin for 'go tool covdata'. +func testcovdata(t testing.TB) string { + exe, err := os.Executable() + if err != nil { + t.Helper() + t.Fatal(err) + } + return exe +} + +// Top level tempdir for test. +var testTempDir string + +// If set, this will preserve all the tmpdir files from the test run. +var preserveTmp = flag.Bool("preservetmp", false, "keep tmpdir files for debugging") + +// TestMain used here so that we can leverage the test executable +// itself as a cmd/covdata executable; compare to similar usage in +// the cmd/go tests. +func TestMain(m *testing.M) { + // When CMDCOVDATA_TEST_RUN_MAIN is set, we're reusing the test + // binary as cmd/cover. In this case we run the main func exported + // via export_test.go, and exit; CMDCOVDATA_TEST_RUN_MAIN is set below + // for actual test invocations. + if os.Getenv("CMDCOVDATA_TEST_RUN_MAIN") != "" { + cmdcovdata.Main() + os.Exit(0) + } + flag.Parse() + topTmpdir, err := os.MkdirTemp("", "cmd-covdata-test-") + if err != nil { + log.Fatal(err) + } + testTempDir = topTmpdir + if !*preserveTmp { + defer os.RemoveAll(topTmpdir) + } else { + fmt.Fprintf(os.Stderr, "debug: preserving tmpdir %s\n", topTmpdir) + } + os.Setenv("CMDCOVDATA_TEST_RUN_MAIN", "true") + os.Exit(m.Run()) +} + +var tdmu sync.Mutex +var tdcount int + +func tempDir(t *testing.T) string { + tdmu.Lock() + dir := filepath.Join(testTempDir, fmt.Sprintf("%03d", tdcount)) + tdcount++ + if err := os.Mkdir(dir, 0777); err != nil { + t.Fatal(err) + } + defer tdmu.Unlock() + return dir +} + +const debugtrace = false + +func gobuild(t *testing.T, indir string, bargs []string) { + t.Helper() + + if debugtrace { + if indir != "" { + t.Logf("in dir %s: ", indir) + } + t.Logf("cmd: %s %+v\n", testenv.GoToolPath(t), bargs) + } + cmd := testenv.Command(t, testenv.GoToolPath(t), bargs...) + cmd.Dir = indir + b, err := cmd.CombinedOutput() + if len(b) != 0 { + t.Logf("## build output:\n%s", b) + } + if err != nil { + t.Fatalf("build error: %v", err) + } +} + +func emitFile(t *testing.T, dst, src string) { + payload, err := os.ReadFile(src) + if err != nil { + t.Fatalf("error reading %q: %v", src, err) + } + if err := os.WriteFile(dst, payload, 0666); err != nil { + t.Fatalf("writing %q: %v", dst, err) + } +} + +const mainPkgPath = "prog" + +func buildProg(t *testing.T, prog string, dir string, tag string, flags []string) (string, string) { + // Create subdirs. + subdir := filepath.Join(dir, prog+"dir"+tag) + if err := os.Mkdir(subdir, 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", subdir, err) + } + depdir := filepath.Join(subdir, "dep") + if err := os.Mkdir(depdir, 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", depdir, err) + } + + // Emit program. + insrc := filepath.Join("testdata", prog+".go") + src := filepath.Join(subdir, prog+".go") + emitFile(t, src, insrc) + indep := filepath.Join("testdata", "dep.go") + dep := filepath.Join(depdir, "dep.go") + emitFile(t, dep, indep) + + // Emit go.mod. + mod := filepath.Join(subdir, "go.mod") + modsrc := "\nmodule " + mainPkgPath + "\n\ngo 1.19\n" + if err := os.WriteFile(mod, []byte(modsrc), 0666); err != nil { + t.Fatal(err) + } + exepath := filepath.Join(subdir, prog+".exe") + bargs := []string{"build", "-cover", "-o", exepath} + bargs = append(bargs, flags...) + gobuild(t, subdir, bargs) + return exepath, subdir +} + +type state struct { + dir string + exedir1 string + exedir2 string + exedir3 string + exepath1 string + exepath2 string + exepath3 string + tool string + outdirs [4]string +} + +const debugWorkDir = false + +func TestCovTool(t *testing.T) { + testenv.MustHaveGoBuild(t) + if !goexperiment.CoverageRedesign { + t.Skipf("stubbed out due to goexperiment.CoverageRedesign=false") + } + dir := tempDir(t) + if testing.Short() { + t.Skip() + } + if debugWorkDir { + // debugging + dir = "/tmp/qqq" + os.RemoveAll(dir) + os.Mkdir(dir, 0777) + } + + s := state{ + dir: dir, + } + s.exepath1, s.exedir1 = buildProg(t, "prog1", dir, "", nil) + s.exepath2, s.exedir2 = buildProg(t, "prog2", dir, "", nil) + flags := []string{"-covermode=atomic"} + s.exepath3, s.exedir3 = buildProg(t, "prog1", dir, "atomic", flags) + + // Reuse unit test executable as tool to be tested. + s.tool = testcovdata(t) + + // Create a few coverage output dirs. + for i := 0; i < 4; i++ { + d := filepath.Join(dir, fmt.Sprintf("covdata%d", i)) + s.outdirs[i] = d + if err := os.Mkdir(d, 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", d, err) + } + } + + // Run instrumented program to generate some coverage data output files, + // as follows: + // + // <tmp>/covdata0 -- prog1.go compiled -cover + // <tmp>/covdata1 -- prog1.go compiled -cover + // <tmp>/covdata2 -- prog1.go compiled -covermode=atomic + // <tmp>/covdata3 -- prog1.go compiled -covermode=atomic + // + for m := 0; m < 2; m++ { + for k := 0; k < 2; k++ { + args := []string{} + if k != 0 { + args = append(args, "foo", "bar") + } + for i := 0; i <= k; i++ { + exepath := s.exepath1 + if m != 0 { + exepath = s.exepath3 + } + cmd := testenv.Command(t, exepath, args...) + cmd.Env = append(cmd.Env, "GOCOVERDIR="+s.outdirs[m*2+k]) + b, err := cmd.CombinedOutput() + if len(b) != 0 { + t.Logf("## instrumented run output:\n%s", b) + } + if err != nil { + t.Fatalf("instrumented run error: %v", err) + } + } + } + } + + // At this point we can fork off a bunch of child tests + // to check different tool modes. + t.Run("MergeSimple", func(t *testing.T) { + t.Parallel() + testMergeSimple(t, s, s.outdirs[0], s.outdirs[1], "set") + testMergeSimple(t, s, s.outdirs[2], s.outdirs[3], "atomic") + }) + t.Run("MergeSelect", func(t *testing.T) { + t.Parallel() + testMergeSelect(t, s, s.outdirs[0], s.outdirs[1], "set") + testMergeSelect(t, s, s.outdirs[2], s.outdirs[3], "atomic") + }) + t.Run("MergePcombine", func(t *testing.T) { + t.Parallel() + testMergeCombinePrograms(t, s) + }) + t.Run("Dump", func(t *testing.T) { + t.Parallel() + testDump(t, s) + }) + t.Run("Percent", func(t *testing.T) { + t.Parallel() + testPercent(t, s) + }) + t.Run("PkgList", func(t *testing.T) { + t.Parallel() + testPkgList(t, s) + }) + t.Run("Textfmt", func(t *testing.T) { + t.Parallel() + testTextfmt(t, s) + }) + t.Run("Subtract", func(t *testing.T) { + t.Parallel() + testSubtract(t, s) + }) + t.Run("Intersect", func(t *testing.T) { + t.Parallel() + testIntersect(t, s, s.outdirs[0], s.outdirs[1], "set") + testIntersect(t, s, s.outdirs[2], s.outdirs[3], "atomic") + }) + t.Run("CounterClash", func(t *testing.T) { + t.Parallel() + testCounterClash(t, s) + }) + t.Run("TestEmpty", func(t *testing.T) { + t.Parallel() + testEmpty(t, s) + }) + t.Run("TestCommandLineErrors", func(t *testing.T) { + t.Parallel() + testCommandLineErrors(t, s, s.outdirs[0]) + }) +} + +const showToolInvocations = true + +func runToolOp(t *testing.T, s state, op string, args []string) []string { + // Perform tool run. + t.Helper() + args = append([]string{op}, args...) + if showToolInvocations { + t.Logf("%s cmd is: %s %+v", op, s.tool, args) + } + cmd := testenv.Command(t, s.tool, args...) + b, err := cmd.CombinedOutput() + if err != nil { + fmt.Fprintf(os.Stderr, "## %s output: %s\n", op, string(b)) + t.Fatalf("%q run error: %v", op, err) + } + output := strings.TrimSpace(string(b)) + lines := strings.Split(output, "\n") + if len(lines) == 1 && lines[0] == "" { + lines = nil + } + return lines +} + +func testDump(t *testing.T, s state) { + // Run the dumper on the two dirs we generated. + dargs := []string{"-pkg=" + mainPkgPath, "-live", "-i=" + s.outdirs[0] + "," + s.outdirs[1]} + lines := runToolOp(t, s, "debugdump", dargs) + + // Sift through the output to make sure it has some key elements. + testpoints := []struct { + tag string + re *regexp.Regexp + }{ + { + "args", + regexp.MustCompile(`^data file .+ GOOS=.+ GOARCH=.+ program args: .+$`), + }, + { + "main package", + regexp.MustCompile(`^Package path: ` + mainPkgPath + `\s*$`), + }, + { + "main function", + regexp.MustCompile(`^Func: main\s*$`), + }, + } + + bad := false + for _, testpoint := range testpoints { + found := false + for _, line := range lines { + if m := testpoint.re.FindStringSubmatch(line); m != nil { + found = true + break + } + } + if !found { + t.Errorf("dump output regexp match failed for %q", testpoint.tag) + bad = true + } + } + if bad { + dumplines(lines) + } +} + +func testPercent(t *testing.T, s state) { + // Run the dumper on the two dirs we generated. + dargs := []string{"-pkg=" + mainPkgPath, "-i=" + s.outdirs[0] + "," + s.outdirs[1]} + lines := runToolOp(t, s, "percent", dargs) + + // Sift through the output to make sure it has the needful. + testpoints := []struct { + tag string + re *regexp.Regexp + }{ + { + "statement coverage percent", + regexp.MustCompile(`coverage: \d+\.\d% of statements\s*$`), + }, + } + + bad := false + for _, testpoint := range testpoints { + found := false + for _, line := range lines { + if m := testpoint.re.FindStringSubmatch(line); m != nil { + found = true + break + } + } + if !found { + t.Errorf("percent output regexp match failed for %s", testpoint.tag) + bad = true + } + } + if bad { + dumplines(lines) + } +} + +func testPkgList(t *testing.T, s state) { + dargs := []string{"-i=" + s.outdirs[0] + "," + s.outdirs[1]} + lines := runToolOp(t, s, "pkglist", dargs) + + want := []string{mainPkgPath, mainPkgPath + "/dep"} + bad := false + if len(lines) != 2 { + t.Errorf("expect pkglist to return two lines") + bad = true + } else { + for i := 0; i < 2; i++ { + lines[i] = strings.TrimSpace(lines[i]) + if want[i] != lines[i] { + t.Errorf("line %d want %s got %s", i, want[i], lines[i]) + bad = true + } + } + } + if bad { + dumplines(lines) + } +} + +func testTextfmt(t *testing.T, s state) { + outf := s.dir + "/" + "t.txt" + dargs := []string{"-pkg=" + mainPkgPath, "-i=" + s.outdirs[0] + "," + s.outdirs[1], + "-o", outf} + lines := runToolOp(t, s, "textfmt", dargs) + + // No output expected. + if len(lines) != 0 { + dumplines(lines) + t.Errorf("unexpected output from go tool covdata textfmt") + } + + // Open and read the first few bits of the file. + payload, err := os.ReadFile(outf) + if err != nil { + t.Errorf("opening %s: %v\n", outf, err) + } + lines = strings.Split(string(payload), "\n") + want0 := "mode: set" + if lines[0] != want0 { + dumplines(lines[0:10]) + t.Errorf("textfmt: want %s got %s", want0, lines[0]) + } + want1 := mainPkgPath + "/prog1.go:13.14,15.2 1 1" + if lines[1] != want1 { + dumplines(lines[0:10]) + t.Errorf("textfmt: want %s got %s", want1, lines[1]) + } +} + +func dumplines(lines []string) { + for i := range lines { + fmt.Fprintf(os.Stderr, "%s\n", lines[i]) + } +} + +type dumpCheck struct { + tag string + re *regexp.Regexp + negate bool + nonzero bool + zero bool +} + +// runDumpChecks examines the output of "go tool covdata debugdump" +// for a given output directory, looking for the presence or absence +// of specific markers. +func runDumpChecks(t *testing.T, s state, dir string, flags []string, checks []dumpCheck) { + dargs := []string{"-i", dir} + dargs = append(dargs, flags...) + lines := runToolOp(t, s, "debugdump", dargs) + if len(lines) == 0 { + t.Fatalf("dump run produced no output") + } + + bad := false + for _, check := range checks { + found := false + for _, line := range lines { + if m := check.re.FindStringSubmatch(line); m != nil { + found = true + if check.negate { + t.Errorf("tag %q: unexpected match", check.tag) + bad = true + + } + if check.nonzero || check.zero { + if len(m) < 2 { + t.Errorf("tag %s: submatch failed (short m)", check.tag) + bad = true + continue + } + if m[1] == "" { + t.Errorf("tag %s: submatch failed", check.tag) + bad = true + continue + } + i, err := strconv.Atoi(m[1]) + if err != nil { + t.Errorf("tag %s: match Atoi failed on %s", + check.tag, m[1]) + continue + } + if check.zero && i != 0 { + t.Errorf("tag %s: match zero failed on %s", + check.tag, m[1]) + } else if check.nonzero && i == 0 { + t.Errorf("tag %s: match nonzero failed on %s", + check.tag, m[1]) + } + } + break + } + } + if !found && !check.negate { + t.Errorf("dump output regexp match failed for %s", check.tag) + bad = true + } + } + if bad { + fmt.Printf("output from 'dump' run:\n") + dumplines(lines) + } +} + +func testMergeSimple(t *testing.T, s state, indir1, indir2, tag string) { + outdir := filepath.Join(s.dir, "simpleMergeOut"+tag) + if err := os.Mkdir(outdir, 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", outdir, err) + } + + // Merge the two dirs into a final result. + ins := fmt.Sprintf("-i=%s,%s", indir1, indir2) + out := fmt.Sprintf("-o=%s", outdir) + margs := []string{ins, out} + lines := runToolOp(t, s, "merge", margs) + if len(lines) != 0 { + t.Errorf("merge run produced %d lines of unexpected output", len(lines)) + dumplines(lines) + } + + // We expect the merge tool to produce exactly two files: a meta + // data file and a counter file. If we get more than just this one + // pair, something went wrong. + podlist, err := pods.CollectPods([]string{outdir}, true) + if err != nil { + t.Fatal(err) + } + if len(podlist) != 1 { + t.Fatalf("expected 1 pod, got %d pods", len(podlist)) + } + ncdfs := len(podlist[0].CounterDataFiles) + if ncdfs != 1 { + t.Fatalf("expected 1 counter data file, got %d", ncdfs) + } + + // Sift through the output to make sure it has some key elements. + // In particular, we want to see entries for all three functions + // ("first", "second", and "third"). + testpoints := []dumpCheck{ + { + tag: "first function", + re: regexp.MustCompile(`^Func: first\s*$`), + }, + { + tag: "second function", + re: regexp.MustCompile(`^Func: second\s*$`), + }, + { + tag: "third function", + re: regexp.MustCompile(`^Func: third\s*$`), + }, + { + tag: "third function unit 0", + re: regexp.MustCompile(`^0: L23:C23 -- L24:C12 NS=1 = (\d+)$`), + nonzero: true, + }, + { + tag: "third function unit 1", + re: regexp.MustCompile(`^1: L27:C2 -- L28:C10 NS=2 = (\d+)$`), + nonzero: true, + }, + { + tag: "third function unit 2", + re: regexp.MustCompile(`^2: L24:C12 -- L26:C3 NS=1 = (\d+)$`), + nonzero: true, + }, + } + flags := []string{"-live", "-pkg=" + mainPkgPath} + runDumpChecks(t, s, outdir, flags, testpoints) +} + +func testMergeSelect(t *testing.T, s state, indir1, indir2 string, tag string) { + outdir := filepath.Join(s.dir, "selectMergeOut"+tag) + if err := os.Mkdir(outdir, 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", outdir, err) + } + + // Merge two input dirs into a final result, but filter + // based on package. + ins := fmt.Sprintf("-i=%s,%s", indir1, indir2) + out := fmt.Sprintf("-o=%s", outdir) + margs := []string{"-pkg=" + mainPkgPath + "/dep", ins, out} + lines := runToolOp(t, s, "merge", margs) + if len(lines) != 0 { + t.Errorf("merge run produced %d lines of unexpected output", len(lines)) + dumplines(lines) + } + + // Dump the files in the merged output dir and examine the result. + // We expect to see only the functions in package "dep". + dargs := []string{"-i=" + outdir} + lines = runToolOp(t, s, "debugdump", dargs) + if len(lines) == 0 { + t.Fatalf("dump run produced no output") + } + want := map[string]int{ + "Package path: " + mainPkgPath + "/dep": 0, + "Func: Dep1": 0, + "Func: PDep": 0, + } + bad := false + for _, line := range lines { + if v, ok := want[line]; ok { + if v != 0 { + t.Errorf("duplicate line %s", line) + bad = true + break + } + want[line] = 1 + continue + } + // no other functions or packages expected. + if strings.HasPrefix(line, "Func:") || strings.HasPrefix(line, "Package path:") { + t.Errorf("unexpected line: %s", line) + bad = true + break + } + } + if bad { + dumplines(lines) + } +} + +func testMergeCombinePrograms(t *testing.T, s state) { + + // Run the new program, emitting output into a new set + // of outdirs. + runout := [2]string{} + for k := 0; k < 2; k++ { + runout[k] = filepath.Join(s.dir, fmt.Sprintf("newcovdata%d", k)) + if err := os.Mkdir(runout[k], 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", runout[k], err) + } + args := []string{} + if k != 0 { + args = append(args, "foo", "bar") + } + cmd := testenv.Command(t, s.exepath2, args...) + cmd.Env = append(cmd.Env, "GOCOVERDIR="+runout[k]) + b, err := cmd.CombinedOutput() + if len(b) != 0 { + t.Logf("## instrumented run output:\n%s", b) + } + if err != nil { + t.Fatalf("instrumented run error: %v", err) + } + } + + // Create out dir for -pcombine merge. + moutdir := filepath.Join(s.dir, "mergeCombineOut") + if err := os.Mkdir(moutdir, 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", moutdir, err) + } + + // Run a merge over both programs, using the -pcombine + // flag to do maximal combining. + ins := fmt.Sprintf("-i=%s,%s,%s,%s", s.outdirs[0], s.outdirs[1], + runout[0], runout[1]) + out := fmt.Sprintf("-o=%s", moutdir) + margs := []string{"-pcombine", ins, out} + lines := runToolOp(t, s, "merge", margs) + if len(lines) != 0 { + t.Errorf("merge run produced unexpected output: %v", lines) + } + + // We expect the merge tool to produce exacty two files: a meta + // data file and a counter file. If we get more than just this one + // pair, something went wrong. + podlist, err := pods.CollectPods([]string{moutdir}, true) + if err != nil { + t.Fatal(err) + } + if len(podlist) != 1 { + t.Fatalf("expected 1 pod, got %d pods", len(podlist)) + } + ncdfs := len(podlist[0].CounterDataFiles) + if ncdfs != 1 { + t.Fatalf("expected 1 counter data file, got %d", ncdfs) + } + + // Sift through the output to make sure it has some key elements. + testpoints := []dumpCheck{ + { + tag: "first function", + re: regexp.MustCompile(`^Func: first\s*$`), + }, + { + tag: "sixth function", + re: regexp.MustCompile(`^Func: sixth\s*$`), + }, + } + + flags := []string{"-live", "-pkg=" + mainPkgPath} + runDumpChecks(t, s, moutdir, flags, testpoints) +} + +func testSubtract(t *testing.T, s state) { + // Create out dir for subtract merge. + soutdir := filepath.Join(s.dir, "subtractOut") + if err := os.Mkdir(soutdir, 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", soutdir, err) + } + + // Subtract the two dirs into a final result. + ins := fmt.Sprintf("-i=%s,%s", s.outdirs[0], s.outdirs[1]) + out := fmt.Sprintf("-o=%s", soutdir) + sargs := []string{ins, out} + lines := runToolOp(t, s, "subtract", sargs) + if len(lines) != 0 { + t.Errorf("subtract run produced unexpected output: %+v", lines) + } + + // Dump the files in the subtract output dir and examine the result. + dargs := []string{"-pkg=" + mainPkgPath, "-live", "-i=" + soutdir} + lines = runToolOp(t, s, "debugdump", dargs) + if len(lines) == 0 { + t.Errorf("dump run produced no output") + } + + // Vet the output. + testpoints := []dumpCheck{ + { + tag: "first function", + re: regexp.MustCompile(`^Func: first\s*$`), + }, + { + tag: "dep function", + re: regexp.MustCompile(`^Func: Dep1\s*$`), + }, + { + tag: "third function", + re: regexp.MustCompile(`^Func: third\s*$`), + }, + { + tag: "third function unit 0", + re: regexp.MustCompile(`^0: L23:C23 -- L24:C12 NS=1 = (\d+)$`), + zero: true, + }, + { + tag: "third function unit 1", + re: regexp.MustCompile(`^1: L27:C2 -- L28:C10 NS=2 = (\d+)$`), + nonzero: true, + }, + { + tag: "third function unit 2", + re: regexp.MustCompile(`^2: L24:C12 -- L26:C3 NS=1 = (\d+)$`), + zero: true, + }, + } + flags := []string{} + runDumpChecks(t, s, soutdir, flags, testpoints) +} + +func testIntersect(t *testing.T, s state, indir1, indir2, tag string) { + // Create out dir for intersection. + ioutdir := filepath.Join(s.dir, "intersectOut"+tag) + if err := os.Mkdir(ioutdir, 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", ioutdir, err) + } + + // Intersect the two dirs into a final result. + ins := fmt.Sprintf("-i=%s,%s", indir1, indir2) + out := fmt.Sprintf("-o=%s", ioutdir) + sargs := []string{ins, out} + lines := runToolOp(t, s, "intersect", sargs) + if len(lines) != 0 { + t.Errorf("intersect run produced unexpected output: %+v", lines) + } + + // Dump the files in the subtract output dir and examine the result. + dargs := []string{"-pkg=" + mainPkgPath, "-live", "-i=" + ioutdir} + lines = runToolOp(t, s, "debugdump", dargs) + if len(lines) == 0 { + t.Errorf("dump run produced no output") + } + + // Vet the output. + testpoints := []dumpCheck{ + { + tag: "first function", + re: regexp.MustCompile(`^Func: first\s*$`), + negate: true, + }, + { + tag: "third function", + re: regexp.MustCompile(`^Func: third\s*$`), + }, + } + flags := []string{"-live"} + runDumpChecks(t, s, ioutdir, flags, testpoints) +} + +func testCounterClash(t *testing.T, s state) { + // Create out dir. + ccoutdir := filepath.Join(s.dir, "ccOut") + if err := os.Mkdir(ccoutdir, 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", ccoutdir, err) + } + + // Try to merge covdata0 (from prog1.go -countermode=set) with + // covdata1 (from prog1.go -countermode=atomic"). This should + // produce a counter mode clash error. + ins := fmt.Sprintf("-i=%s,%s", s.outdirs[0], s.outdirs[3]) + out := fmt.Sprintf("-o=%s", ccoutdir) + args := append([]string{}, "merge", ins, out, "-pcombine") + if debugtrace { + t.Logf("cc merge command is %s %v\n", s.tool, args) + } + cmd := testenv.Command(t, s.tool, args...) + b, err := cmd.CombinedOutput() + t.Logf("%% output: %s\n", string(b)) + if err == nil { + t.Fatalf("clash merge passed unexpectedly") + } + got := string(b) + want := "counter mode clash while reading meta-data" + if !strings.Contains(got, want) { + t.Errorf("counter clash merge: wanted %s got %s", want, got) + } +} + +func testEmpty(t *testing.T, s state) { + + // Create a new empty directory. + empty := filepath.Join(s.dir, "empty") + if err := os.Mkdir(empty, 0777); err != nil { + t.Fatalf("can't create dir %s: %v", empty, err) + } + + // Create out dir. + eoutdir := filepath.Join(s.dir, "emptyOut") + if err := os.Mkdir(eoutdir, 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", eoutdir, err) + } + + // Run various operations (merge, dump, textfmt, and so on) + // using the empty directory. We're not interested in the output + // here, just making sure that you can do these runs without + // any error or crash. + + scenarios := []struct { + tag string + args []string + }{ + { + tag: "merge", + args: []string{"merge", "-o", eoutdir}, + }, + { + tag: "textfmt", + args: []string{"textfmt", "-o", filepath.Join(eoutdir, "foo.txt")}, + }, + { + tag: "func", + args: []string{"func"}, + }, + { + tag: "pkglist", + args: []string{"pkglist"}, + }, + { + tag: "debugdump", + args: []string{"debugdump"}, + }, + { + tag: "percent", + args: []string{"percent"}, + }, + } + + for _, x := range scenarios { + ins := fmt.Sprintf("-i=%s", empty) + args := append([]string{}, x.args...) + args = append(args, ins) + if false { + t.Logf("cmd is %s %v\n", s.tool, args) + } + cmd := testenv.Command(t, s.tool, args...) + b, err := cmd.CombinedOutput() + t.Logf("%% output: %s\n", string(b)) + if err != nil { + t.Fatalf("command %s %+v failed with %v", + s.tool, x.args, err) + } + } +} + +func testCommandLineErrors(t *testing.T, s state, outdir string) { + + // Create out dir. + eoutdir := filepath.Join(s.dir, "errorsOut") + if err := os.Mkdir(eoutdir, 0777); err != nil { + t.Fatalf("can't create outdir %s: %v", eoutdir, err) + } + + // Run various operations (merge, dump, textfmt, and so on) + // using the empty directory. We're not interested in the output + // here, just making sure that you can do these runs without + // any error or crash. + + scenarios := []struct { + tag string + args []string + exp string + }{ + { + tag: "input missing", + args: []string{"merge", "-o", eoutdir, "-i", "not there"}, + exp: "error: reading inputs: ", + }, + { + tag: "badv", + args: []string{"textfmt", "-i", outdir, "-v=abc"}, + }, + } + + for _, x := range scenarios { + args := append([]string{}, x.args...) + if false { + t.Logf("cmd is %s %v\n", s.tool, args) + } + cmd := testenv.Command(t, s.tool, args...) + b, err := cmd.CombinedOutput() + if err == nil { + t.Logf("%% output: %s\n", string(b)) + t.Fatalf("command %s %+v unexpectedly succeeded", + s.tool, x.args) + } else { + if !strings.Contains(string(b), x.exp) { + t.Fatalf("command %s %+v:\ngot:\n%s\nwanted to see: %v\n", + s.tool, x.args, string(b), x.exp) + } + } + } +} |