1package convert 2 3import ( 4 "fmt" 5 "go/build" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "regexp" 11) 12 13/* 14 * RewritePackage takes a name (eg: my-package/tools), finds its test files using 15 * Go's build package, and then rewrites them. A ginkgo test suite file will 16 * also be added for this package, and all of its child packages. 17 */ 18func RewritePackage(packageName string) { 19 pkg, err := packageWithName(packageName) 20 if err != nil { 21 panic(fmt.Sprintf("unexpected error reading package: '%s'\n%s\n", packageName, err.Error())) 22 } 23 24 for _, filename := range findTestsInPackage(pkg) { 25 rewriteTestsInFile(filename) 26 } 27} 28 29/* 30 * Given a package, findTestsInPackage reads the test files in the directory, 31 * and then recurses on each child package, returning a slice of all test files 32 * found in this process. 33 */ 34func findTestsInPackage(pkg *build.Package) (testfiles []string) { 35 for _, file := range append(pkg.TestGoFiles, pkg.XTestGoFiles...) { 36 testfile, _ := filepath.Abs(filepath.Join(pkg.Dir, file)) 37 testfiles = append(testfiles, testfile) 38 } 39 40 dirFiles, err := ioutil.ReadDir(pkg.Dir) 41 if err != nil { 42 panic(fmt.Sprintf("unexpected error reading dir: '%s'\n%s\n", pkg.Dir, err.Error())) 43 } 44 45 re := regexp.MustCompile(`^[._]`) 46 47 for _, file := range dirFiles { 48 if !file.IsDir() { 49 continue 50 } 51 52 if re.Match([]byte(file.Name())) { 53 continue 54 } 55 56 packageName := filepath.Join(pkg.ImportPath, file.Name()) 57 subPackage, err := packageWithName(packageName) 58 if err != nil { 59 panic(fmt.Sprintf("unexpected error reading package: '%s'\n%s\n", packageName, err.Error())) 60 } 61 62 testfiles = append(testfiles, findTestsInPackage(subPackage)...) 63 } 64 65 addGinkgoSuiteForPackage(pkg) 66 goFmtPackage(pkg) 67 return 68} 69 70/* 71 * Shells out to `ginkgo bootstrap` to create a test suite file 72 */ 73func addGinkgoSuiteForPackage(pkg *build.Package) { 74 originalDir, err := os.Getwd() 75 if err != nil { 76 panic(err) 77 } 78 79 suite_test_file := filepath.Join(pkg.Dir, pkg.Name+"_suite_test.go") 80 81 _, err = os.Stat(suite_test_file) 82 if err == nil { 83 return // test file already exists, this should be a no-op 84 } 85 86 err = os.Chdir(pkg.Dir) 87 if err != nil { 88 panic(err) 89 } 90 91 output, err := exec.Command("ginkgo", "bootstrap").Output() 92 93 if err != nil { 94 panic(fmt.Sprintf("error running 'ginkgo bootstrap'.\nstdout: %s\n%s\n", output, err.Error())) 95 } 96 97 err = os.Chdir(originalDir) 98 if err != nil { 99 panic(err) 100 } 101} 102 103/* 104 * Shells out to `go fmt` to format the package 105 */ 106func goFmtPackage(pkg *build.Package) { 107 path, _ := filepath.Abs(pkg.ImportPath) 108 output, err := exec.Command("go", "fmt", path).CombinedOutput() 109 110 if err != nil { 111 fmt.Printf("Warning: Error running 'go fmt %s'.\nstdout: %s\n%s\n", path, output, err.Error()) 112 } 113} 114 115/* 116 * Attempts to return a package with its test files already read. 117 * The ImportMode arg to build.Import lets you specify if you want go to read the 118 * buildable go files inside the package, but it fails if the package has no go files 119 */ 120func packageWithName(name string) (pkg *build.Package, err error) { 121 pkg, err = build.Default.Import(name, ".", build.ImportMode(0)) 122 if err == nil { 123 return 124 } 125 126 pkg, err = build.Default.Import(name, ".", build.ImportMode(1)) 127 return 128} 129