...

Source file src/testing/match.go

     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