testing: benchmarks: -bench: allow filtering subbenchmarks

Pull in testing/match.go verbatim from upstream and use it to filter benchmarks.

TODO: also update testing.go to filter tests similarly.
This commit is contained in:
Dan Kegel
2022-01-11 14:10:57 -08:00
committed by Ron Evans
parent 93e0636c03
commit fbd72a4b2c
4 changed files with 352 additions and 43 deletions
+122
View File
@@ -5,9 +5,131 @@
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, partial 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 '/'.
elem := strings.Split(name, "/")
for i, s := range elem {
if i >= len(m.filter) {
break
}
if ok, _ := m.matchFunc(m.filter[i], s); !ok {
return name, false, false
}
}
return name, true, len(elem) < len(m.filter)
}
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 or 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 {