...

Source file src/testing/match.go

Documentation: testing

		 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, 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  
		77  func 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 or more counts, if necessary.
	 114  func (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.
	 135  func 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  
	 151  func 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  

View as plain text