github.com/thockin/go2make@v0.0.0-20221008213743-c1956c0434a7/go2make_test.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "bytes" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 "strings" 25 "testing" 26 27 "github.com/google/go-cmp/cmp" 28 "github.com/lithammer/dedent" 29 "golang.org/x/tools/go/packages" 30 ) 31 32 func initModule(t *testing.T, name string, files map[string]string) string { 33 dir := t.TempDir() 34 writeFile(t, dir, "go.mod", dedent.Dedent(` 35 module example.com/mod 36 go 1.18 37 `)) 38 for path, content := range files { 39 writeFile(t, dir, path, content) 40 } 41 return dir 42 } 43 44 func writeFile(t *testing.T, dir, path, content string) { 45 path = filepath.Join(dir, path) 46 if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { 47 t.Fatal(err) 48 } 49 if err := ioutil.WriteFile(path, []byte(content), 0644); err != nil { 50 t.Fatal(err) 51 } 52 } 53 54 // build a map of pkg -> filenames 55 func pkgFiles(pkgs []*packages.Package) map[string][]string { 56 out := make(map[string][]string, len(pkgs)) 57 for _, p := range pkgs { 58 skip := false 59 for _, e := range p.Errors { 60 // Some test cases try bad patterns on purpose. 61 if e.Kind == packages.ListError { 62 skip = true 63 break 64 } 65 } 66 if skip { 67 continue 68 } 69 files := make([]string, 0, len(p.GoFiles)) 70 for _, f := range p.GoFiles { 71 files = append(files, filepath.Base(f)) 72 } 73 out[p.PkgPath] = files 74 } 75 return out 76 } 77 78 func TestLoadPackages(t *testing.T) { 79 cases := []struct { 80 name string 81 files map[string]string 82 tags []string 83 expectPkgs map[string][]string 84 }{{ 85 name: "one_pkg_no_imports", 86 files: map[string]string{ 87 "file.go": dedent.Dedent(` 88 package p 89 var V string 90 `), 91 }, 92 expectPkgs: map[string][]string{ 93 "example.com/mod": {"file.go"}, 94 }, 95 }, { 96 name: "one_pkg_with_other_files", 97 files: map[string]string{ 98 "README": "", 99 "file.go": dedent.Dedent(` 100 package p 101 var V string 102 `), 103 "file_test.go": dedent.Dedent(` 104 package p 105 var T string 106 `), 107 "_ignore.go": dedent.Dedent(` 108 package p 109 var I string 110 `), 111 }, 112 expectPkgs: map[string][]string{ 113 "example.com/mod": {"file.go"}, 114 }, 115 }, { 116 name: "one_pkg_with_imports", 117 files: map[string]string{ 118 "file.go": dedent.Dedent(` 119 package p 120 import "io" 121 import "os" 122 func init() { io.WriteString(os.Stdout, "") } 123 `), 124 }, 125 expectPkgs: map[string][]string{ 126 "example.com/mod": {"file.go"}, 127 }, 128 }, { 129 name: "multi_pkg_no_imports", 130 files: map[string]string{ 131 "p1/file1.go": dedent.Dedent(` 132 package p1 133 var V string 134 `), 135 "p2/file2.go": dedent.Dedent(` 136 package p2 137 var V string 138 `), 139 "p3/file3.go": dedent.Dedent(` 140 package p3 141 var V string 142 `), 143 }, 144 expectPkgs: map[string][]string{ 145 "example.com/mod/p1": {"file1.go"}, 146 "example.com/mod/p2": {"file2.go"}, 147 "example.com/mod/p3": {"file3.go"}, 148 }, 149 }, { 150 name: "multi_pkg_with_imports", 151 files: map[string]string{ 152 "p1/file1.go": dedent.Dedent(` 153 package p1 154 import "io" 155 import "os" 156 func init() { io.WriteString(os.Stdout, "") } 157 `), 158 "p2/file2.go": dedent.Dedent(` 159 package p2 160 import "io" 161 import "os" 162 func init() { io.WriteString(os.Stdout, "") } 163 `), 164 "p3/file3.go": dedent.Dedent(` 165 package p3 166 import "io" 167 import "os" 168 func init() { io.WriteString(os.Stdout, "") } 169 `), 170 }, 171 expectPkgs: map[string][]string{ 172 "example.com/mod/p1": {"file1.go"}, 173 "example.com/mod/p2": {"file2.go"}, 174 "example.com/mod/p3": {"file3.go"}, 175 }, 176 }, { 177 name: "multi_pkg_with_tags", 178 tags: []string{"foo"}, 179 files: map[string]string{ 180 "p1/file1.go": dedent.Dedent(` 181 package p1 182 var V string 183 `), 184 "p2/file2.go": dedent.Dedent(` 185 //go:build !foo 186 // +build !foo 187 package p2 188 var V string 189 `), 190 "p3/file3.go": dedent.Dedent(` 191 //go:build foo 192 // +build foo 193 package p3 194 var V string 195 `), 196 }, 197 expectPkgs: map[string][]string{ 198 "example.com/mod/p1": {"file1.go"}, 199 "example.com/mod/p3": {"file3.go"}, 200 }, 201 }, { 202 name: "multi_pkg_multi_file_with_tags", 203 tags: []string{"foo", "bar"}, 204 files: map[string]string{ 205 "p1/file1a.go": dedent.Dedent(` 206 package p1 207 var Va string 208 `), 209 "p1/file1b.go": dedent.Dedent(` 210 package p1 211 var Vb string 212 `), 213 "p2/file2a.go": dedent.Dedent(` 214 //go:build !foo 215 // +build !foo 216 package p2 217 var Va string 218 `), 219 "p2/file2b.go": dedent.Dedent(` 220 package p2 221 var Vb string 222 `), 223 "p3/file3a.go": dedent.Dedent(` 224 //go:build bar 225 // +build bar 226 package p3 227 var Va string 228 `), 229 "p3/file3b.go": dedent.Dedent(` 230 package p3 231 var Vb string 232 `), 233 }, 234 expectPkgs: map[string][]string{ 235 "example.com/mod/p1": {"file1a.go", "file1b.go"}, 236 "example.com/mod/p2": {"file2b.go"}, 237 "example.com/mod/p3": {"file3a.go", "file3b.go"}, 238 }, 239 }} 240 241 wd, err := os.Getwd() 242 if err != nil { 243 t.Fatal(err) 244 } 245 246 for _, tc := range cases { 247 t.Run(tc.name, func(t *testing.T) { 248 dir := initModule(t, "example.com/mod", tc.files) 249 250 emit := emitter{ 251 tags: tc.tags, 252 } 253 254 // pushd 255 if err := os.Chdir(dir); err != nil { 256 t.Fatal(err) 257 } 258 259 for _, pattern := range []string{"example.com/mod/...", "./..."} { 260 pkgs, err := emit.loadPackages(pattern) 261 if err != nil { 262 t.Errorf("unexpected error: %v", err) 263 } 264 if want, got := tc.expectPkgs, pkgFiles(pkgs); !cmp.Equal(want, got) { 265 t.Errorf("wrong result for pattern %q:\n\twant: %v\n\t got: %v", pattern, want, got) 266 } 267 } 268 269 // popd 270 if err := os.Chdir(wd); err != nil { 271 t.Fatal(err) 272 } 273 }) 274 } 275 } 276 277 func TestLoadPackagesMultiModule(t *testing.T) { 278 cases := []struct { 279 name string 280 files map[string]string 281 tags []string 282 expectPkgs map[string][]string 283 testSubByPath bool 284 }{{ 285 name: "one_pkg", 286 files: map[string]string{ 287 "file.go": dedent.Dedent(` 288 package p 289 var V string 290 `), 291 }, 292 expectPkgs: map[string][]string{ 293 "example.com/mod": {"file.go"}, 294 }, 295 }, { 296 name: "multi_pkg", 297 files: map[string]string{ 298 "p1/file1.go": dedent.Dedent(` 299 package p1 300 var V string 301 `), 302 "p2/file2.go": dedent.Dedent(` 303 package p2 304 var V string 305 `), 306 "p3/file3.go": dedent.Dedent(` 307 package p3 308 var V string 309 `), 310 }, 311 expectPkgs: map[string][]string{ 312 "example.com/mod/p1": {"file1.go"}, 313 "example.com/mod/p2": {"file2.go"}, 314 "example.com/mod/p3": {"file3.go"}, 315 }, 316 }, { 317 name: "multi_module_no_workspace", 318 files: map[string]string{ 319 "p1/file1.go": dedent.Dedent(` 320 package p1 321 var V string 322 `), 323 "m2/go.mod": dedent.Dedent(` 324 module example.com/m2 325 go 1.18 326 `), 327 "m2/file2.go": dedent.Dedent(` 328 package m2 329 var V string 330 `), 331 "m3/go.mod": dedent.Dedent(` 332 module example.com/m3 333 go 1.18 334 `), 335 "m3/file3.go": dedent.Dedent(` 336 package m3 337 var V string 338 `), 339 }, 340 expectPkgs: map[string][]string{ 341 "example.com/mod/p1": {"file1.go"}, 342 }, 343 }, { 344 name: "multi_module_workspace", 345 files: map[string]string{ 346 "go.work": dedent.Dedent(` 347 go 1.18 348 use ( 349 . 350 ./m2 351 ) 352 replace ( 353 example.com/m2 v0.0.0 => ./m2 354 ) 355 `), 356 "p1/file1.go": dedent.Dedent(` 357 package p1 358 var V string 359 `), 360 "m2/go.mod": dedent.Dedent(` 361 module example.com/m2 362 go 1.18 363 `), 364 "m2/file2.go": dedent.Dedent(` 365 package m2 366 var V string 367 `), 368 "m3/go.mod": dedent.Dedent(` 369 module example.com/m3 370 go 1.18 371 `), 372 "m3/file3.go": dedent.Dedent(` 373 package m3 374 var V string 375 `), 376 }, 377 expectPkgs: map[string][]string{ 378 "example.com/mod/p1": {"file1.go"}, 379 "example.com/m2": {"file2.go"}, 380 }, 381 }} 382 383 wd, err := os.Getwd() 384 if err != nil { 385 t.Fatal(err) 386 } 387 388 for _, tc := range cases { 389 t.Run(tc.name, func(t *testing.T) { 390 emit := emitter{ 391 tags: tc.tags, 392 } 393 394 // pushd 395 dir := initModule(t, "example.com/mod", tc.files) 396 if err := os.Chdir(dir); err != nil { 397 t.Fatal(err) 398 } 399 400 for _, pattern := range [][]string{{"example.com/mod/...", "example.com/m2/..."}, {"./...", "./m2/..."}, {"all"}} { 401 pkgs, err := emit.loadPackages(pattern...) 402 if err != nil { 403 t.Errorf("unexpected error: %v", err) 404 } 405 if want, got := tc.expectPkgs, pkgFiles(pkgs); !cmp.Equal(want, got) { 406 t.Errorf("wrong result for pattern(s) %q:\n\twant: %v\n\t got: %v", pattern, want, got) 407 } 408 } 409 410 // popd 411 if err := os.Chdir(wd); err != nil { 412 t.Fatal(err) 413 } 414 }) 415 } 416 } 417 418 func TestVisitPackage(t *testing.T) { 419 pkgpath := "example.com/mod/pkg" 420 421 cases := []struct { 422 name string 423 pkg packages.Package 424 initMap func(pkgMap map[string]*packages.Package, pkg *packages.Package) // optional 425 expectErrs bool 426 }{{ 427 name: "already_present", 428 pkg: packages.Package{ 429 PkgPath: pkgpath, 430 }, 431 initMap: func(pkgMap map[string]*packages.Package, pkg *packages.Package) { 432 pkgMap[pkgpath] = pkg 433 }, 434 }, { 435 name: "success", 436 pkg: packages.Package{ 437 PkgPath: pkgpath, 438 }, 439 }, { 440 name: "list_error", 441 pkg: packages.Package{ 442 PkgPath: pkgpath, 443 Errors: []packages.Error{{Kind: packages.ListError}}, 444 }, 445 expectErrs: true, 446 }, { 447 name: "parse_error", 448 pkg: packages.Package{ 449 PkgPath: pkgpath, 450 Errors: []packages.Error{{ 451 Kind: packages.ParseError, 452 }}, 453 }, 454 expectErrs: true, 455 }} 456 457 for _, tc := range cases { 458 t.Run(tc.name, func(t *testing.T) { 459 pkgMap := map[string]*packages.Package{} 460 if tc.initMap != nil { 461 tc.initMap(pkgMap, &tc.pkg) 462 } 463 emit := emitter{} 464 465 ok := emit.visitPackage(&tc.pkg, pkgMap) 466 if ok && tc.expectErrs { 467 t.Errorf("unexpected success") 468 } 469 if !ok && !tc.expectErrs { 470 t.Errorf("unexpected failure") 471 } 472 if want, got := 1, len(pkgMap); want != got { 473 t.Errorf("unexpected number of packages: want %d, got %d, %v", want, got, pkgMap) 474 } 475 if p, found := pkgMap[pkgpath]; !found { 476 t.Errorf("package %q not found in map: %v", pkgpath, pkgMap) 477 } else if p != &tc.pkg { 478 t.Errorf("package %q in map is different pointer: %v", pkgpath, p) 479 } 480 }) 481 } 482 } 483 484 func TestEmitMake(t *testing.T) { 485 cases := []struct { 486 name string 487 files map[string]string 488 tags []string 489 expect string 490 }{{ 491 name: "one_pkg_no_imports", 492 files: map[string]string{ 493 "file.go": dedent.Dedent(` 494 package p 495 var V string 496 `), 497 }, 498 expect: dedent.Dedent(` 499 .go2make/by-pkg/./m2/.../_pkg: 500 @mkdir -p $(@D) 501 @touch $@ 502 503 .go2make/by-pkg/./m3/.../_pkg: 504 @mkdir -p $(@D) 505 @touch $@ 506 507 .go2make/by-pkg/example.com/mod/_files: ./ 508 @mkdir -p $(@D) 509 @ls $</*.go | LC_ALL=C sort > $@.tmp 510 @if ! cmp -s $@.tmp $@; then \ 511 cat $@.tmp > $@; \ 512 fi 513 @rm -f $@.tmp 514 515 .go2make/by-pkg/example.com/mod/_pkg: .go2make/by-pkg/example.com/mod/_files \ 516 ./file.go 517 @mkdir -p $(@D) 518 @touch $@ 519 520 .go2make/by-path/./_pkg: .go2make/by-pkg/example.com/mod/_pkg 521 @mkdir -p $(@D) 522 @touch $@ 523 `), 524 }, { 525 name: "one_pkg_with_other_files", 526 files: map[string]string{ 527 "README": "", 528 "file.go": dedent.Dedent(` 529 package p 530 var V string 531 `), 532 "file_test.go": dedent.Dedent(` 533 package p 534 import "io" 535 import "os" 536 func init() { io.WriteString(os.Stdout, "") } 537 `), 538 "_ignore.go": dedent.Dedent(` 539 package p 540 import "io" 541 import "os" 542 func init() { io.WriteString(os.Stdout, "") } 543 `), 544 }, 545 expect: dedent.Dedent(` 546 .go2make/by-pkg/./m2/.../_pkg: 547 @mkdir -p $(@D) 548 @touch $@ 549 550 .go2make/by-pkg/./m3/.../_pkg: 551 @mkdir -p $(@D) 552 @touch $@ 553 554 .go2make/by-pkg/example.com/mod/_files: ./ 555 @mkdir -p $(@D) 556 @ls $</*.go | LC_ALL=C sort > $@.tmp 557 @if ! cmp -s $@.tmp $@; then \ 558 cat $@.tmp > $@; \ 559 fi 560 @rm -f $@.tmp 561 562 .go2make/by-pkg/example.com/mod/_pkg: .go2make/by-pkg/example.com/mod/_files \ 563 ./file.go 564 @mkdir -p $(@D) 565 @touch $@ 566 567 .go2make/by-path/./_pkg: .go2make/by-pkg/example.com/mod/_pkg 568 @mkdir -p $(@D) 569 @touch $@ 570 `), 571 }, { 572 name: "one_pkg_with_imports", 573 files: map[string]string{ 574 "file.go": dedent.Dedent(` 575 package p 576 import "io" 577 import "os" 578 func init() { io.WriteString(os.Stdout, "") } 579 `), 580 }, 581 expect: dedent.Dedent(` 582 .go2make/by-pkg/./m2/.../_pkg: 583 @mkdir -p $(@D) 584 @touch $@ 585 586 .go2make/by-pkg/./m3/.../_pkg: 587 @mkdir -p $(@D) 588 @touch $@ 589 590 .go2make/by-pkg/example.com/mod/_files: ./ 591 @mkdir -p $(@D) 592 @ls $</*.go | LC_ALL=C sort > $@.tmp 593 @if ! cmp -s $@.tmp $@; then \ 594 cat $@.tmp > $@; \ 595 fi 596 @rm -f $@.tmp 597 598 .go2make/by-pkg/example.com/mod/_pkg: .go2make/by-pkg/example.com/mod/_files \ 599 ./file.go 600 @mkdir -p $(@D) 601 @touch $@ 602 603 .go2make/by-path/./_pkg: .go2make/by-pkg/example.com/mod/_pkg 604 @mkdir -p $(@D) 605 @touch $@ 606 `), 607 }, { 608 name: "multi_pkg_no_imports", 609 files: map[string]string{ 610 "p1/file1.go": dedent.Dedent(` 611 package p1 612 var V string 613 `), 614 "p2/file2.go": dedent.Dedent(` 615 package p2 616 import "example.com/mod/p1" 617 var V = p1.V 618 `), 619 "p3/file3.go": dedent.Dedent(` 620 package p3 621 import "example.com/mod/p1" 622 import "example.com/mod/p2" 623 var V = p1.V + p2.V 624 `), 625 }, 626 expect: dedent.Dedent(` 627 .go2make/by-pkg/./m2/.../_pkg: 628 @mkdir -p $(@D) 629 @touch $@ 630 631 .go2make/by-pkg/./m3/.../_pkg: 632 @mkdir -p $(@D) 633 @touch $@ 634 635 .go2make/by-pkg/example.com/mod/p1/_files: ./p1/ 636 @mkdir -p $(@D) 637 @ls $</*.go | LC_ALL=C sort > $@.tmp 638 @if ! cmp -s $@.tmp $@; then \ 639 cat $@.tmp > $@; \ 640 fi 641 @rm -f $@.tmp 642 643 .go2make/by-pkg/example.com/mod/p1/_pkg: .go2make/by-pkg/example.com/mod/p1/_files \ 644 ./p1/file1.go 645 @mkdir -p $(@D) 646 @touch $@ 647 648 .go2make/by-path/./p1/_pkg: .go2make/by-pkg/example.com/mod/p1/_pkg 649 @mkdir -p $(@D) 650 @touch $@ 651 652 .go2make/by-pkg/example.com/mod/p2/_files: ./p2/ 653 @mkdir -p $(@D) 654 @ls $</*.go | LC_ALL=C sort > $@.tmp 655 @if ! cmp -s $@.tmp $@; then \ 656 cat $@.tmp > $@; \ 657 fi 658 @rm -f $@.tmp 659 660 .go2make/by-pkg/example.com/mod/p2/_pkg: .go2make/by-pkg/example.com/mod/p2/_files \ 661 ./p2/file2.go \ 662 .go2make/by-pkg/example.com/mod/p1/_pkg 663 @mkdir -p $(@D) 664 @touch $@ 665 666 .go2make/by-path/./p2/_pkg: .go2make/by-pkg/example.com/mod/p2/_pkg 667 @mkdir -p $(@D) 668 @touch $@ 669 670 .go2make/by-pkg/example.com/mod/p3/_files: ./p3/ 671 @mkdir -p $(@D) 672 @ls $</*.go | LC_ALL=C sort > $@.tmp 673 @if ! cmp -s $@.tmp $@; then \ 674 cat $@.tmp > $@; \ 675 fi 676 @rm -f $@.tmp 677 678 .go2make/by-pkg/example.com/mod/p3/_pkg: .go2make/by-pkg/example.com/mod/p3/_files \ 679 ./p3/file3.go \ 680 .go2make/by-pkg/example.com/mod/p1/_pkg \ 681 .go2make/by-pkg/example.com/mod/p2/_pkg 682 @mkdir -p $(@D) 683 @touch $@ 684 685 .go2make/by-path/./p3/_pkg: .go2make/by-pkg/example.com/mod/p3/_pkg 686 @mkdir -p $(@D) 687 @touch $@ 688 `), 689 }, { 690 name: "multi_module_workspace", 691 files: map[string]string{ 692 "go.work": dedent.Dedent(` 693 go 1.18 694 use ( 695 . 696 ./m2 697 ) 698 replace ( 699 example.com/m2 v0.0.0 => ./m2 700 ) 701 `), 702 "p1/file1.go": dedent.Dedent(` 703 package p1 704 import "example.com/m2" 705 var V = m2.V 706 `), 707 "m2/go.mod": dedent.Dedent(` 708 module example.com/m2 709 go 1.18 710 `), 711 "m2/file2.go": dedent.Dedent(` 712 package m2 713 var V string 714 `), 715 "m3/go.mod": dedent.Dedent(` 716 module example.com/m3 717 go 1.18 718 `), 719 "m3/file3.go": dedent.Dedent(` 720 package m3 721 var V string 722 `), 723 }, 724 expect: dedent.Dedent(` 725 .go2make/by-pkg/./m3/.../_pkg: 726 @mkdir -p $(@D) 727 @touch $@ 728 729 .go2make/by-pkg/example.com/m2/_files: ./m2/ 730 @mkdir -p $(@D) 731 @ls $</*.go | LC_ALL=C sort > $@.tmp 732 @if ! cmp -s $@.tmp $@; then \ 733 cat $@.tmp > $@; \ 734 fi 735 @rm -f $@.tmp 736 737 .go2make/by-pkg/example.com/m2/_pkg: .go2make/by-pkg/example.com/m2/_files \ 738 ./m2/file2.go 739 @mkdir -p $(@D) 740 @touch $@ 741 742 .go2make/by-path/./m2/_pkg: .go2make/by-pkg/example.com/m2/_pkg 743 @mkdir -p $(@D) 744 @touch $@ 745 746 .go2make/by-pkg/example.com/mod/p1/_files: ./p1/ 747 @mkdir -p $(@D) 748 @ls $</*.go | LC_ALL=C sort > $@.tmp 749 @if ! cmp -s $@.tmp $@; then \ 750 cat $@.tmp > $@; \ 751 fi 752 @rm -f $@.tmp 753 754 .go2make/by-pkg/example.com/mod/p1/_pkg: .go2make/by-pkg/example.com/mod/p1/_files \ 755 ./p1/file1.go \ 756 .go2make/by-pkg/example.com/m2/_pkg 757 @mkdir -p $(@D) 758 @touch $@ 759 760 .go2make/by-path/./p1/_pkg: .go2make/by-pkg/example.com/mod/p1/_pkg 761 @mkdir -p $(@D) 762 @touch $@ 763 `), 764 }} 765 766 wd, err := os.Getwd() 767 if err != nil { 768 t.Fatal(err) 769 } 770 771 for _, tc := range cases { 772 t.Run(tc.name, func(t *testing.T) { 773 dir := initModule(t, "example.com/mod", tc.files) 774 775 emit := emitter{ 776 stateDir: ".go2make", 777 relPath: dir, 778 ignoreErrors: true, // easier output comparison 779 } 780 781 // pushd 782 if err := os.Chdir(dir); err != nil { 783 t.Fatal(err) 784 } 785 786 pkgs, err := emit.loadPackages("./...", "./m2/...", "./m3/...") 787 if err != nil { 788 t.Errorf("unexpected error: %v", err) 789 } 790 pkgMap := emit.visitPackages(pkgs) 791 if pkgMap == nil { 792 t.Errorf("unexpected error") 793 } 794 buf := bytes.Buffer{} 795 emit.emitMake(&buf, pkgMap) 796 if want, got := strings.Trim(tc.expect, "\n"), strings.Trim(buf.String(), "\n"); want != got { 797 t.Errorf("wrong result:\n%s", cmp.Diff(want, got)) 798 } 799 800 // popd 801 if err := os.Chdir(wd); err != nil { 802 t.Fatal(err) 803 } 804 }) 805 } 806 }