...
Run Format

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 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		for i, s := range strings.Split(name, "/") {
    66			if i >= len(m.filter) {
    67				break
    68			}
    69			if ok, _ := m.matchFunc(m.filter[i], s); !ok {
    70				return name, false
    71			}
    72		}
    73		return name, true
    74	}
    75	
    76	func splitRegexp(s string) []string {
    77		a := make([]string, 0, strings.Count(s, "/"))
    78		cs := 0
    79		cp := 0
    80		for i := 0; i < len(s); {
    81			switch s[i] {
    82			case '[':
    83				cs++
    84			case ']':
    85				if cs--; cs < 0 { // An unmatched ']' is legal.
    86					cs = 0
    87				}
    88			case '(':
    89				if cs == 0 {
    90					cp++
    91				}
    92			case ')':
    93				if cs == 0 {
    94					cp--
    95				}
    96			case '\\':
    97				i++
    98			case '/':
    99				if cs == 0 && cp == 0 {
   100					a = append(a, s[:i])
   101					s = s[i+1:]
   102					i = 0
   103					continue
   104				}
   105			}
   106			i++
   107		}
   108		return append(a, s)
   109	}
   110	
   111	// unique creates a unique name for the given parent and subname by affixing it
   112	// with one ore more counts, if necessary.
   113	func (m *matcher) unique(parent, subname string) string {
   114		name := fmt.Sprintf("%s/%s", parent, subname)
   115		empty := subname == ""
   116		for {
   117			next, exists := m.subNames[name]
   118			if !empty && !exists {
   119				m.subNames[name] = 1 // next count is 1
   120				return name
   121			}
   122			// Name was already used. We increment with the count and append a
   123			// string with the count.
   124			m.subNames[name] = next + 1
   125	
   126			// Add a count to guarantee uniqueness.
   127			name = fmt.Sprintf("%s#%02d", name, next)
   128			empty = false
   129		}
   130	}
   131	
   132	// rewrite rewrites a subname to having only printable characters and no white
   133	// space.
   134	func rewrite(s string) string {
   135		b := []byte{}
   136		for _, r := range s {
   137			switch {
   138			case isSpace(r):
   139				b = append(b, '_')
   140			case !strconv.IsPrint(r):
   141				s := strconv.QuoteRune(r)
   142				b = append(b, s[1:len(s)-1]...)
   143			default:
   144				b = append(b, string(r)...)
   145			}
   146		}
   147		return string(b)
   148	}
   149	
   150	func isSpace(r rune) bool {
   151		if r < 0x2000 {
   152			switch r {
   153			// Note: not the same as Unicode Z class.
   154			case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680:
   155				return true
   156			}
   157		} else {
   158			if r <= 0x200a {
   159				return true
   160			}
   161			switch r {
   162			case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
   163				return true
   164			}
   165		}
   166		return false
   167	}
   168	

View as plain text