| // Copyright 2015 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package testing | |
| import ( | |
| "fmt" | |
| "reflect" | |
| "regexp" | |
| "strings" | |
| "unicode" | |
| ) | |
| func init() { | |
| testingTesting = true | |
| } | |
| // Verify that our IsSpace agrees with unicode.IsSpace. | |
| func TestIsSpace(t *T) { | |
| n := 0 | |
| for r := rune(0); r <= unicode.MaxRune; r++ { | |
| if isSpace(r) != unicode.IsSpace(r) { | |
| t.Errorf("IsSpace(%U)=%t incorrect", r, isSpace(r)) | |
| n++ | |
| if n > 10 { | |
| return | |
| } | |
| } | |
| } | |
| } | |
| func TestSplitRegexp(t *T) { | |
| res := func(s ...string) filterMatch { return simpleMatch(s) } | |
| alt := func(m ...filterMatch) filterMatch { return alternationMatch(m) } | |
| testCases := []struct { | |
| pattern string | |
| result filterMatch | |
| }{ | |
| // Correct patterns | |
| // If a regexp pattern is correct, all split regexps need to be correct | |
| // as well. | |
| {"", res("")}, | |
| {"/", res("", "")}, | |
| {"//", res("", "", "")}, | |
| {"A", res("A")}, | |
| {"A/B", res("A", "B")}, | |
| {"A/B/", res("A", "B", "")}, | |
| {"/A/B/", res("", "A", "B", "")}, | |
| {"[A]/(B)", res("[A]", "(B)")}, | |
| {"[/]/[/]", res("[/]", "[/]")}, | |
| {"[/]/[:/]", res("[/]", "[:/]")}, | |
| {"/]", res("", "]")}, | |
| {"]/", res("]", "")}, | |
| {"]/[/]", res("]", "[/]")}, | |
| {`([)/][(])`, res(`([)/][(])`)}, | |
| {"[(]/[)]", res("[(]", "[)]")}, | |
| {"A/B|C/D", alt(res("A", "B"), res("C", "D"))}, | |
| // Faulty patterns | |
| // Errors in original should produce at least one faulty regexp in results. | |
| {")/", res(")/")}, | |
| {")/(/)", res(")/(", ")")}, | |
| {"a[/)b", res("a[/)b")}, | |
| {"(/]", res("(/]")}, | |
| {"(/", res("(/")}, | |
| {"[/]/[/", res("[/]", "[/")}, | |
| {`\p{/}`, res(`\p{`, "}")}, | |
| {`\p/`, res(`\p`, "")}, | |
| {`[[:/:]]`, res(`[[:/:]]`)}, | |
| } | |
| for _, tc := range testCases { | |
| a := splitRegexp(tc.pattern) | |
| if !reflect.DeepEqual(a, tc.result) { | |
| t.Errorf("splitRegexp(%q) = %#v; want %#v", tc.pattern, a, tc.result) | |
| } | |
| // If there is any error in the pattern, one of the returned subpatterns | |
| // needs to have an error as well. | |
| if _, err := regexp.Compile(tc.pattern); err != nil { | |
| ok := true | |
| if err := a.verify("", regexp.MatchString); err != nil { | |
| ok = false | |
| } | |
| if ok { | |
| t.Errorf("%s: expected error in any of %q", tc.pattern, a) | |
| } | |
| } | |
| } | |
| } | |
| func TestMatcher(t *T) { | |
| testCases := []struct { | |
| pattern string | |
| skip string | |
| parent, sub string | |
| ok bool | |
| partial bool | |
| }{ | |
| // Behavior without subtests. | |
| {"", "", "", "TestFoo", true, false}, | |
| {"TestFoo", "", "", "TestFoo", true, false}, | |
| {"TestFoo/", "", "", "TestFoo", true, true}, | |
| {"TestFoo/bar/baz", "", "", "TestFoo", true, true}, | |
| {"TestFoo", "", "", "TestBar", false, false}, | |
| {"TestFoo/", "", "", "TestBar", false, false}, | |
| {"TestFoo/bar/baz", "", "", "TestBar/bar/baz", false, false}, | |
| {"", "TestBar", "", "TestFoo", true, false}, | |
| {"", "TestBar", "", "TestBar", false, false}, | |
| // Skipping a non-existent test doesn't change anything. | |
| {"", "TestFoo/skipped", "", "TestFoo", true, false}, | |
| {"TestFoo", "TestFoo/skipped", "", "TestFoo", true, false}, | |
| {"TestFoo/", "TestFoo/skipped", "", "TestFoo", true, true}, | |
| {"TestFoo/bar/baz", "TestFoo/skipped", "", "TestFoo", true, true}, | |
| {"TestFoo", "TestFoo/skipped", "", "TestBar", false, false}, | |
| {"TestFoo/", "TestFoo/skipped", "", "TestBar", false, false}, | |
| {"TestFoo/bar/baz", "TestFoo/skipped", "", "TestBar/bar/baz", false, false}, | |
| // with subtests | |
| {"", "", "TestFoo", "x", true, false}, | |
| {"TestFoo", "", "TestFoo", "x", true, false}, | |
| {"TestFoo/", "", "TestFoo", "x", true, false}, | |
| {"TestFoo/bar/baz", "", "TestFoo", "bar", true, true}, | |
| {"", "TestFoo/skipped", "TestFoo", "x", true, false}, | |
| {"TestFoo", "TestFoo/skipped", "TestFoo", "x", true, false}, | |
| {"TestFoo", "TestFoo/skipped", "TestFoo", "skipped", false, false}, | |
| {"TestFoo/", "TestFoo/skipped", "TestFoo", "x", true, false}, | |
| {"TestFoo/bar/baz", "TestFoo/skipped", "TestFoo", "bar", true, true}, | |
| // Subtest with a '/' in its name still allows for copy and pasted names | |
| // to match. | |
| {"TestFoo/bar/baz", "", "TestFoo", "bar/baz", true, false}, | |
| {"TestFoo/bar/baz", "TestFoo/bar/baz", "TestFoo", "bar/baz", false, false}, | |
| {"TestFoo/bar/baz", "TestFoo/bar/baz/skip", "TestFoo", "bar/baz", true, false}, | |
| {"TestFoo/bar/baz", "", "TestFoo/bar", "baz", true, false}, | |
| {"TestFoo/bar/baz", "", "TestFoo", "x", false, false}, | |
| {"TestFoo", "", "TestBar", "x", false, false}, | |
| {"TestFoo/", "", "TestBar", "x", false, false}, | |
| {"TestFoo/bar/baz", "", "TestBar", "x/bar/baz", false, false}, | |
| {"A/B|C/D", "", "TestA", "B", true, false}, | |
| {"A/B|C/D", "", "TestC", "D", true, false}, | |
| {"A/B|C/D", "", "TestA", "C", false, false}, | |
| // subtests only | |
| {"", "", "TestFoo", "x", true, false}, | |
| {"/", "", "TestFoo", "x", true, false}, | |
| {"./", "", "TestFoo", "x", true, false}, | |
| {"./.", "", "TestFoo", "x", true, false}, | |
| {"/bar/baz", "", "TestFoo", "bar", true, true}, | |
| {"/bar/baz", "", "TestFoo", "bar/baz", true, false}, | |
| {"//baz", "", "TestFoo", "bar/baz", true, false}, | |
| {"//", "", "TestFoo", "bar/baz", true, false}, | |
| {"/bar/baz", "", "TestFoo/bar", "baz", true, false}, | |
| {"//foo", "", "TestFoo", "bar/baz", false, false}, | |
| {"/bar/baz", "", "TestFoo", "x", false, false}, | |
| {"/bar/baz", "", "TestBar", "x/bar/baz", false, false}, | |
| } | |
| for _, tc := range testCases { | |
| m := newMatcher(regexp.MatchString, tc.pattern, "-test.run", tc.skip) | |
| parent := &common{name: tc.parent} | |
| if tc.parent != "" { | |
| parent.level = 1 | |
| } | |
| if n, ok, partial := m.fullName(parent, tc.sub); ok != tc.ok || partial != tc.partial { | |
| t.Errorf("for pattern %q, fullName(parent=%q, sub=%q) = %q, ok %v partial %v; want ok %v partial %v", | |
| tc.pattern, tc.parent, tc.sub, n, ok, partial, tc.ok, tc.partial) | |
| } | |
| } | |
| } | |
| var namingTestCases = []struct{ name, want string }{ | |
| // Uniqueness | |
| {"", "x/#00"}, | |
| {"", "x/#01"}, | |
| {"#0", "x/#0"}, // Doesn't conflict with #00 because the number of digits differs. | |
| {"#00", "x/#00#01"}, // Conflicts with implicit #00 (used above), so add a suffix. | |
| {"#", "x/#"}, | |
| {"#", "x/##01"}, | |
| {"t", "x/t"}, | |
| {"t", "x/t#01"}, | |
| {"t", "x/t#02"}, | |
| {"t#00", "x/t#00"}, // Explicit "#00" doesn't conflict with the unsuffixed first subtest. | |
| {"a#01", "x/a#01"}, // user has subtest with this name. | |
| {"a", "x/a"}, // doesn't conflict with this name. | |
| {"a", "x/a#02"}, // This string is claimed now, so resume | |
| {"a", "x/a#03"}, // with counting. | |
| {"a#02", "x/a#02#01"}, // We already used a#02 once, so add a suffix. | |
| {"b#00", "x/b#00"}, | |
| {"b", "x/b"}, // Implicit 0 doesn't conflict with explicit "#00". | |
| {"b", "x/b#01"}, | |
| {"b#9223372036854775807", "x/b#9223372036854775807"}, // MaxInt64 | |
| {"b", "x/b#02"}, | |
| {"b", "x/b#03"}, | |
| // Sanitizing | |
| {"A:1 B:2", "x/A:1_B:2"}, | |
| {"s\t\r\u00a0", "x/s___"}, | |
| {"\x01", `x/\x01`}, | |
| {"\U0010ffff", `x/\U0010ffff`}, | |
| } | |
| func TestNaming(t *T) { | |
| m := newMatcher(regexp.MatchString, "", "", "") | |
| parent := &common{name: "x", level: 1} // top-level test. | |
| for i, tc := range namingTestCases { | |
| if got, _, _ := m.fullName(parent, tc.name); got != tc.want { | |
| t.Errorf("%d:%s: got %q; want %q", i, tc.name, got, tc.want) | |
| } | |
| } | |
| } | |
| func FuzzNaming(f *F) { | |
| for _, tc := range namingTestCases { | |
| f.Add(tc.name) | |
| } | |
| parent := &common{name: "x", level: 1} | |
| var m *matcher | |
| var seen map[string]string | |
| reset := func() { | |
| m = allMatcher() | |
| seen = make(map[string]string) | |
| } | |
| reset() | |
| f.Fuzz(func(t *T, subname string) { | |
| if len(subname) > 10 { | |
| // Long names attract the OOM killer. | |
| t.Skip() | |
| } | |
| name := m.unique(parent.name, subname) | |
| if !strings.Contains(name, "/"+subname) { | |
| t.Errorf("name %q does not contain subname %q", name, subname) | |
| } | |
| if prev, ok := seen[name]; ok { | |
| t.Errorf("name %q generated by both %q and %q", name, prev, subname) | |
| } | |
| if len(seen) > 1e6 { | |
| // Free up memory. | |
| reset() | |
| } | |
| seen[name] = subname | |
| }) | |
| } | |
| // GoString returns a string that is more readable than the default, which makes | |
| // it easier to read test errors. | |
| func (m alternationMatch) GoString() string { | |
| s := make([]string, len(m)) | |
| for i, m := range m { | |
| s[i] = fmt.Sprintf("%#v", m) | |
| } | |
| return fmt.Sprintf("(%s)", strings.Join(s, " | ")) | |
| } |