github.com/lixvbnet/courtney@v0.0.0-20221025031132-0dcb02231211/tester/tester_test.go (about) 1 package tester_test 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "path" 8 "path/filepath" 9 "reflect" 10 "regexp" 11 "strconv" 12 "strings" 13 "testing" 14 15 "github.com/lixvbnet/courtney/shared" 16 "github.com/lixvbnet/courtney/tester" 17 "github.com/dave/patsy" 18 "github.com/dave/patsy/builder" 19 "github.com/dave/patsy/vos" 20 "golang.org/x/tools/cover" 21 ) 22 23 func TestTester_ProcessExcludes(t *testing.T) { 24 for _, gomod := range []bool{true, false} { 25 t.Run(fmt.Sprintf("gomod=%v", gomod), func(t *testing.T) { 26 env := vos.Mock() 27 b, err := builder.New(env, "ns", gomod) 28 if err != nil { 29 t.Fatalf("Error creating builder in %s", err) 30 } 31 defer b.Cleanup() 32 33 _, pdir, err := b.Package("a", map[string]string{ 34 "a.go": `package a`, 35 }) 36 if err != nil { 37 t.Fatalf("Error creating temp package: %s", err) 38 } 39 40 setup := &shared.Setup{ 41 Env: env, 42 Paths: patsy.NewCache(env), 43 } 44 ts := tester.New(setup) 45 ts.Results = []*cover.Profile{ 46 { 47 FileName: "ns/a/a.go", 48 Blocks: []cover.ProfileBlock{ 49 {Count: 1, StartLine: 1, EndLine: 10}, 50 {Count: 0, StartLine: 11, EndLine: 20}, 51 {Count: 1, StartLine: 21, EndLine: 30}, 52 {Count: 0, StartLine: 31, EndLine: 40}, 53 }, 54 }, 55 } 56 excludes := map[string]map[int]bool{ 57 filepath.Join(pdir, "a.go"): { 58 25: true, 59 35: true, 60 }, 61 } 62 expected := []cover.ProfileBlock{ 63 {Count: 1, StartLine: 1, EndLine: 10}, 64 {Count: 0, StartLine: 11, EndLine: 20}, 65 {Count: 1, StartLine: 21, EndLine: 30}, 66 } 67 if err := ts.ProcessExcludes(excludes); err != nil { 68 t.Fatalf("Processing excludes: %s", err) 69 } 70 if !reflect.DeepEqual(ts.Results[0].Blocks, expected) { 71 t.Fatalf("Processing excludes - got:\n%#v\nexpected:\n%#v\n", ts.Results[0].Blocks, expected) 72 } 73 }) 74 } 75 } 76 77 func TestTester_Enforce(t *testing.T) { 78 for _, gomod := range []bool{true, false} { 79 t.Run(fmt.Sprintf("gomod=%v", gomod), func(t *testing.T) { 80 env := vos.Mock() 81 setup := &shared.Setup{ 82 Env: env, 83 Paths: patsy.NewCache(env), 84 Enforce: true, 85 } 86 b, err := builder.New(env, "ns", gomod) 87 if err != nil { 88 t.Fatalf("Error creating builder: %s", err) 89 } 90 defer b.Cleanup() 91 92 _, _, _ = b.Package("a", map[string]string{ 93 "a.go": "package a\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20", 94 }) 95 96 ts := tester.New(setup) 97 ts.Results = []*cover.Profile{ 98 { 99 FileName: "ns/a/a.go", 100 Mode: "b", 101 Blocks: []cover.ProfileBlock{ 102 {Count: 1}, 103 }, 104 }, 105 } 106 if err := ts.Enforce(); err != nil { 107 t.Fatalf("Error enforcing: %s", err) 108 } 109 110 ts.Results[0].Blocks = []cover.ProfileBlock{ 111 {Count: 1, StartLine: 1, EndLine: 2}, 112 {Count: 0, StartLine: 6, EndLine: 11}, 113 } 114 err = ts.Enforce() 115 if err == nil { 116 t.Fatal("Error enforcing - should get error, got nil") 117 } 118 expected := "Error - untested code:\nns/a/a.go:6-11:\n\t5\n\t6\n\t7\n\t8\n\t9\n\t10" 119 if err.Error() != expected { 120 t.Fatalf("Error enforcing - got \n%s\nexpected:\n%s\n", strconv.Quote(err.Error()), strconv.Quote(expected)) 121 } 122 123 // check that blocks next to each other are merged 124 ts.Results[0].Blocks = []cover.ProfileBlock{ 125 {Count: 1, StartLine: 1, EndLine: 2}, 126 {Count: 0, StartLine: 6, EndLine: 11}, 127 {Count: 0, StartLine: 12, EndLine: 16}, 128 {Count: 0, StartLine: 18, EndLine: 21}, 129 } 130 err = ts.Enforce() 131 if err == nil { 132 t.Fatal("Error enforcing - should get error, got nil") 133 } 134 expected = "Error - untested code:\nns/a/a.go:6-16:\n\t5\n\t6\n\t7\n\t8\n\t9\n\t10\n\t11\n\t12\n\t13\n\t14\n\t15ns/a/a.go:18-21:\n\t17\n\t18\n\t19\n\t20" 135 if err.Error() != expected { 136 t.Fatalf("Error enforcing - got \n%s\nexpected:\n%s\n", strconv.Quote(err.Error()), strconv.Quote(expected)) 137 } 138 }) 139 } 140 } 141 142 func TestTester_Save_output(t *testing.T) { 143 env := vos.Mock() 144 dir, err := ioutil.TempDir("", "") 145 if err != nil { 146 t.Fatalf("Error creating temp dir: %s", err) 147 } 148 out := filepath.Join(dir, "foo.bar") 149 setup := &shared.Setup{ 150 Env: env, 151 Paths: patsy.NewCache(env), 152 Output: out, 153 } 154 ts := tester.New(setup) 155 ts.Results = []*cover.Profile{ 156 { 157 FileName: "a", 158 Mode: "b", 159 Blocks: []cover.ProfileBlock{{}}, 160 }, 161 } 162 if err := ts.Save(); err != nil { 163 t.Fatalf("Error saving: %s", err) 164 } 165 if _, err := ioutil.ReadFile(out); err != nil { 166 t.Fatalf("Error loading coverage: %s", err) 167 } 168 } 169 170 func TestTester_Save_no_results(t *testing.T) { 171 env := vos.Mock() 172 sout := &bytes.Buffer{} 173 serr := &bytes.Buffer{} 174 env.Setstdout(sout) 175 env.Setstderr(serr) 176 setup := &shared.Setup{ 177 Env: env, 178 Paths: patsy.NewCache(env), 179 } 180 ts := tester.New(setup) 181 if err := ts.Save(); err != nil { 182 t.Fatalf("Error saving: %s", err) 183 } 184 expected := "No results\n" 185 if sout.String() != expected { 186 t.Fatalf("Error saving, stdout: got:\n%s\nexpected:\n%s\n", sout.String(), expected) 187 } 188 } 189 190 func TestTester_Test(t *testing.T) { 191 192 type args []string 193 type files map[string]string 194 type packages map[string]files 195 type test struct { 196 args args 197 packages packages 198 } 199 200 tests := map[string]test{ 201 "simple": { 202 args: args{"ns/..."}, 203 packages: packages{ 204 "a": files{ 205 "a.go": `package a 206 func Foo(i int) int { 207 i++ // 0 208 return i 209 } 210 `, 211 "a_test.go": `package a`, 212 }, 213 }, 214 }, 215 "simple test": { 216 args: args{"ns/..."}, 217 packages: packages{ 218 "a": files{ 219 "a.go": `package a 220 221 func Foo(i int) int { 222 i++ // 1 223 return i 224 } 225 226 func Bar(i int) int { 227 i++ // 0 228 return i 229 } 230 `, 231 "a_test.go": `package a 232 233 import "testing" 234 235 func TestFoo(t *testing.T) { 236 i := Foo(1) 237 if i != 2 { 238 t.Fail() 239 } 240 } 241 `, 242 }, 243 }, 244 }, 245 "cross package test": { 246 args: args{"ns/a", "ns/b"}, 247 packages: packages{ 248 "a": files{ 249 "a.go": `package a 250 251 func Foo(i int) int { 252 i++ // 1 253 return i 254 } 255 256 func Bar(i int) int { 257 i++ // 1 258 return i 259 } 260 `, 261 "a_test.go": `package a 262 263 import "testing" 264 265 func TestFoo(t *testing.T) { 266 i := Foo(1) 267 if i != 2 { 268 t.Fail() 269 } 270 } 271 `, 272 }, 273 "b": files{ 274 "b_exclude.go": `package b`, 275 "b_test.go": `package b 276 277 import ( 278 "testing" 279 "ns/a" 280 ) 281 282 func TestBar(t *testing.T) { 283 i := a.Bar(1) 284 if i != 2 { 285 t.Fail() 286 } 287 } 288 `, 289 }, 290 }, 291 }, 292 } 293 294 for name, test := range tests { 295 for _, gomod := range []bool{true, false} { 296 t.Run(fmt.Sprintf("%s,gomod=%v", name, gomod), func(t *testing.T) { 297 env := vos.Mock() 298 b, err := builder.New(env, "ns", gomod) 299 if err != nil { 300 t.Fatalf("Error creating builder in %s: %+v", name, err) 301 } 302 defer b.Cleanup() 303 304 for pname, files := range test.packages { 305 if _, _, err := b.Package(pname, files); err != nil { 306 t.Fatalf("Error creating package %s in %s: %+v", pname, name, err) 307 } 308 } 309 310 paths := patsy.NewCache(env) 311 312 setup := &shared.Setup{ 313 Env: env, 314 Paths: paths, 315 } 316 if err := setup.Parse(test.args); err != nil { 317 t.Fatalf("Error in '%s' parsing args: %+v", name, err) 318 } 319 320 ts := tester.New(setup) 321 322 if err := ts.Test(); err != nil { 323 t.Fatalf("Error in '%s' while running test: %+v", name, err) 324 } 325 326 fmt.Printf("Results: %#v\n", ts.Results) 327 328 filesInOutput := map[string]bool{} 329 for _, p := range ts.Results { 330 331 filesInOutput[p.FileName] = true 332 pkg, fname := path.Split(p.FileName) 333 pkg = strings.TrimSuffix(pkg, "/") 334 dir, err := patsy.Dir(env, pkg) 335 if err != nil { 336 t.Fatalf("Error in '%s' while getting dir from package: %+v", name, err) 337 } 338 src, err := ioutil.ReadFile(filepath.Join(dir, fname)) 339 if err != nil { 340 t.Fatalf("Error in '%s' while opening coverage: %+v", name, err) 341 } 342 lines := strings.Split(string(src), "\n") 343 matched := map[int]bool{} 344 for _, b := range p.Blocks { 345 if !strings.HasSuffix(lines[b.StartLine], fmt.Sprintf("// %d", b.Count)) { 346 t.Fatalf("Error in '%s' - incorrect count %d at %s line %d", name, b.Count, p.FileName, b.StartLine) 347 } 348 matched[b.StartLine] = true 349 } 350 for i, line := range lines { 351 if annotatedLine.MatchString(line) { 352 if _, ok := matched[i]; !ok { 353 t.Fatalf("Error in '%s' - annotated line doesn't match a coverage block as %s line %d", name, p.FileName, i) 354 } 355 } 356 } 357 } 358 fmt.Printf("%#v\n", filesInOutput) 359 for pname, files := range test.packages { 360 for fname := range files { 361 if strings.HasSuffix(fname, ".mod") { 362 continue 363 } 364 if strings.HasSuffix(fname, "_test.go") { 365 continue 366 } 367 if strings.HasSuffix(fname, "_exclude.go") { 368 // so we can have simple source files with no logic 369 // blocks 370 continue 371 } 372 fullFilename := path.Join("ns", pname, fname) 373 fmt.Println(fullFilename) 374 if _, ok := filesInOutput[fullFilename]; !ok { 375 t.Fatalf("Error in '%s' - %s does not appear in coverge output", name, fullFilename) 376 } 377 } 378 } 379 }) 380 } 381 } 382 } 383 384 var annotatedLine = regexp.MustCompile(`// \d+$`)