diff --git a/fstest/test_all/run.go b/fstest/test_all/run.go index ea10b385d..0d12aa15c 100644 --- a/fstest/test_all/run.go +++ b/fstest/test_all/run.go @@ -15,6 +15,7 @@ import ( "path" "regexp" "runtime" + "sort" "strings" "sync" "time" @@ -101,6 +102,35 @@ func (r *Run) dumpOutput() { log.Println("------------------------------------------------------------") } +// This converts a slice of test names into a regexp which matches +// them. +func testsToRegexp(tests []string) string { + var split []map[string]struct{} + // Make a slice with maps of the used parts at each level + for _, test := range tests { + for i, name := range strings.Split(test, "/") { + if i >= len(split) { + split = append(split, make(map[string]struct{})) + } + split[i][name] = struct{}{} + } + } + var out []string + for _, level := range split { + var testsInLevel = []string{} + for name := range level { + testsInLevel = append(testsInLevel, name) + } + sort.Strings(testsInLevel) + if len(testsInLevel) > 1 { + out = append(out, "^("+strings.Join(testsInLevel, "|")+")$") + } else { + out = append(out, "^"+testsInLevel[0]+"$") + } + } + return strings.Join(out, "/") +} + var failRe = regexp.MustCompile(`(?m)^\s*--- FAIL: (Test.*?) \(`) // findFailures looks for all the tests which failed @@ -126,7 +156,7 @@ func (r *Run) findFailures() { } r.failedTests = newTests if len(r.failedTests) != 0 { - r.runFlag = "^(" + strings.Join(r.failedTests, "|") + ")$" + r.runFlag = testsToRegexp(r.failedTests) } else { r.runFlag = "" } diff --git a/fstest/test_all/run_test.go b/fstest/test_all/run_test.go new file mode 100644 index 000000000..b51ea3313 --- /dev/null +++ b/fstest/test_all/run_test.go @@ -0,0 +1,181 @@ +package main + +import ( + "fmt" + "os/exec" + "regexp" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTestsToRegexp(t *testing.T) { + for _, test := range []struct { + in []string + want string + }{ + { + in: []string{}, + want: "", + }, + { + in: []string{"TestOne"}, + want: "^TestOne$", + }, + { + in: []string{"TestOne", "TestTwo"}, + want: "^(TestOne|TestTwo)$", + }, + { + in: []string{"TestOne", "TestTwo", "TestThree"}, + want: "^(TestOne|TestThree|TestTwo)$", + }, + { + in: []string{"TestOne/Sub1"}, + want: "^TestOne$/^Sub1$", + }, + { + in: []string{ + "TestOne/Sub1", + "TestTwo", + }, + want: "^(TestOne|TestTwo)$/^Sub1$", + }, + { + in: []string{ + "TestOne/Sub1", + "TestOne/Sub2", + "TestTwo", + }, + want: "^(TestOne|TestTwo)$/^(Sub1|Sub2)$", + }, + { + in: []string{ + "TestOne/Sub1", + "TestOne/Sub2/SubSub1", + "TestTwo", + }, + want: "^(TestOne|TestTwo)$/^(Sub1|Sub2)$/^SubSub1$", + }, + { + in: []string{ + "TestTests/A1", + "TestTests/B/B1", + "TestTests/C/C3/C31", + }, + want: "^TestTests$/^(A1|B|C)$/^(B1|C3)$/^C31$", + }, + } { + got := testsToRegexp(test.in) + assert.Equal(t, test.want, got, fmt.Sprintf("in=%v want=%q got=%q", test.in, test.want, got)) + } +} + +var runRe = regexp.MustCompile(`(?m)^\s*=== RUN\s*(Test.*?)\s*$`) + +// Test the regexp work with the -run flag in actually selecting the right tests +func TestTestsToRegexpLive(t *testing.T) { + for _, test := range []struct { + in []string + want []string + }{ + { + in: []string{ + "TestTests/A1", + "TestTests/C/C3", + }, + want: []string{ + "TestTests", + "TestTests/A1", + "TestTests/C", + "TestTests/C/C3", + "TestTests/C/C3/C31", + "TestTests/C/C3/C32", + }, + }, + { + in: []string{ + "TestTests", + "TestTests/A1", + "TestTests/B", + "TestTests/B/B1", + "TestTests/C", + }, + want: []string{ + "TestTests", + "TestTests/A1", + "TestTests/B", + "TestTests/B/B1", + "TestTests/C", + // FIXME there doesn't seem to be a way to select a non-terminal test + // and all of its subtests if there is a longer path (in this case B/B1) + // "TestTests/C/C1", + // "TestTests/C/C2", + // "TestTests/C/C3", + // "TestTests/C/C3/C31", + // "TestTests/C/C3/C32", + }, + }, + { + in: []string{ + "TestTests/A1", + "TestTests/B/B1", + "TestTests/C/C3/C31", + }, + want: []string{ + "TestTests", + "TestTests/A1", + "TestTests/B", + "TestTests/B/B1", + "TestTests/C", + "TestTests/C/C3", + "TestTests/C/C3/C31", + }, + }, + { + in: []string{ + "TestTests/B/B1", + "TestTests/C/C3/C31", + }, + want: []string{ + "TestTests", + "TestTests/B", + "TestTests/B/B1", + "TestTests/C", + "TestTests/C/C3", + "TestTests/C/C3/C31", + }, + }, + } { + runRegexp := testsToRegexp(test.in) + cmd := exec.Command("go", "test", "-v", "-run", runRegexp) + out, err := cmd.CombinedOutput() + require.NoError(t, err) + var got []string + for _, match := range runRe.FindAllSubmatch(out, -1) { + got = append(got, string(match[1])) + } + assert.Equal(t, test.want, got, fmt.Sprintf("in=%v want=%v got=%v, runRegexp=%q", test.in, test.want, got, runRegexp)) + } +} + +var nilTest = func(t *testing.T) {} + +// Nested tests for TestTestsToRegexpLive to run +func TestTests(t *testing.T) { + t.Run("A1", nilTest) + t.Run("A2", nilTest) + t.Run("B", func(t *testing.T) { + t.Run("B1", nilTest) + t.Run("B2", nilTest) + }) + t.Run("C", func(t *testing.T) { + t.Run("C1", nilTest) + t.Run("C2", nilTest) + t.Run("C3", func(t *testing.T) { + t.Run("C31", nilTest) + t.Run("C32", nilTest) + }) + }) +}