// 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, 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 { 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 }