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