...
Run Format

Source file src/testing/match.go

  // Copyright 2015 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  package testing
  
  import (
  	"fmt"
  	"os"
  	"strconv"
  	"strings"
  	"sync"
  )
  
  // matcher sanitizes, uniques, and filters names of subtests and subbenchmarks.
  type matcher struct {
  	filter    []string
  	matchFunc func(pat, str string) (bool, error)
  
  	mu       sync.Mutex
  	subNames map[string]int64
  }
  
  // TODO: fix test_main to avoid race and improve caching, also allowing to
  // eliminate this Mutex.
  var matchMutex sync.Mutex
  
  func newMatcher(matchString func(pat, str string) (bool, error), patterns, name string) *matcher {
  	var filter []string
  	if patterns != "" {
  		filter = splitRegexp(patterns)
  		for i, s := range filter {
  			filter[i] = rewrite(s)
  		}
  		// Verify filters before doing any processing.
  		for i, s := range filter {
  			if _, err := matchString(s, "non-empty"); err != nil {
  				fmt.Fprintf(os.Stderr, "testing: invalid regexp for element %d of %s (%q): %s\n", i, name, s, err)
  				os.Exit(1)
  			}
  		}
  	}
  	return &matcher{
  		filter:    filter,
  		matchFunc: matchString,
  		subNames:  map[string]int64{},
  	}
  }
  
  func (m *matcher) fullName(c *common, subname string) (name string, ok bool) {
  	name = subname
  
  	m.mu.Lock()
  	defer m.mu.Unlock()
  
  	if c != nil && c.level > 0 {
  		name = m.unique(c.name, rewrite(subname))
  	}
  
  	matchMutex.Lock()
  	defer matchMutex.Unlock()
  
  	// We check the full array of paths each time to allow for the case that
  	// a pattern contains a '/'.
  	for i, s := range strings.Split(name, "/") {
  		if i >= len(m.filter) {
  			break
  		}
  		if ok, _ := m.matchFunc(m.filter[i], s); !ok {
  			return name, false
  		}
  	}
  	return name, true
  }
  
  func splitRegexp(s string) []string {
  	a := make([]string, 0, strings.Count(s, "/"))
  	cs := 0
  	cp := 0
  	for i := 0; i < len(s); {
  		switch s[i] {
  		case '[':
  			cs++
  		case ']':
  			if cs--; cs < 0 { // An unmatched ']' is legal.
  				cs = 0
  			}
  		case '(':
  			if cs == 0 {
  				cp++
  			}
  		case ')':
  			if cs == 0 {
  				cp--
  			}
  		case '\\':
  			i++
  		case '/':
  			if cs == 0 && cp == 0 {
  				a = append(a, s[:i])
  				s = s[i+1:]
  				i = 0
  				continue
  			}
  		}
  		i++
  	}
  	return append(a, s)
  }
  
  // unique creates a unique name for the given parent and subname by affixing it
  // with one ore more counts, if necessary.
  func (m *matcher) unique(parent, subname string) string {
  	name := fmt.Sprintf("%s/%s", parent, subname)
  	empty := subname == ""
  	for {
  		next, exists := m.subNames[name]
  		if !empty && !exists {
  			m.subNames[name] = 1 // next count is 1
  			return name
  		}
  		// Name was already used. We increment with the count and append a
  		// string with the count.
  		m.subNames[name] = next + 1
  
  		// Add a count to guarantee uniqueness.
  		name = fmt.Sprintf("%s#%02d", name, next)
  		empty = false
  	}
  }
  
  // rewrite rewrites a subname to having only printable characters and no white
  // space.
  func rewrite(s string) string {
  	b := []byte{}
  	for _, r := range s {
  		switch {
  		case isSpace(r):
  			b = append(b, '_')
  		case !strconv.IsPrint(r):
  			s := strconv.QuoteRune(r)
  			b = append(b, s[1:len(s)-1]...)
  		default:
  			b = append(b, string(r)...)
  		}
  	}
  	return string(b)
  }
  
  func isSpace(r rune) bool {
  	if r < 0x2000 {
  		switch r {
  		// Note: not the same as Unicode Z class.
  		case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680:
  			return true
  		}
  	} else {
  		if r <= 0x200a {
  			return true
  		}
  		switch r {
  		case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
  			return true
  		}
  	}
  	return false
  }
  

View as plain text