1// Copyright 2015 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 5package testing 6 7import ( 8 "fmt" 9 "os" 10 "strconv" 11 "strings" 12 "sync" 13) 14 15// matcher sanitizes, uniques, and filters names of subtests and subbenchmarks. 16type matcher struct { 17 filter []string 18 matchFunc func(pat, str string) (bool, error) 19 20 mu sync.Mutex 21 subNames map[string]int64 22} 23 24// TODO: fix test_main to avoid race and improve caching, also allowing to 25// eliminate this Mutex. 26var matchMutex sync.Mutex 27 28func newMatcher(matchString func(pat, str string) (bool, error), patterns, name string) *matcher { 29 var filter []string 30 if patterns != "" { 31 filter = splitRegexp(patterns) 32 for i, s := range filter { 33 filter[i] = rewrite(s) 34 } 35 // Verify filters before doing any processing. 36 for i, s := range filter { 37 if _, err := matchString(s, "non-empty"); err != nil { 38 fmt.Fprintf(os.Stderr, "testing: invalid regexp for element %d of %s (%q): %s\n", i, name, s, err) 39 os.Exit(1) 40 } 41 } 42 } 43 return &matcher{ 44 filter: filter, 45 matchFunc: matchString, 46 subNames: map[string]int64{}, 47 } 48} 49 50func (m *matcher) fullName(c *common, subname string) (name string, ok, partial bool) { 51 name = subname 52 53 m.mu.Lock() 54 defer m.mu.Unlock() 55 56 if c != nil && c.level > 0 { 57 name = m.unique(c.name, rewrite(subname)) 58 } 59 60 matchMutex.Lock() 61 defer matchMutex.Unlock() 62 63 // We check the full array of paths each time to allow for the case that 64 // a pattern contains a '/'. 65 elem := strings.Split(name, "/") 66 for i, s := range elem { 67 if i >= len(m.filter) { 68 break 69 } 70 if ok, _ := m.matchFunc(m.filter[i], s); !ok { 71 return name, false, false 72 } 73 } 74 return name, true, len(elem) < len(m.filter) 75} 76 77func splitRegexp(s string) []string { 78 a := make([]string, 0, strings.Count(s, "/")) 79 cs := 0 80 cp := 0 81 for i := 0; i < len(s); { 82 switch s[i] { 83 case '[': 84 cs++ 85 case ']': 86 if cs--; cs < 0 { // An unmatched ']' is legal. 87 cs = 0 88 } 89 case '(': 90 if cs == 0 { 91 cp++ 92 } 93 case ')': 94 if cs == 0 { 95 cp-- 96 } 97 case '\\': 98 i++ 99 case '/': 100 if cs == 0 && cp == 0 { 101 a = append(a, s[:i]) 102 s = s[i+1:] 103 i = 0 104 continue 105 } 106 } 107 i++ 108 } 109 return append(a, s) 110} 111 112// unique creates a unique name for the given parent and subname by affixing it 113// with one ore more counts, if necessary. 114func (m *matcher) unique(parent, subname string) string { 115 name := fmt.Sprintf("%s/%s", parent, subname) 116 empty := subname == "" 117 for { 118 next, exists := m.subNames[name] 119 if !empty && !exists { 120 m.subNames[name] = 1 // next count is 1 121 return name 122 } 123 // Name was already used. We increment with the count and append a 124 // string with the count. 125 m.subNames[name] = next + 1 126 127 // Add a count to guarantee uniqueness. 128 name = fmt.Sprintf("%s#%02d", name, next) 129 empty = false 130 } 131} 132 133// rewrite rewrites a subname to having only printable characters and no white 134// space. 135func rewrite(s string) string { 136 b := []byte{} 137 for _, r := range s { 138 switch { 139 case isSpace(r): 140 b = append(b, '_') 141 case !strconv.IsPrint(r): 142 s := strconv.QuoteRune(r) 143 b = append(b, s[1:len(s)-1]...) 144 default: 145 b = append(b, string(r)...) 146 } 147 } 148 return string(b) 149} 150 151func isSpace(r rune) bool { 152 if r < 0x2000 { 153 switch r { 154 // Note: not the same as Unicode Z class. 155 case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680: 156 return true 157 } 158 } else { 159 if r <= 0x200a { 160 return true 161 } 162 switch r { 163 case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000: 164 return true 165 } 166 } 167 return false 168} 169