Skip to content

Commit c87564b

Browse files
authored
Support multiple test files (exercism#141)
When extracting test code from test files, support multiple test files and not just the first test file.
1 parent 258ac64 commit c87564b

5 files changed

Lines changed: 53 additions & 51 deletions

File tree

testrunner/ast.go

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -43,35 +43,37 @@ type rootLevelTest struct {
4343

4444
// FindAllRootLevelTests parses the test file and extracts the name,
4545
// test code and task id for each top level test (parent test) in the file.
46-
func FindAllRootLevelTests(fileName string) []rootLevelTest {
46+
func FindAllRootLevelTests(fileNames []string) []rootLevelTest {
4747
defer handleASTPanic()
4848
tests := []rootLevelTest{}
4949
fset := token.NewFileSet()
5050
ppc := parser.ParseComments
51-
file, err := parser.ParseFile(fset, fileName, nil, ppc)
52-
if err != nil {
53-
log.Printf("error: not able to parse '%s': %s", fileName, err)
54-
return nil
55-
}
56-
for _, d := range file.Decls {
57-
if f, ok := d.(*ast.FuncDecl); ok && strings.HasPrefix(f.Name.Name, "Test") {
58-
taskID := findTaskID(f.Doc)
59-
fun := &printer.CommentedNode{Node: f, Comments: file.Comments}
60-
var buf bytes.Buffer
61-
printErr := printer.Fprint(&buf, fset, fun)
62-
if printErr != nil {
63-
log.Printf("warning: failed to print AST for test %s in %s: %s",
64-
f.Name.Name, fileName, err,
65-
)
66-
}
51+
for _, fileName := range fileNames {
52+
file, err := parser.ParseFile(fset, fileName, nil, ppc)
53+
if err != nil {
54+
log.Printf("error: not able to parse '%s': %s", fileName, err)
55+
return nil
56+
}
57+
for _, d := range file.Decls {
58+
if f, ok := d.(*ast.FuncDecl); ok && strings.HasPrefix(f.Name.Name, "Test") {
59+
taskID := findTaskID(f.Doc)
60+
fun := &printer.CommentedNode{Node: f, Comments: file.Comments}
61+
var buf bytes.Buffer
62+
printErr := printer.Fprint(&buf, fset, fun)
63+
if printErr != nil {
64+
log.Printf("warning: failed to print AST for test %s in %s: %s",
65+
f.Name.Name, fileName, err,
66+
)
67+
}
6768

68-
tests = append(tests, rootLevelTest{
69-
name: f.Name.Name,
70-
fileName: fileName,
71-
code: buf.String(),
72-
taskID: taskID,
73-
pkgName: file.Name.Name,
74-
})
69+
tests = append(tests, rootLevelTest{
70+
name: f.Name.Name,
71+
fileName: fileName,
72+
code: buf.String(),
73+
taskID: taskID,
74+
pkgName: file.Name.Name,
75+
})
76+
}
7577
}
7678
}
7779
return tests

testrunner/ast_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func TestGetFuncCode(t *testing.T) {
2727
}
2828
for _, tt := range tests {
2929
t.Run(tt.name, func(t *testing.T) {
30-
rootLevelTests := FindAllRootLevelTests(tt.testFile)
30+
rootLevelTests := FindAllRootLevelTests([]string{tt.testFile})
3131
rootLevelTestsMap := ConvertToMapByTestName(rootLevelTests)
3232
if rootLevelTestsMap[tt.testName].code != tt.code {
3333
t.Errorf("FindAllRootLevelTests for %s did not return correct code, got %v; want %v",

testrunner/execute.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,8 @@ func processTestResults(
220220
results := make([]testResult, 0)
221221
resultIdxByName := make(map[string]int)
222222

223-
testFile := FindTestFile(input_dir)
224-
rootLevelTests := FindAllRootLevelTests(testFile)
223+
testFiles := FindTestFiles(input_dir)
224+
rootLevelTests := FindAllRootLevelTests(testFiles)
225225
rootLevelTestsMap := ConvertToMapByTestName(rootLevelTests)
226226

227227
for _, parsedLine := range parsedOutput.testLines {

testrunner/extract.go

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,30 @@ func splitTestName(testName string) (string, string) {
1515
return before, after
1616
}
1717

18-
func FindTestFile(codePath string) string {
19-
files, err := os.ReadDir(codePath)
18+
func FindTestFiles(codePath string) []string {
19+
files, err := filepath.Glob(filepath.Join(codePath, "*_test.go"))
2020
if err != nil {
2121
log.Printf("warning: input_dir '%s' cannot be read: %s", codePath, err)
22-
return ""
22+
return nil
2323
}
24+
var found []string
25+
for _, testpath := range files {
26+
fh, err := os.ReadFile(testpath)
27+
if err != nil {
28+
log.Printf("warning: test file '%s' read failed: %s", testpath, err)
29+
}
2430

25-
for _, f := range files {
26-
if strings.HasSuffix(f.Name(), "_test.go") {
27-
testpath := filepath.Join(codePath, f.Name())
28-
fh, err := os.ReadFile(testpath)
29-
if err != nil {
30-
log.Printf("warning: test file '%s' read failed: %s", testpath, err)
31-
}
32-
33-
// We need to check we found the file that actually contains the tests and not only the
34-
// generated test cases (cases_test.go).
35-
// Text processing is easier than using AST and should be reliable enough.
36-
if strings.Contains(string(fh), "func Test") {
37-
return testpath
38-
}
31+
// We need to check we found the file that actually contains the tests and not only the
32+
// generated test cases (cases_test.go).
33+
// Text processing is easier than using AST and should be reliable enough.
34+
if strings.Contains(string(fh), "func Test") {
35+
found = append(found, testpath)
3936
}
4037
}
41-
log.Printf("error: test file not found in input_dir '%s'", codePath)
42-
return ""
38+
if len(found) == 0 {
39+
log.Printf("error: test file not found in input_dir '%s'", codePath)
40+
}
41+
return found
4342
}
4443

4544
// return the associated test function code from the given test file

testrunner/extract_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package testrunner
22

33
import (
44
"path/filepath"
5+
"slices"
56
"strings"
67
"testing"
78
)
@@ -40,22 +41,22 @@ func TestFindTestFile(t *testing.T) {
4041
tests := []struct {
4142
name string
4243
codePath string
43-
fileName string
44+
fileName []string
4445
}{
4546
{
4647
name: "found single test file",
4748
codePath: filepath.Join("testdata", "practice", "passing"),
48-
fileName: filepath.Join("testdata", "practice", "passing", "passing_test.go"),
49+
fileName: []string{filepath.Join("testdata", "practice", "passing", "passing_test.go")},
4950
},
5051
{
5152
name: "found correct test file if there are two",
5253
codePath: filepath.Join("testdata", "concept", "conditionals"),
53-
fileName: filepath.Join("testdata", "concept", "conditionals", "conditionals_test.go"),
54+
fileName: []string{filepath.Join("testdata", "concept", "conditionals", "conditionals_test.go")},
5455
},
5556
}
5657
for _, tt := range tests {
5758
t.Run(tt.name, func(t *testing.T) {
58-
if tf := FindTestFile(tt.codePath); tf != tt.fileName {
59+
if tf := FindTestFiles(tt.codePath); !slices.Equal(tf, tt.fileName) {
5960
t.Errorf("findTestFile(%v) = %v; want %v",
6061
tt.codePath, tf, tt.fileName)
6162
}
@@ -65,7 +66,7 @@ func TestFindTestFile(t *testing.T) {
6566

6667
func TestExtractTestCode(t *testing.T) {
6768
tf := filepath.Join("testdata", "concept", "conditionals", "conditionals_test.go")
68-
rootLevelTests := FindAllRootLevelTests(tf)
69+
rootLevelTests := FindAllRootLevelTests([]string{tf})
6970
rootLevelTestsMap := ConvertToMapByTestName(rootLevelTests)
7071
tests := []struct {
7172
name string

0 commit comments

Comments
 (0)