Source file src/runtime/coverage/ts_test.go

     1  // Copyright 2022 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 coverage
     6  
     7  import (
     8  	"encoding/json"
     9  	"internal/coverage"
    10  	"internal/goexperiment"
    11  	"internal/testenv"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"strings"
    16  	"testing"
    17  	_ "unsafe"
    18  )
    19  
    20  //go:linkname testing_testGoCoverDir testing.testGoCoverDir
    21  func testing_testGoCoverDir() string
    22  
    23  func testGoCoverDir(t *testing.T) string {
    24  	tgcd := testing_testGoCoverDir()
    25  	if tgcd != "" {
    26  		return tgcd
    27  	}
    28  	return t.TempDir()
    29  }
    30  
    31  // TestTestSupport does a basic verification of the functionality in
    32  // runtime/coverage.processCoverTestDir (doing this here as opposed to
    33  // relying on other test paths will provide a better signal when
    34  // running "go test -cover" for this package).
    35  func TestTestSupport(t *testing.T) {
    36  	if !goexperiment.CoverageRedesign {
    37  		return
    38  	}
    39  	if testing.CoverMode() == "" {
    40  		return
    41  	}
    42  	tgcd := testGoCoverDir(t)
    43  	t.Logf("testing.testGoCoverDir() returns %s mode=%s\n",
    44  		tgcd, testing.CoverMode())
    45  
    46  	textfile := filepath.Join(t.TempDir(), "file.txt")
    47  	var sb strings.Builder
    48  	err := processCoverTestDirInternal(tgcd, textfile,
    49  		testing.CoverMode(), "", &sb)
    50  	if err != nil {
    51  		t.Fatalf("bad: %v", err)
    52  	}
    53  
    54  	// Check for existence of text file.
    55  	if inf, err := os.Open(textfile); err != nil {
    56  		t.Fatalf("problems opening text file %s: %v", textfile, err)
    57  	} else {
    58  		inf.Close()
    59  	}
    60  
    61  	// Check for percent output with expected tokens.
    62  	strout := sb.String()
    63  	want := "of statements"
    64  	if !strings.Contains(strout, want) {
    65  		t.Logf("output from run: %s\n", strout)
    66  		t.Fatalf("percent output missing token: %q", want)
    67  	}
    68  }
    69  
    70  var funcInvoked bool
    71  
    72  //go:noinline
    73  func thisFunctionOnlyCalledFromSnapshotTest(n int) int {
    74  	if funcInvoked {
    75  		panic("bad")
    76  	}
    77  	funcInvoked = true
    78  
    79  	// Contents here not especially important, just so long as we
    80  	// have some statements.
    81  	t := 0
    82  	for i := 0; i < n; i++ {
    83  		for j := 0; j < i; j++ {
    84  			t += i ^ j
    85  		}
    86  	}
    87  	return t
    88  }
    89  
    90  // Tests runtime/coverage.snapshot() directly. Note that if
    91  // coverage is not enabled, the hook is designed to just return
    92  // zero.
    93  func TestCoverageSnapshot(t *testing.T) {
    94  	C1 := snapshot()
    95  	thisFunctionOnlyCalledFromSnapshotTest(15)
    96  	C2 := snapshot()
    97  	cond := "C1 > C2"
    98  	val := C1 > C2
    99  	if testing.CoverMode() != "" {
   100  		cond = "C1 >= C2"
   101  		val = C1 >= C2
   102  	}
   103  	t.Logf("%f %f\n", C1, C2)
   104  	if val {
   105  		t.Errorf("erroneous snapshots, %s = true C1=%f C2=%f",
   106  			cond, C1, C2)
   107  	}
   108  }
   109  
   110  const hellogo = `
   111  package main
   112  
   113  func main() {
   114    println("hello")
   115  }
   116  `
   117  
   118  // Returns a pair F,T where F is a meta-data file generated from
   119  // "hello.go" above, and T is a token to look for that should be
   120  // present in the coverage report from F.
   121  func genAuxMeta(t *testing.T, dstdir string) (string, string) {
   122  	// Do a GOCOVERDIR=<tmp> go run hello.go
   123  	src := filepath.Join(dstdir, "hello.go")
   124  	if err := os.WriteFile(src, []byte(hellogo), 0777); err != nil {
   125  		t.Fatalf("write failed: %v", err)
   126  	}
   127  	args := []string{"run", "-covermode=" + testing.CoverMode(), src}
   128  	cmd := exec.Command(testenv.GoToolPath(t), args...)
   129  	cmd.Env = updateGoCoverDir(os.Environ(), dstdir, true)
   130  	if b, err := cmd.CombinedOutput(); err != nil {
   131  		t.Fatalf("go run failed (%v): %s", err, b)
   132  	}
   133  
   134  	// Pick out the generated meta-data file.
   135  	files, err := os.ReadDir(dstdir)
   136  	if err != nil {
   137  		t.Fatalf("reading %s: %v", dstdir, err)
   138  	}
   139  	for _, f := range files {
   140  		if strings.HasPrefix(f.Name(), "covmeta") {
   141  			return filepath.Join(dstdir, f.Name()), "hello.go:"
   142  		}
   143  	}
   144  	t.Fatalf("could not locate generated meta-data file")
   145  	return "", ""
   146  }
   147  
   148  func TestAuxMetaDataFiles(t *testing.T) {
   149  	if !goexperiment.CoverageRedesign {
   150  		return
   151  	}
   152  	if testing.CoverMode() == "" {
   153  		return
   154  	}
   155  	testenv.MustHaveGoRun(t)
   156  	tgcd := testGoCoverDir(t)
   157  	t.Logf("testing.testGoCoverDir() returns %s mode=%s\n",
   158  		tgcd, testing.CoverMode())
   159  
   160  	td := t.TempDir()
   161  
   162  	// Manufacture a new, separate meta-data file not related to this
   163  	// test. Contents are not important, just so long as the
   164  	// packages/paths are different.
   165  	othermetadir := filepath.Join(td, "othermeta")
   166  	if err := os.Mkdir(othermetadir, 0777); err != nil {
   167  		t.Fatalf("mkdir failed: %v", err)
   168  	}
   169  	mfile, token := genAuxMeta(t, othermetadir)
   170  
   171  	// Write a metafiles file.
   172  	metafiles := filepath.Join(tgcd, coverage.MetaFilesFileName)
   173  	mfc := coverage.MetaFileCollection{
   174  		ImportPaths:       []string{"command-line-arguments"},
   175  		MetaFileFragments: []string{mfile},
   176  	}
   177  	jdata, err := json.Marshal(mfc)
   178  	if err != nil {
   179  		t.Fatalf("marshal MetaFileCollection: %v", err)
   180  	}
   181  	if err := os.WriteFile(metafiles, jdata, 0666); err != nil {
   182  		t.Fatalf("write failed: %v", err)
   183  	}
   184  
   185  	// Kick off guts of test.
   186  	var sb strings.Builder
   187  	textfile := filepath.Join(td, "file2.txt")
   188  	err = processCoverTestDirInternal(tgcd, textfile,
   189  		testing.CoverMode(), "", &sb)
   190  	if err != nil {
   191  		t.Fatalf("bad: %v", err)
   192  	}
   193  	if err = os.Remove(metafiles); err != nil {
   194  		t.Fatalf("removing metafiles file: %v", err)
   195  	}
   196  
   197  	// Look for the expected things in the coverage profile.
   198  	contents, err := os.ReadFile(textfile)
   199  	strc := string(contents)
   200  	if err != nil {
   201  		t.Fatalf("problems reading text file %s: %v", textfile, err)
   202  	}
   203  	if !strings.Contains(strc, token) {
   204  		t.Logf("content: %s\n", string(contents))
   205  		t.Fatalf("cov profile does not contain aux meta content %q", token)
   206  	}
   207  }
   208  

View as plain text