github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/testing/match.go (about) 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 5 package testing 6 7 import ( 8 "fmt" 9 "os" 10 "strconv" 11 "strings" 12 "sync" 13 ) 14 15 // matcher sanitizes, uniques, and filters names of subtests and subbenchmarks. 16 type 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. 26 var matchMutex sync.Mutex 27 28 func 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 50 func (m *matcher) fullName(c *common, subname string) (name string, ok 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 for i, s := range strings.Split(name, "/") { 66 if i >= len(m.filter) { 67 break 68 } 69 if ok, _ := m.matchFunc(m.filter[i], s); !ok { 70 return name, false 71 } 72 } 73 return name, true 74 } 75 76 func splitRegexp(s string) []string { 77 a := make([]string, 0, strings.Count(s, "/")) 78 cs := 0 79 cp := 0 80 for i := 0; i < len(s); { 81 switch s[i] { 82 case '[': 83 cs++ 84 case ']': 85 if cs--; cs < 0 { // An unmatched ']' is legal. 86 cs = 0 87 } 88 case '(': 89 if cs == 0 { 90 cp++ 91 } 92 case ')': 93 if cs == 0 { 94 cp-- 95 } 96 case '\\': 97 i++ 98 case '/': 99 if cs == 0 && cp == 0 { 100 a = append(a, s[:i]) 101 s = s[i+1:] 102 i = 0 103 continue 104 } 105 } 106 i++ 107 } 108 return append(a, s) 109 } 110 111 // unique creates a unique name for the given parent and subname by affixing it 112 // with one ore more counts, if necessary. 113 func (m *matcher) unique(parent, subname string) string { 114 name := fmt.Sprintf("%s/%s", parent, subname) 115 empty := subname == "" 116 for { 117 next, exists := m.subNames[name] 118 if !empty && !exists { 119 m.subNames[name] = 1 // next count is 1 120 return name 121 } 122 // Name was already used. We increment with the count and append a 123 // string with the count. 124 m.subNames[name] = next + 1 125 126 // Add a count to guarantee uniqueness. 127 name = fmt.Sprintf("%s#%02d", name, next) 128 empty = false 129 } 130 } 131 132 // rewrite rewrites a subname to having only printable characters and no white 133 // space. 134 func rewrite(s string) string { 135 b := []byte{} 136 for _, r := range s { 137 switch { 138 case isSpace(r): 139 b = append(b, '_') 140 case !strconv.IsPrint(r): 141 s := strconv.QuoteRune(r) 142 b = append(b, s[1:len(s)-1]...) 143 default: 144 b = append(b, string(r)...) 145 } 146 } 147 return string(b) 148 } 149 150 func isSpace(r rune) bool { 151 if r < 0x2000 { 152 switch r { 153 // Note: not the same as Unicode Z class. 154 case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680: 155 return true 156 } 157 } else { 158 if r <= 0x200a { 159 return true 160 } 161 switch r { 162 case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000: 163 return true 164 } 165 } 166 return false 167 }