github.com/coreos/mantle@v0.13.0/harness/match.go (about) 1 // Copyright 2017 CoreOS, Inc. 2 // Copyright 2015 The Go Authors. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package harness 17 18 import ( 19 "fmt" 20 "os" 21 "regexp" 22 "strconv" 23 "strings" 24 "sync" 25 "unicode" 26 ) 27 28 // matcher sanitizes, uniques, and filters names of subtests and subbenchmarks. 29 type matcher struct { 30 filter []string 31 32 mu sync.Mutex 33 subNames map[string]int64 34 } 35 36 // TODO: fix test_main to avoid race and improve caching, also allowing to 37 // eliminate this Mutex. 38 var matchMutex sync.Mutex 39 var matchPat string 40 var matchRe *regexp.Regexp 41 42 func matchString(pat, str string) (result bool, err error) { 43 if matchRe == nil || matchPat != pat { 44 matchPat = pat 45 matchRe, err = regexp.Compile(matchPat) 46 if err != nil { 47 return 48 } 49 } 50 return matchRe.MatchString(str), nil 51 } 52 53 func newMatcher(patterns, name string) *matcher { 54 var filter []string 55 if patterns != "" { 56 filter = splitRegexp(patterns) 57 for i, s := range filter { 58 filter[i] = rewrite(s) 59 } 60 // Verify filters before doing any processing. 61 for i, s := range filter { 62 if _, err := matchString(s, "non-empty"); err != nil { 63 fmt.Fprintf(os.Stderr, "testing: invalid regexp for element %d of %s (%q): %s\n", i, name, s, err) 64 os.Exit(1) 65 } 66 } 67 } 68 return &matcher{ 69 filter: filter, 70 subNames: map[string]int64{}, 71 } 72 } 73 74 func (m *matcher) fullName(c *H, subname string) (name string, ok bool) { 75 name = subname 76 77 m.mu.Lock() 78 defer m.mu.Unlock() 79 80 if c != nil && c.level > 0 { 81 name = m.unique(c.name, rewrite(subname)) 82 } 83 84 matchMutex.Lock() 85 defer matchMutex.Unlock() 86 87 // We check the full array of paths each time to allow for the case that 88 // a pattern contains a '/'. 89 for i, s := range strings.Split(name, "/") { 90 if i >= len(m.filter) { 91 break 92 } 93 if ok, _ := matchString(m.filter[i], s); !ok { 94 return name, false 95 } 96 } 97 return name, true 98 } 99 100 func splitRegexp(s string) []string { 101 a := make([]string, 0, strings.Count(s, "/")) 102 cs := 0 103 cp := 0 104 for i := 0; i < len(s); { 105 switch s[i] { 106 case '[': 107 cs++ 108 case ']': 109 if cs--; cs < 0 { // An unmatched ']' is legal. 110 cs = 0 111 } 112 case '(': 113 if cs == 0 { 114 cp++ 115 } 116 case ')': 117 if cs == 0 { 118 cp-- 119 } 120 case '\\': 121 i++ 122 case '/': 123 if cs == 0 && cp == 0 { 124 a = append(a, s[:i]) 125 s = s[i+1:] 126 i = 0 127 continue 128 } 129 } 130 i++ 131 } 132 return append(a, s) 133 } 134 135 // unique creates a unique name for the given parent and subname by affixing it 136 // with one ore more counts, if necessary. 137 func (m *matcher) unique(parent, subname string) string { 138 name := fmt.Sprintf("%s/%s", parent, subname) 139 empty := subname == "" 140 for { 141 next, exists := m.subNames[name] 142 if !empty && !exists { 143 m.subNames[name] = 1 // next count is 1 144 return name 145 } 146 // Name was already used. We increment with the count and append a 147 // string with the count. 148 m.subNames[name] = next + 1 149 150 // Add a count to guarantee uniqueness. 151 name = fmt.Sprintf("%s#%02d", name, next) 152 empty = false 153 } 154 } 155 156 // rewrite rewrites a subname to having only printable characters and no white 157 // space. 158 func rewrite(s string) string { 159 b := []byte{} 160 for _, r := range s { 161 switch { 162 case unicode.IsSpace(r): 163 b = append(b, '_') 164 case !strconv.IsPrint(r): 165 s := strconv.QuoteRune(r) 166 b = append(b, s[1:len(s)-1]...) 167 default: 168 b = append(b, string(r)...) 169 } 170 } 171 return string(b) 172 }