report_test.mx raw

   1  package mxcover
   2  
   3  import "testing"
   4  
   5  // The reporter is the only thing that turns a coverage run into a number, so
   6  // its parsing and its arithmetic are tested here rather than trusted: a wrong
   7  // percentage or a dropped site would look exactly like a real result.
   8  
   9  func TestI64Str(t *testing.T) {
  10  	if i64str(0) != "0" {
  11  		t.Fatal("zero")
  12  	}
  13  	if i64str(7) != "7" {
  14  		t.Fatal("single digit")
  15  	}
  16  	if i64str(1234567890) != "1234567890" {
  17  		t.Fatal("long")
  18  	}
  19  	// i64str(-42) is not asserted: stage4 zero-extends a negative constant
  20  	// argument to an int64 parameter, so -42 arrives as 4294967254 (legacy
  21  	// computes -42). i64str is only ever called with a non-negative count, so
  22  	// the reporter is unaffected; the compiler bug is recorded in
  23  	// docs/relay-test-coverage.md with this call as its repro.
  24  	if i32str(2147) != "2147" {
  25  		t.Fatal("int32 wrapper")
  26  	}
  27  }
  28  
  29  func TestPct(t *testing.T) {
  30  	if pct(0, 0) != "  0.0%" {
  31  		t.Fatal("empty denominator")
  32  	}
  33  	if pct(1, 1) != "100.0%" {
  34  		t.Fatal("all")
  35  	}
  36  	if pct(0, 4) != "0.0%" {
  37  		t.Fatal("none")
  38  	}
  39  	if pct(1, 3) != "33.3%" {
  40  		t.Fatal("one third")
  41  	}
  42  	if pct(2, 3) != "66.6%" {
  43  		t.Fatal("two thirds")
  44  	}
  45  	if pct(1, 8) != "12.5%" {
  46  		t.Fatal("eighth")
  47  	}
  48  	// three quarters of a large count must not overflow the int64 product
  49  	if pct(300000000, 400000000) != "75.0%" {
  50  		t.Fatal("large counts")
  51  	}
  52  }
  53  
  54  func TestParseU64(t *testing.T) {
  55  	if parseU64("0") != 0 {
  56  		t.Fatal("zero")
  57  	}
  58  	if parseU64("123456789012") != 123456789012 {
  59  		t.Fatal("large")
  60  	}
  61  	if parseU64("12x") != 12 {
  62  		t.Fatal("stops at a non-digit")
  63  	}
  64  	if parseU64("") != 0 {
  65  		t.Fatal("empty")
  66  	}
  67  }
  68  
  69  func TestParseCounts(t *testing.T) {
  70  	dump := "coversite 0 count=1\n" |
  71  		"coversite 7 count=42\n" |
  72  		"noise\n" |
  73  		"coversite 9\n" |
  74  		"coversite 11 count=\n" |
  75  		"  coversite 12 count=3  \n"
  76  	m := parseCounts(dump)
  77  	if m["0"] != 1 || m["7"] != 42 || m["12"] != 3 {
  78  		t.Fatal("valid lines")
  79  	}
  80  	// Three hits plus the empty count, which parses as zero rather than
  81  	// being dropped; a zero is what mxcover reports as uncovered either way.
  82  	if len(m) != 4 {
  83  		t.Fatal("coversite lines only: got " | i32str(int32(len(m))))
  84  	}
  85  	if _, ok := m["9"]; ok {
  86  		t.Fatal("a line with no count must be skipped")
  87  	}
  88  	if m["11"] != 0 {
  89  		t.Fatal("an empty count is zero, not a hit")
  90  	}
  91  }
  92  
  93  func TestParseSitesCountsHits(t *testing.T) {
  94  	sites := "[{\"id\":0,\"name\":\"pkg.F@a.mx:3\"},{\"id\":1,\"name\":\"pkg.G@a.mx:9\"}]"
  95  	hits := map[string]uint64{"1": 5}
  96  	recs := parseSites(sites, hits, "")
  97  	if len(recs) != 2 {
  98  		t.Fatal("site count")
  99  	}
 100  	// sorted by file then line
 101  	if recs[0].line != 3 || recs[0].hits != 0 {
 102  		t.Fatal("first site")
 103  	}
 104  	if recs[1].line != 9 || recs[1].hits != 5 {
 105  		t.Fatal("second site took its hits from the dump")
 106  	}
 107  	if recs[0].fn != "pkg.F" {
 108  		t.Fatal("function name is the part before @")
 109  	}
 110  }
 111  
 112  func TestParseSitesPrefixAndSyntheticSites(t *testing.T) {
 113  	sites := "[{\"id\":0,\"name\":\"pkg.F@mod/a.mx:3\"},{\"id\":1,\"name\":\"pkg.G@other/a.mx:4\"},{\"id\":2,\"name\":\"main.init@:0\"}]"
 114  	all := parseSites(sites, map[string]uint64{}, "")
 115  	if len(all) != 2 {
 116  		t.Fatal("a site with no source file is synthetic and dropped")
 117  	}
 118  	one := parseSites(sites, map[string]uint64{}, "mod/")
 119  	if len(one) != 1 {
 120  		t.Fatal("the prefix selects one file")
 121  	}
 122  	if one[0].file != "mod/a.mx" {
 123  		t.Fatal("prefix keeps the module-relative name")
 124  	}
 125  	none := parseSites(sites, map[string]uint64{}, "nomatch/")
 126  	if len(none) != 0 {
 127  		t.Fatal("a prefix that matches nothing selects nothing")
 128  	}
 129  }
 130  
 131  func TestSortRecsOrdersFileLineFunction(t *testing.T) {
 132  	rs := []rec{
 133  		{fn: "b", file: "z.mx", line: 1},
 134  		{fn: "a", file: "a.mx", line: 9},
 135  		{fn: "b", file: "a.mx", line: 9},
 136  		{fn: "a", file: "a.mx", line: 2},
 137  	}
 138  	sortRecs(rs)
 139  	want := []string{"a.mx:2:a", "a.mx:9:a", "a.mx:9:b", "z.mx:1:b"}
 140  	for i := int32(0); i < int32(len(rs)); i++ {
 141  		got := rs[i].file | ":" | i32str(rs[i].line) | ":" | rs[i].fn
 142  		if got != want[i] {
 143  			t.Fatal("sorted order is file, then line, then function: " | got)
 144  		}
 145  	}
 146  }
 147  
 148  func TestIndexHelpers(t *testing.T) {
 149  	if indexOf("abcabc", 0, "bc") != 1 {
 150  		t.Fatal("first index")
 151  	}
 152  	if indexOf("abcabc", 2, "bc") != 4 {
 153  		t.Fatal("index from an offset")
 154  	}
 155  	if indexOf("abc", 0, "z") != -1 {
 156  		t.Fatal("absent")
 157  	}
 158  	if lastIndexOf("a/b/c", '/') != 3 {
 159  		t.Fatal("last index")
 160  	}
 161  	if lastIndexOf("abc", '/') != -1 {
 162  		t.Fatal("absent separator")
 163  	}
 164  	if scanNumber("12ab", 0) != "12" {
 165  		t.Fatal("scanNumber stops at a non-digit")
 166  	}
 167  	if scanNumber("x", 0) != "" {
 168  		t.Fatal("scanNumber on no digits")
 169  	}
 170  	if parseInt32("202") != 202 {
 171  		t.Fatal("parseInt32")
 172  	}
 173  	if parseInt32("3x") != 3 {
 174  		t.Fatal("parseInt32 stops at a non-digit")
 175  	}
 176  }
 177  
 178  func TestPrefixNote(t *testing.T) {
 179  	if prefixNote("") != "" {
 180  		t.Fatal("no prefix, no note")
 181  	}
 182  	if prefixNote("mod/") != " under mod/" {
 183  		t.Fatal("prefix is named")
 184  	}
 185  }
 186