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