| // 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" | |
| "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. | |
| var matchMutex sync.Mutex | |
| func newMatcher(matchString func(pat, str string) (bool, error), pattern, name string) *matcher { | |
| // Verify filters before doing any processing. | |
| if _, err := matchString(pattern, "non-empty"); err != nil { | |
| fmt.Fprintf(os.Stderr, "testing: invalid regexp for %s: %s\n", name, err) | |
| os.Exit(1) | |
| } | |
| return &matcher{ | |
| filter: pattern, | |
| 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() | |
| if c != nil && c.level == 0 { | |
| if matched, _ := m.matchFunc(m.filter, subname); !matched { | |
| return name, false | |
| } | |
| } | |
| return name, true | |
| } | |
| // 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 | |
| } |