Black Lives Matter. Support the Equal Justice Initiative.

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