168 lines
3.5 KiB
Go
168 lines
3.5 KiB
Go
|
// 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"
|
||
|
"os"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
// matcher sanitizes, uniques, and filters names of subtests and subbenchmarks.
|
||
|
type matcher struct {
|
||
|
filter []string
|
||
|
matchFunc func(pat, str string) (bool, error)
|
||
|
|
||
|
mu sync.Mutex
|
||
|
subNames map[string]int64
|
||
|
}
|
||
|
|
||
|
// TODO: fix test_main to avoid race and improve caching, also allowing to
|
||
|
// eliminate this Mutex.
|
||
|
var matchMutex sync.Mutex
|
||
|
|
||
|
func newMatcher(matchString func(pat, str string) (bool, error), patterns, name string) *matcher {
|
||
|
var filter []string
|
||
|
if patterns != "" {
|
||
|
filter = splitRegexp(patterns)
|
||
|
for i, s := range filter {
|
||
|
filter[i] = rewrite(s)
|
||
|
}
|
||
|
// Verify filters before doing any processing.
|
||
|
for i, s := range filter {
|
||
|
if _, err := matchString(s, "non-empty"); err != nil {
|
||
|
fmt.Fprintf(os.Stderr, "testing: invalid regexp for element %d of %s (%q): %s\n", i, name, s, err)
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return &matcher{
|
||
|
filter: filter,
|
||
|
matchFunc: matchString,
|
||
|
subNames: map[string]int64{},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (m *matcher) fullName(c *common, subname string) (name string, ok bool) {
|
||
|
name = subname
|
||
|
|
||
|
m.mu.Lock()
|
||
|
defer m.mu.Unlock()
|
||
|
|
||
|
if c != nil && c.level > 0 {
|
||
|
name = m.unique(c.name, rewrite(subname))
|
||
|
}
|
||
|
|
||
|
matchMutex.Lock()
|
||
|
defer matchMutex.Unlock()
|
||
|
|
||
|
// We check the full array of paths each time to allow for the case that
|
||
|
// a pattern contains a '/'.
|
||
|
for i, s := range strings.Split(name, "/") {
|
||
|
if i >= len(m.filter) {
|
||
|
break
|
||
|
}
|
||
|
if ok, _ := m.matchFunc(m.filter[i], s); !ok {
|
||
|
return name, false
|
||
|
}
|
||
|
}
|
||
|
return name, true
|
||
|
}
|
||
|
|
||
|
func splitRegexp(s string) []string {
|
||
|
a := make([]string, 0, strings.Count(s, "/"))
|
||
|
cs := 0
|
||
|
cp := 0
|
||
|
for i := 0; i < len(s); {
|
||
|
switch s[i] {
|
||
|
case '[':
|
||
|
cs++
|
||
|
case ']':
|
||
|
if cs--; cs < 0 { // An unmatched ']' is legal.
|
||
|
cs = 0
|
||
|
}
|
||
|
case '(':
|
||
|
if cs == 0 {
|
||
|
cp++
|
||
|
}
|
||
|
case ')':
|
||
|
if cs == 0 {
|
||
|
cp--
|
||
|
}
|
||
|
case '\\':
|
||
|
i++
|
||
|
case '/':
|
||
|
if cs == 0 && cp == 0 {
|
||
|
a = append(a, s[:i])
|
||
|
s = s[i+1:]
|
||
|
i = 0
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
i++
|
||
|
}
|
||
|
return append(a, s)
|
||
|
}
|
||
|
|
||
|
// unique creates a unique name for the given parent and subname by affixing it
|
||
|
// with one ore more counts, if necessary.
|
||
|
func (m *matcher) unique(parent, subname string) string {
|
||
|
name := fmt.Sprintf("%s/%s", parent, subname)
|
||
|
empty := subname == ""
|
||
|
for {
|
||
|
next, exists := m.subNames[name]
|
||
|
if !empty && !exists {
|
||
|
m.subNames[name] = 1 // next count is 1
|
||
|
return name
|
||
|
}
|
||
|
// Name was already used. We increment with the count and append a
|
||
|
// string with the count.
|
||
|
m.subNames[name] = next + 1
|
||
|
|
||
|
// Add a count to guarantee uniqueness.
|
||
|
name = fmt.Sprintf("%s#%02d", name, next)
|
||
|
empty = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// rewrite rewrites a subname to having only printable characters and no white
|
||
|
// space.
|
||
|
func rewrite(s string) string {
|
||
|
b := []byte{}
|
||
|
for _, r := range s {
|
||
|
switch {
|
||
|
case isSpace(r):
|
||
|
b = append(b, '_')
|
||
|
case !strconv.IsPrint(r):
|
||
|
s := strconv.QuoteRune(r)
|
||
|
b = append(b, s[1:len(s)-1]...)
|
||
|
default:
|
||
|
b = append(b, string(r)...)
|
||
|
}
|
||
|
}
|
||
|
return string(b)
|
||
|
}
|
||
|
|
||
|
func isSpace(r rune) bool {
|
||
|
if r < 0x2000 {
|
||
|
switch r {
|
||
|
// Note: not the same as Unicode Z class.
|
||
|
case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680:
|
||
|
return true
|
||
|
}
|
||
|
} else {
|
||
|
if r <= 0x200a {
|
||
|
return true
|
||
|
}
|
||
|
switch r {
|
||
|
case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|