// 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, " | ")) }