Skip to content

Commit b92c87d

Browse files
author
Bryan C. Mills
committed
playground: make isTest match its documentation
This fixes a bug for names such as "Test1", noticed while I was writing a review comment for CL 226757. Change-Id: I1425e380cb2abbb746b108fd97cd8da8f5d40998 Reviewed-on: https://go-review.googlesource.com/c/playground/+/228791 Run-TryBot: Bryan C. Mills <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Alexander Rakoczy <[email protected]>
1 parent 136c41e commit b92c87d

File tree

2 files changed

+188
-2
lines changed

2 files changed

+188
-2
lines changed

sandbox.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import (
3131
"sync"
3232
"text/template"
3333
"time"
34+
"unicode"
35+
"unicode/utf8"
3436

3537
"cloud.google.com/go/compute/metadata"
3638
"github.com/bradfitz/gomemcache/memcache"
@@ -190,15 +192,16 @@ func isTestFunc(fn *ast.FuncDecl) bool {
190192

191193
// isTest tells whether name looks like a test (or benchmark, according to prefix).
192194
// It is a Test (say) if there is a character after Test that is not a lower-case letter.
193-
// We don't want TesticularCancer.
195+
// We don't want mistaken Testimony or erroneous Benchmarking.
194196
func isTest(name, prefix string) bool {
195197
if !strings.HasPrefix(name, prefix) {
196198
return false
197199
}
198200
if len(name) == len(prefix) { // "Test" is ok
199201
return true
200202
}
201-
return ast.IsExported(name[len(prefix):])
203+
r, _ := utf8.DecodeRuneInString(name[len(prefix):])
204+
return !unicode.IsLower(r)
202205
}
203206

204207
// getTestProg returns source code that executes all valid tests and examples in src.

sandbox_test.go

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright 2020 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"go/token"
9+
"os"
10+
"os/exec"
11+
"reflect"
12+
"runtime"
13+
"strings"
14+
"testing"
15+
)
16+
17+
// TestIsTest verifies that the isTest helper function matches
18+
// exactly (and only) the names of functions recognized as tests.
19+
func TestIsTest(t *testing.T) {
20+
cmd := exec.Command(os.Args[0], "-test.list=.")
21+
out, err := cmd.CombinedOutput()
22+
if err != nil {
23+
t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out)
24+
}
25+
t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)
26+
27+
isTestFunction := map[string]bool{}
28+
lines := strings.Split(string(out), "\n")
29+
for _, line := range lines {
30+
isTestFunction[strings.TrimSpace(line)] = true
31+
}
32+
33+
for _, tc := range []struct {
34+
prefix string
35+
f interface{}
36+
want bool
37+
}{
38+
{"Test", Test, true},
39+
{"Test", TestIsTest, true},
40+
{"Test", Test1IsATest, true},
41+
{"Test", TestÑIsATest, true},
42+
43+
{"Test", TestisNotATest, false},
44+
45+
{"Example", Example, true},
46+
{"Example", ExampleTest, true},
47+
{"Example", Example_isAnExample, true},
48+
{"Example", ExampleTest_isAnExample, true},
49+
50+
// Example_noOutput has a valid example function name but lacks an output
51+
// declaration, but the isTest function operates only on the test name
52+
// so it cannot detect that the function is not a test.
53+
54+
{"Example", Example1IsAnExample, true},
55+
{"Example", ExampleisNotAnExample, false},
56+
57+
{"Benchmark", Benchmark, true},
58+
{"Benchmark", BenchmarkNop, true},
59+
{"Benchmark", Benchmark1IsABenchmark, true},
60+
61+
{"Benchmark", BenchmarkisNotABenchmark, false},
62+
} {
63+
name := nameOf(t, tc.f)
64+
t.Run(name, func(t *testing.T) {
65+
if tc.want != isTestFunction[name] {
66+
t.Fatalf(".want (%v) is inconsistent with -test.list", tc.want)
67+
}
68+
if !strings.HasPrefix(name, tc.prefix) {
69+
t.Fatalf("%q is not a prefix of %v", tc.prefix, name)
70+
}
71+
72+
got := isTest(name, tc.prefix)
73+
if got != tc.want {
74+
t.Errorf(`isTest(%q, %q) = %v; want %v`, name, tc.prefix, got, tc.want)
75+
}
76+
})
77+
}
78+
}
79+
80+
// nameOf returns the runtime-reported name of function f.
81+
func nameOf(t *testing.T, f interface{}) string {
82+
t.Helper()
83+
84+
v := reflect.ValueOf(f)
85+
if v.Kind() != reflect.Func {
86+
t.Fatalf("%v is not a function", f)
87+
}
88+
89+
rf := runtime.FuncForPC(v.Pointer())
90+
if rf == nil {
91+
t.Fatalf("%v.Pointer() is not a known function", f)
92+
}
93+
94+
fullName := rf.Name()
95+
parts := strings.Split(fullName, ".")
96+
97+
name := parts[len(parts)-1]
98+
if !token.IsIdentifier(name) {
99+
t.Fatalf("%q is not a valid identifier", name)
100+
}
101+
return name
102+
}
103+
104+
// TestisNotATest is not a test function, despite appearances.
105+
//
106+
// Please ignore any lint or vet warnings for this function.
107+
func TestisNotATest(t *testing.T) {
108+
panic("This is not a valid test function.")
109+
}
110+
111+
// Test11IsATest is a valid test function.
112+
func Test1IsATest(t *testing.T) {
113+
}
114+
115+
// Test is a test with a minimal name.
116+
func Test(t *testing.T) {
117+
}
118+
119+
// TestÑIsATest is a test with an interesting Unicode name.
120+
func TestÑIsATest(t *testing.T) {
121+
}
122+
123+
func Example() {
124+
// Output:
125+
}
126+
127+
func ExampleTest() {
128+
// This is an example for the function Test.
129+
// ❤ recursion.
130+
Test(nil)
131+
132+
// Output:
133+
}
134+
135+
func Example1IsAnExample() {
136+
// Output:
137+
}
138+
139+
// ExampleisNotAnExample is not an example function, despite appearances.
140+
//
141+
// Please ignore any lint or vet warnings for this function.
142+
func ExampleisNotAnExample() {
143+
panic("This is not a valid example function.")
144+
145+
// Output:
146+
// None. (This is not really an example function.)
147+
}
148+
149+
func Example_isAnExample() {
150+
// Output:
151+
}
152+
153+
func ExampleTest_isAnExample() {
154+
Test(nil)
155+
156+
// Output:
157+
}
158+
159+
func Example_noOutput() {
160+
// No output declared: should be compiled but not run.
161+
}
162+
163+
func Benchmark(b *testing.B) {
164+
for i := 0; i < b.N; i++ {
165+
}
166+
}
167+
168+
func BenchmarkNop(b *testing.B) {
169+
for i := 0; i < b.N; i++ {
170+
}
171+
}
172+
173+
func Benchmark1IsABenchmark(b *testing.B) {
174+
for i := 0; i < b.N; i++ {
175+
}
176+
}
177+
178+
// BenchmarkisNotABenchmark is not a benchmark function, despite appearances.
179+
//
180+
// Please ignore any lint or vet warnings for this function.
181+
func BenchmarkisNotABenchmark(b *testing.B) {
182+
panic("This is not a valid benchmark function.")
183+
}

0 commit comments

Comments
 (0)