// 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 (
	
	
	
	
	
)

// matcher sanitizes, uniques, and filters names of subtests and subbenchmarks.
type matcher struct {
	filter    filterMatch
	skip      filterMatch
	matchFunc func(pat, str string) (bool, error)

	mu sync.Mutex

	// subNames is used to deduplicate subtest names.
	// Each key is the subtest name joined to the deduplicated name of the parent test.
	// Each value is the count of the number of occurrences of the given subtest name
	// already seen.
	subNames map[string]int32
}

type filterMatch interface {
	// matches checks the name against the receiver's pattern strings using the
	// given match function.
	matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool)

	// verify checks that the receiver's pattern strings are valid filters by
	// calling the given match function.
	verify(name string, matchString func(pat, str string) (bool, error)) error
}

// simpleMatch matches a test name if all of the pattern strings match in
// sequence.
type simpleMatch []string

// alternationMatch matches a test name if one of the alternations match.
type alternationMatch []filterMatch

// TODO: fix test_main to avoid race and improve caching, also allowing to
// eliminate this Mutex.
var matchMutex sync.Mutex

func allMatcher() *matcher {
	return newMatcher(nil, "", "", "")
}

func newMatcher( func(,  string) (bool, error), , ,  string) *matcher {
	var ,  filterMatch
	if  == "" {
		 = simpleMatch{} // always partial true
	} else {
		 = splitRegexp()
		if  := .verify(, );  != nil {
			fmt.Fprintf(os.Stderr, "testing: invalid regexp for %s\n", )
			os.Exit(1)
		}
	}
	if  == "" {
		 = alternationMatch{} // always false
	} else {
		 = splitRegexp()
		if  := .verify("-test.skip", );  != nil {
			fmt.Fprintf(os.Stderr, "testing: invalid regexp for %v\n", )
			os.Exit(1)
		}
	}
	return &matcher{
		filter:    ,
		skip:      ,
		matchFunc: ,
		subNames:  map[string]int32{},
	}
}

func ( *matcher) ( *common,  string) ( string, ,  bool) {
	 = 

	.mu.Lock()
	defer .mu.Unlock()

	if  != nil && .level > 0 {
		 = .unique(.name, rewrite())
	}

	matchMutex.Lock()
	defer matchMutex.Unlock()

	// We check the full array of paths each time to allow for the case that a pattern contains a '/'.
	 := strings.Split(, "/")

	// filter must match.
	// accept partial match that may produce full match later.
	,  = .filter.matches(, .matchFunc)
	if ! {
		return , false, false
	}

	// skip must not match.
	// ignore partial match so we can get to more precise match later.
	,  := .skip.matches(, .matchFunc)
	if  && ! {
		return , false, false
	}

	return , , 
}

// clearSubNames clears the matcher's internal state, potentially freeing
// memory. After this is called, T.Name may return the same strings as it did
// for earlier subtests.
func ( *matcher) () {
	.mu.Lock()
	defer .mu.Unlock()
	clear(.subNames)
}

func ( simpleMatch) ( []string,  func(,  string) (bool, error)) (,  bool) {
	for ,  := range  {
		if  >= len() {
			break
		}
		if ,  := ([], ); ! {
			return false, false
		}
	}
	return true, len() < len()
}

func ( simpleMatch) ( string,  func(,  string) (bool, error)) error {
	for ,  := range  {
		[] = rewrite()
	}
	// Verify filters before doing any processing.
	for ,  := range  {
		if ,  := (, "non-empty");  != nil {
			return fmt.Errorf("element %d of %s (%q): %s", , , , )
		}
	}
	return nil
}

func ( alternationMatch) ( []string,  func(,  string) (bool, error)) (,  bool) {
	for ,  := range  {
		if ,  = .matches(, );  {
			return , 
		}
	}
	return false, false
}

func ( alternationMatch) ( string,  func(,  string) (bool, error)) error {
	for ,  := range  {
		if  := .verify(, );  != nil {
			return fmt.Errorf("alternation %d of %s", , )
		}
	}
	return nil
}

func splitRegexp( string) filterMatch {
	 := make(simpleMatch, 0, strings.Count(, "/"))
	 := make(alternationMatch, 0, strings.Count(, "|"))
	 := 0
	 := 0
	for  := 0;  < len(); {
		switch [] {
		case '[':
			++
		case ']':
			if --;  < 0 { // An unmatched ']' is legal.
				 = 0
			}
		case '(':
			if  == 0 {
				++
			}
		case ')':
			if  == 0 {
				--
			}
		case '\\':
			++
		case '/':
			if  == 0 &&  == 0 {
				 = append(, [:])
				 = [+1:]
				 = 0
				continue
			}
		case '|':
			if  == 0 &&  == 0 {
				 = append(, [:])
				 = [+1:]
				 = 0
				 = append(, )
				 = make(simpleMatch, 0, len())
				continue
			}
		}
		++
	}

	 = append(, )
	if len() == 0 {
		return 
	}
	return append(, )
}

// unique creates a unique name for the given parent and subname by affixing it
// with one or more counts, if necessary.
func ( *matcher) (,  string) string {
	 :=  + "/" + 

	for {
		 := .subNames[]
		if  < 0 {
			panic("subtest count overflow")
		}
		.subNames[] =  + 1

		if  == 0 &&  != "" {
			,  := parseSubtestNumber()
			if len() < len() &&  < .subNames[] {
				// This test is explicitly named like "parent/subname#NN",
				// and #NN was already used for the NNth occurrence of "parent/subname".
				// Loop to add a disambiguating suffix.
				continue
			}
			return 
		}

		 := fmt.Sprintf("%s#%02d", , )
		if .subNames[] != 0 {
			// This is the nth occurrence of base, but the name "parent/subname#NN"
			// collides with the first occurrence of a subtest *explicitly* named
			// "parent/subname#NN". Try the next number.
			continue
		}

		return 
	}
}

// parseSubtestNumber splits a subtest name into a "#%02d"-formatted int32
// suffix (if present), and a prefix preceding that suffix (always).
func parseSubtestNumber( string) ( string,  int32) {
	 := strings.LastIndex(, "#")
	if  < 0 {
		return , 0
	}

	,  := [:], [+1:]
	if len() < 2 || (len() > 2 && [0] == '0') {
		// Even if suffix is numeric, it is not a possible output of a "%02" format
		// string: it has either too few digits or too many leading zeroes.
		return , 0
	}
	if  == "00" {
		if !strings.HasSuffix(, "/") {
			// We only use "#00" as a suffix for subtests named with the empty
			// string — it isn't a valid suffix if the subtest name is non-empty.
			return , 0
		}
	}

	,  := strconv.ParseInt(, 10, 32)
	if  != nil ||  < 0 {
		return , 0
	}
	return , int32()
}

// rewrite rewrites a subname to having only printable characters and no white
// space.
func rewrite( string) string {
	 := []byte{}
	for ,  := range  {
		switch {
		case isSpace():
			 = append(, '_')
		case !strconv.IsPrint():
			 := strconv.QuoteRune()
			 = append(, [1:len()-1]...)
		default:
			 = append(, string()...)
		}
	}
	return string()
}

func isSpace( rune) bool {
	if  < 0x2000 {
		switch  {
		// Note: not the same as Unicode Z class.
		case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680:
			return true
		}
	} else {
		if  <= 0x200a {
			return true
		}
		switch  {
		case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
			return true
		}
	}
	return false
}