...

Source file src/github.com/dlclark/regexp2/regexp_test.go

Documentation: github.com/dlclark/regexp2

     1  package regexp2
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/dlclark/regexp2/syntax"
    11  )
    12  
    13  func TestBacktrack_CatastrophicTimeout(t *testing.T) {
    14  	r, err := Compile("(.+)*\\?", 0)
    15  	if err != nil {
    16  		t.Fatal(err)
    17  	}
    18  	t.Logf("code dump: %v", r.code.Dump())
    19  	const subject = "Do you think you found the problem string!"
    20  
    21  	const earlyAllowance = 10 * time.Millisecond
    22  	var lateAllowance = clockPeriod + 500*time.Millisecond // Large allowance in case machine is slow
    23  
    24  	for _, timeout := range []time.Duration{
    25  		-1 * time.Millisecond,
    26  		0 * time.Millisecond,
    27  		1 * time.Millisecond,
    28  		10 * time.Millisecond,
    29  		100 * time.Millisecond,
    30  		500 * time.Millisecond,
    31  		1000 * time.Millisecond,
    32  	} {
    33  		t.Run(fmt.Sprint(timeout), func(t *testing.T) {
    34  			r.MatchTimeout = timeout
    35  			start := time.Now()
    36  			m, err := r.FindStringMatch(subject)
    37  			elapsed := time.Since(start)
    38  			if err == nil {
    39  				t.Errorf("expected timeout err")
    40  			}
    41  			if m != nil {
    42  				t.Errorf("Expected no match")
    43  			}
    44  			t.Logf("timeed out after %v", elapsed)
    45  			if elapsed < timeout-earlyAllowance {
    46  				t.Errorf("Match timed out too quickly (%v instead of expected %v)", elapsed, timeout-earlyAllowance)
    47  			}
    48  			if elapsed > timeout+lateAllowance {
    49  				t.Errorf("Match timed out too late (%v instead of expected %v)", elapsed, timeout+lateAllowance)
    50  			}
    51  		})
    52  	}
    53  }
    54  
    55  func TestSetPrefix(t *testing.T) {
    56  	r := MustCompile(`^\s*-TEST`, 0)
    57  	if r.code.FcPrefix == nil {
    58  		t.Fatalf("Expected prefix set [-\\s] but was nil")
    59  	}
    60  	if r.code.FcPrefix.PrefixSet.String() != "[-\\s]" {
    61  		t.Fatalf("Expected prefix set [\\s-] but was %v", r.code.FcPrefix.PrefixSet.String())
    62  	}
    63  }
    64  
    65  func TestSetInCode(t *testing.T) {
    66  	r := MustCompile(`(?<body>\s*(?<name>.+))`, 0)
    67  	t.Logf("code dump: %v", r.code.Dump())
    68  	if want, got := 1, len(r.code.Sets); want != got {
    69  		t.Fatalf("r.code.Sets wanted %v, got %v", want, got)
    70  	}
    71  	if want, got := "[\\s]", r.code.Sets[0].String(); want != got {
    72  		t.Fatalf("first set wanted %v, got %v", want, got)
    73  	}
    74  }
    75  
    76  func TestRegexp_Basic(t *testing.T) {
    77  	r, err := Compile("test(?<named>ing)?", 0)
    78  	//t.Logf("code dump: %v", r.code.Dump())
    79  
    80  	if err != nil {
    81  		t.Errorf("unexpected compile err: %v", err)
    82  	}
    83  	m, err := r.FindStringMatch("this is a testing stuff")
    84  	if err != nil {
    85  		t.Errorf("unexpected match err: %v", err)
    86  	}
    87  	if m == nil {
    88  		t.Error("Nil match, expected success")
    89  	} else {
    90  		//t.Logf("Match: %v", m.dump())
    91  	}
    92  }
    93  
    94  // check all our functions and properties around basic capture groups and referential for Group 0
    95  func TestCapture_Basic(t *testing.T) {
    96  	r := MustCompile(`.*\B(SUCCESS)\B.*`, 0)
    97  	m, err := r.FindStringMatch("adfadsfSUCCESSadsfadsf")
    98  	if err != nil {
    99  		t.Fatalf("Unexpected match error: %v", err)
   100  	}
   101  
   102  	if m == nil {
   103  		t.Fatalf("Should have matched")
   104  	}
   105  	if want, got := "adfadsfSUCCESSadsfadsf", m.String(); want != got {
   106  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   107  	}
   108  	if want, got := 0, m.Index; want != got {
   109  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   110  	}
   111  	if want, got := 22, m.Length; want != got {
   112  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   113  	}
   114  	if want, got := 1, len(m.Captures); want != got {
   115  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   116  	}
   117  
   118  	if want, got := m.String(), m.Captures[0].String(); want != got {
   119  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   120  	}
   121  	if want, got := 0, m.Captures[0].Index; want != got {
   122  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   123  	}
   124  	if want, got := 22, m.Captures[0].Length; want != got {
   125  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   126  	}
   127  
   128  	g := m.Groups()
   129  	if want, got := 2, len(g); want != got {
   130  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   131  	}
   132  	// group 0 is always the match
   133  	if want, got := m.String(), g[0].String(); want != got {
   134  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   135  	}
   136  	if want, got := 1, len(g[0].Captures); want != got {
   137  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   138  	}
   139  	// group 0's capture is always the match
   140  	if want, got := m.Captures[0].String(), g[0].Captures[0].String(); want != got {
   141  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   142  	}
   143  
   144  	// group 1 is our first explicit group (unnamed)
   145  	if want, got := 7, g[1].Index; want != got {
   146  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   147  	}
   148  	if want, got := 7, g[1].Length; want != got {
   149  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   150  	}
   151  	if want, got := "SUCCESS", g[1].String(); want != got {
   152  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   153  	}
   154  }
   155  
   156  func TestEscapeUnescape_Basic(t *testing.T) {
   157  	s1 := "#$^*+(){}<>\\|. "
   158  	s2 := Escape(s1)
   159  	s3, err := Unescape(s2)
   160  	if err != nil {
   161  		t.Fatalf("Unexpected error during unescape: %v", err)
   162  	}
   163  
   164  	//confirm one way
   165  	if want, got := `\#\$\^\*\+\(\)\{\}<>\\\|\.\ `, s2; want != got {
   166  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   167  	}
   168  
   169  	//confirm round-trip
   170  	if want, got := s1, s3; want != got {
   171  		t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
   172  	}
   173  
   174  }
   175  
   176  func TestGroups_Basic(t *testing.T) {
   177  	type d struct {
   178  		p    string
   179  		s    string
   180  		name []string
   181  		num  []int
   182  		strs []string
   183  	}
   184  	data := []d{
   185  		d{"(?<first_name>\\S+)\\s(?<last_name>\\S+)", // example
   186  			"Ryan Byington",
   187  			[]string{"0", "first_name", "last_name"},
   188  			[]int{0, 1, 2},
   189  			[]string{"Ryan Byington", "Ryan", "Byington"}},
   190  		d{"((?<One>abc)\\d+)?(?<Two>xyz)(.*)", // example
   191  			"abc208923xyzanqnakl",
   192  			[]string{"0", "1", "2", "One", "Two"},
   193  			[]int{0, 1, 2, 3, 4},
   194  			[]string{"abc208923xyzanqnakl", "abc208923", "anqnakl", "abc", "xyz"}},
   195  		d{"((?<256>abc)\\d+)?(?<16>xyz)(.*)", // numeric names
   196  			"0272saasdabc8978xyz][]12_+-",
   197  			[]string{"0", "1", "2", "16", "256"},
   198  			[]int{0, 1, 2, 16, 256},
   199  			[]string{"abc8978xyz][]12_+-", "abc8978", "][]12_+-", "xyz", "abc"}},
   200  		d{"((?<4>abc)(?<digits>\\d+))?(?<2>xyz)(?<everything_else>.*)", // mix numeric and string names
   201  			"0272saasdabc8978xyz][]12_+-",
   202  			[]string{"0", "1", "2", "digits", "4", "everything_else"},
   203  			[]int{0, 1, 2, 3, 4, 5},
   204  			[]string{"abc8978xyz][]12_+-", "abc8978", "xyz", "8978", "abc", "][]12_+-"}},
   205  		d{"(?<first_name>\\S+)\\s(?<first_name>\\S+)", // dupe string names
   206  			"Ryan Byington",
   207  			[]string{"0", "first_name"},
   208  			[]int{0, 1},
   209  			[]string{"Ryan Byington", "Byington"}},
   210  		d{"(?<15>\\S+)\\s(?<15>\\S+)", // dupe numeric names
   211  			"Ryan Byington",
   212  			[]string{"0", "15"},
   213  			[]int{0, 15},
   214  			[]string{"Ryan Byington", "Byington"}},
   215  		// *** repeated from above, but with alt cap syntax ***
   216  		d{"(?'first_name'\\S+)\\s(?'last_name'\\S+)", //example
   217  			"Ryan Byington",
   218  			[]string{"0", "first_name", "last_name"},
   219  			[]int{0, 1, 2},
   220  			[]string{"Ryan Byington", "Ryan", "Byington"}},
   221  		d{"((?'One'abc)\\d+)?(?'Two'xyz)(.*)", // example
   222  			"abc208923xyzanqnakl",
   223  			[]string{"0", "1", "2", "One", "Two"},
   224  			[]int{0, 1, 2, 3, 4},
   225  			[]string{"abc208923xyzanqnakl", "abc208923", "anqnakl", "abc", "xyz"}},
   226  		d{"((?'256'abc)\\d+)?(?'16'xyz)(.*)", // numeric names
   227  			"0272saasdabc8978xyz][]12_+-",
   228  			[]string{"0", "1", "2", "16", "256"},
   229  			[]int{0, 1, 2, 16, 256},
   230  			[]string{"abc8978xyz][]12_+-", "abc8978", "][]12_+-", "xyz", "abc"}},
   231  		d{"((?'4'abc)(?'digits'\\d+))?(?'2'xyz)(?'everything_else'.*)", // mix numeric and string names
   232  			"0272saasdabc8978xyz][]12_+-",
   233  			[]string{"0", "1", "2", "digits", "4", "everything_else"},
   234  			[]int{0, 1, 2, 3, 4, 5},
   235  			[]string{"abc8978xyz][]12_+-", "abc8978", "xyz", "8978", "abc", "][]12_+-"}},
   236  		d{"(?'first_name'\\S+)\\s(?'first_name'\\S+)", // dupe string names
   237  			"Ryan Byington",
   238  			[]string{"0", "first_name"},
   239  			[]int{0, 1},
   240  			[]string{"Ryan Byington", "Byington"}},
   241  		d{"(?'15'\\S+)\\s(?'15'\\S+)", // dupe numeric names
   242  			"Ryan Byington",
   243  			[]string{"0", "15"},
   244  			[]int{0, 15},
   245  			[]string{"Ryan Byington", "Byington"}},
   246  	}
   247  
   248  	fatalf := func(re *Regexp, v d, format string, args ...interface{}) {
   249  		args = append(args, v, re.code.Dump())
   250  
   251  		t.Fatalf(format+" using test data: %#v\ndump:%v", args...)
   252  	}
   253  
   254  	validateGroupNamesNumbers := func(re *Regexp, v d) {
   255  		if len(v.name) != len(v.num) {
   256  			fatalf(re, v, "Invalid data, group name count and number count must match")
   257  		}
   258  
   259  		groupNames := re.GetGroupNames()
   260  		if !reflect.DeepEqual(groupNames, v.name) {
   261  			fatalf(re, v, "group names expected: %v, actual: %v", v.name, groupNames)
   262  		}
   263  		groupNums := re.GetGroupNumbers()
   264  		if !reflect.DeepEqual(groupNums, v.num) {
   265  			fatalf(re, v, "group numbers expected: %v, actual: %v", v.num, groupNums)
   266  		}
   267  		// make sure we can freely get names and numbers from eachother
   268  		for i := range groupNums {
   269  			if want, got := groupNums[i], re.GroupNumberFromName(groupNames[i]); want != got {
   270  				fatalf(re, v, "group num from name Wanted '%v'\nGot '%v'", want, got)
   271  			}
   272  			if want, got := groupNames[i], re.GroupNameFromNumber(groupNums[i]); want != got {
   273  				fatalf(re, v, "group name from num Wanted '%v'\nGot '%v'", want, got)
   274  			}
   275  		}
   276  	}
   277  
   278  	for _, v := range data {
   279  		// compile the regex
   280  		re := MustCompile(v.p, 0)
   281  
   282  		// validate our group name/num info before execute
   283  		validateGroupNamesNumbers(re, v)
   284  
   285  		m, err := re.FindStringMatch(v.s)
   286  		if err != nil {
   287  			fatalf(re, v, "Unexpected error in match: %v", err)
   288  		}
   289  		if m == nil {
   290  			fatalf(re, v, "Match is nil")
   291  		}
   292  		if want, got := len(v.strs), m.GroupCount(); want != got {
   293  			fatalf(re, v, "GroupCount() Wanted '%v'\nGot '%v'", want, got)
   294  		}
   295  		g := m.Groups()
   296  		if want, got := len(v.strs), len(g); want != got {
   297  			fatalf(re, v, "len(m.Groups()) Wanted '%v'\nGot '%v'", want, got)
   298  		}
   299  		// validate each group's value from the execute
   300  		for i := range v.name {
   301  			grp1 := m.GroupByName(v.name[i])
   302  			grp2 := m.GroupByNumber(v.num[i])
   303  			// should be identical reference
   304  			if grp1 != grp2 {
   305  				fatalf(re, v, "Expected GroupByName and GroupByNumber to return same result for %v, %v", v.name[i], v.num[i])
   306  			}
   307  			if want, got := v.strs[i], grp1.String(); want != got {
   308  				fatalf(re, v, "Value[%v] Wanted '%v'\nGot '%v'", i, want, got)
   309  			}
   310  		}
   311  
   312  		// validate our group name/num info after execute
   313  		validateGroupNamesNumbers(re, v)
   314  	}
   315  }
   316  
   317  func TestErr_GroupName(t *testing.T) {
   318  	// group 0 is off limits
   319  	if _, err := Compile("foo(?<0>bar)", 0); err == nil {
   320  		t.Fatalf("zero group, expected error during compile")
   321  	} else if want, got := "error parsing regexp: capture number cannot be zero in `foo(?<0>bar)`", err.Error(); want != got {
   322  		t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
   323  	}
   324  	if _, err := Compile("foo(?'0'bar)", 0); err == nil {
   325  		t.Fatalf("zero group, expected error during compile")
   326  	} else if want, got := "error parsing regexp: capture number cannot be zero in `foo(?'0'bar)`", err.Error(); want != got {
   327  		t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
   328  	}
   329  
   330  	// group tag can't start with a num
   331  	if _, err := Compile("foo(?<1bar>)", 0); err == nil {
   332  		t.Fatalf("invalid group name, expected error during compile")
   333  	} else if want, got := "error parsing regexp: invalid group name: group names must begin with a word character and have a matching terminator in `foo(?<1bar>)`", err.Error(); want != got {
   334  		t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
   335  	}
   336  	if _, err := Compile("foo(?'1bar')", 0); err == nil {
   337  		t.Fatalf("invalid group name, expected error during compile")
   338  	} else if want, got := "error parsing regexp: invalid group name: group names must begin with a word character and have a matching terminator in `foo(?'1bar')`", err.Error(); want != got {
   339  		t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
   340  	}
   341  
   342  	// missing closing group tag
   343  	if _, err := Compile("foo(?<bar)", 0); err == nil {
   344  		t.Fatalf("invalid group name, expected error during compile")
   345  	} else if want, got := "error parsing regexp: invalid group name: group names must begin with a word character and have a matching terminator in `foo(?<bar)`", err.Error(); want != got {
   346  		t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
   347  	}
   348  	if _, err := Compile("foo(?'bar)", 0); err == nil {
   349  		t.Fatalf("invalid group name, expected error during compile")
   350  	} else if want, got := "error parsing regexp: invalid group name: group names must begin with a word character and have a matching terminator in `foo(?'bar)`", err.Error(); want != got {
   351  		t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
   352  	}
   353  
   354  }
   355  
   356  func TestConstantUneffected(t *testing.T) {
   357  	// had a bug where "constant" sets would get modified with alternations and be broken in memory until restart
   358  	// this meant that if you used a known-set (like \s) in a larger set it would "poison" \s for the process
   359  	re := MustCompile(`(\s|\*)test\s`, 0)
   360  	if want, got := 2, len(re.code.Sets); want != got {
   361  		t.Fatalf("wanted %v sets, got %v", want, got)
   362  	}
   363  	if want, got := "[\\*\\s]", re.code.Sets[0].String(); want != got {
   364  		t.Fatalf("wanted set 0 %v, got %v", want, got)
   365  	}
   366  	if want, got := "[\\s]", re.code.Sets[1].String(); want != got {
   367  		t.Fatalf("wanted set 1 %v, got %v", want, got)
   368  	}
   369  }
   370  
   371  func TestAlternationConstAndEscape(t *testing.T) {
   372  	re := MustCompile(`\:|\s`, 0)
   373  	if want, got := 1, len(re.code.Sets); want != got {
   374  		t.Fatalf("wanted %v sets, got %v", want, got)
   375  	}
   376  	if want, got := "[:\\s]", re.code.Sets[0].String(); want != got {
   377  		t.Fatalf("wanted set 0 %v, got %v", want, got)
   378  	}
   379  }
   380  
   381  func TestStartingCharsOptionalNegate(t *testing.T) {
   382  	// to maintain matching with the corefx we've made the negative char classes be negative and the
   383  	// categories they contain positive.  This means they're not combinable or suitable for prefixes.
   384  	// In general this could be a fine thing since negatives are extremely wide groups and not
   385  	// missing much on prefix optimizations.
   386  
   387  	// the below expression *could* have a prefix of [\S\d] but
   388  	// this requires a change in charclass.go when setting
   389  	// NotSpaceClass = getCharSetFromCategoryString()
   390  	// to negate the individual categories rather than the CharSet itself
   391  	// this would deviate from corefx
   392  
   393  	re := MustCompile(`(^(\S{2} )?\S{2}(\d+|/) *\S{3}\S{3} ?\d{2,4}[A-Z] ?\d{2}[A-Z]{3}|(\S{2} )?\d{2,4})`, 0)
   394  	if re.code.FcPrefix != nil {
   395  		t.Fatalf("FcPrefix wanted nil, got %v", re.code.FcPrefix)
   396  	}
   397  }
   398  
   399  func TestParseNegativeDigit(t *testing.T) {
   400  	re := MustCompile(`\D`, 0)
   401  	if want, got := 1, len(re.code.Sets); want != got {
   402  		t.Fatalf("wanted %v sets, got %v", want, got)
   403  	}
   404  
   405  	if want, got := "[\\P{Nd}]", re.code.Sets[0].String(); want != got {
   406  		t.Fatalf("wanted set 0 %v, got %v", want, got)
   407  	}
   408  }
   409  
   410  func TestRunNegativeDigit(t *testing.T) {
   411  	re := MustCompile(`\D`, 0)
   412  	m, err := re.MatchString("this is a test")
   413  	if err != nil {
   414  		t.Fatalf("Unexpected error: %v", err)
   415  	}
   416  	if !m {
   417  		t.Fatalf("Expected match")
   418  	}
   419  }
   420  
   421  func TestCancellingClasses(t *testing.T) {
   422  	// [\w\W\s] should become "." because it means "anything"
   423  	re := MustCompile(`[\w\W\s]`, 0)
   424  	if want, got := 1, len(re.code.Sets); want != got {
   425  		t.Fatalf("wanted %v sets, got %v", want, got)
   426  	}
   427  	if want, got := syntax.AnyClass().String(), re.code.Sets[0].String(); want != got {
   428  		t.Fatalf("wanted set 0 %v, got %v", want, got)
   429  	}
   430  }
   431  
   432  func TestConcatLoopCaptureSet(t *testing.T) {
   433  	//(A|B)*?CD different Concat/Loop/Capture/Set (had [A-Z] should be [AB])
   434  	// we were not copying the Sets in the prefix FC stack, so the underlying sets were unexpectedly mutating
   435  	// so set [AB] becomes [ABC] when we see the the static C in FC stack generation (which are the valid start chars),
   436  	// but that was mutating the tree node's original set [AB] because even though we copied the slie header,
   437  	// the two header's pointed to the same underlying byte array...which was mutated.
   438  
   439  	re := MustCompile(`(A|B)*CD`, 0)
   440  	if want, got := 1, len(re.code.Sets); want != got {
   441  		t.Fatalf("wanted %v sets, got %v", want, got)
   442  	}
   443  	if want, got := "[AB]", re.code.Sets[0].String(); want != got {
   444  		t.Fatalf("wanted set 0 %v, got %v", want, got)
   445  	}
   446  }
   447  
   448  func TestFirstcharsIgnoreCase(t *testing.T) {
   449  	//((?i)AB(?-i)C|D)E different Firstchars (had [da] should be [ad])
   450  	// we were not canonicalizing when converting the prefix set to lower case
   451  	// so our set's were potentially not searching properly
   452  	re := MustCompile(`((?i)AB(?-i)C|D)E`, 0)
   453  
   454  	if re.code.FcPrefix == nil {
   455  		t.Fatalf("wanted prefix, got nil")
   456  	}
   457  
   458  	if want, got := "[ad]", re.code.FcPrefix.PrefixSet.String(); want != got {
   459  		t.Fatalf("wanted prefix %v, got %v", want, got)
   460  	}
   461  }
   462  
   463  func TestRepeatingGroup(t *testing.T) {
   464  	re := MustCompile(`(data?)+`, 0)
   465  
   466  	m, err := re.FindStringMatch("datadat")
   467  	if err != nil {
   468  		t.Fatalf("Unexpected err: %v", err)
   469  	}
   470  
   471  	if m == nil {
   472  		t.Fatalf("Expected match")
   473  	}
   474  
   475  	g := m.GroupByNumber(1)
   476  	if g == nil {
   477  		t.Fatalf("Expected group")
   478  	}
   479  
   480  	if want, got := 2, len(g.Captures); want != got {
   481  		t.Fatalf("wanted cap count %v, got %v", want, got)
   482  	}
   483  
   484  	if want, got := g.Captures[1].String(), g.Capture.String(); want != got {
   485  		t.Fatalf("expected last capture of the group to be embedded")
   486  	}
   487  
   488  	if want, got := "data", g.Captures[0].String(); want != got {
   489  		t.Fatalf("expected cap 0 to be %v, got %v", want, got)
   490  	}
   491  	if want, got := "dat", g.Captures[1].String(); want != got {
   492  		t.Fatalf("expected cap 1 to be %v, got %v", want, got)
   493  	}
   494  
   495  }
   496  
   497  func TestFindNextMatch_Basic(t *testing.T) {
   498  	re := MustCompile(`(T|E)(?=h|E|S|$)`, 0)
   499  	m, err := re.FindStringMatch(`This is a TEST`)
   500  	if err != nil {
   501  		t.Fatalf("Unexpected err 0: %v", err)
   502  	}
   503  	if m == nil {
   504  		t.Fatalf("Expected match 0")
   505  	}
   506  	if want, got := 0, m.Index; want != got {
   507  		t.Fatalf("expected match 0 to start at %v, got %v", want, got)
   508  	}
   509  
   510  	m, err = re.FindNextMatch(m)
   511  	if err != nil {
   512  		t.Fatalf("Unexpected err 1: %v", err)
   513  	}
   514  	if m == nil {
   515  		t.Fatalf("Expected match 1")
   516  	}
   517  	if want, got := 10, m.Index; want != got {
   518  		t.Fatalf("expected match 1 to start at %v, got %v", want, got)
   519  	}
   520  
   521  	m, err = re.FindNextMatch(m)
   522  	if err != nil {
   523  		t.Fatalf("Unexpected err 2: %v", err)
   524  	}
   525  	if m == nil {
   526  		t.Fatalf("Expected match 2")
   527  	}
   528  	if want, got := 11, m.Index; want != got {
   529  		t.Fatalf("expected match 2 to start at %v, got %v", want, got)
   530  	}
   531  
   532  	m, err = re.FindNextMatch(m)
   533  	if err != nil {
   534  		t.Fatalf("Unexpected err 3: %v", err)
   535  	}
   536  	if m == nil {
   537  		t.Fatalf("Expected match 3")
   538  	}
   539  	if want, got := 13, m.Index; want != got {
   540  		t.Fatalf("expected match 3 to start at %v, got %v", want, got)
   541  	}
   542  }
   543  
   544  func TestUnicodeSupplementaryCharSetMatch(t *testing.T) {
   545  	//0x2070E 0x20731 𠜱 0x20779 𠝹
   546  	re := MustCompile("[𠜎-𠝹]", 0)
   547  
   548  	if m, err := re.MatchString("\u2070"); err != nil {
   549  		t.Fatalf("Unexpected err: %v", err)
   550  	} else if m {
   551  		t.Fatalf("Unexpected match")
   552  	}
   553  
   554  	if m, err := re.MatchString("𠜱"); err != nil {
   555  		t.Fatalf("Unexpected err: %v", err)
   556  	} else if !m {
   557  		t.Fatalf("Expected match")
   558  	}
   559  }
   560  
   561  func TestUnicodeSupplementaryCharInRange(t *testing.T) {
   562  	//0x2070E 0x20731 𠜱 0x20779 𠝹
   563  	re := MustCompile(".", 0)
   564  
   565  	if m, err := re.MatchString("\u2070"); err != nil {
   566  		t.Fatalf("Unexpected err: %v", err)
   567  	} else if !m {
   568  		t.Fatalf("Expected match")
   569  	}
   570  
   571  	if m, err := re.MatchString("𠜱"); err != nil {
   572  		t.Fatalf("Unexpected err: %v", err)
   573  	} else if !m {
   574  		t.Fatalf("Expected match")
   575  	}
   576  }
   577  
   578  func TestUnicodeScriptSets(t *testing.T) {
   579  	re := MustCompile(`\p{Katakana}+`, 0)
   580  	if m, err := re.MatchString("\u30A0\u30FF"); err != nil {
   581  		t.Fatalf("Unexpected err: %v", err)
   582  	} else if !m {
   583  		t.Fatalf("Expected match")
   584  	}
   585  }
   586  
   587  func TestHexadecimalCurlyBraces(t *testing.T) {
   588  	re := MustCompile(`\x20`, 0)
   589  	if m, err := re.MatchString(" "); err != nil {
   590  		t.Fatalf("Unexpected err: %v", err)
   591  	} else if !m {
   592  		t.Fatalf("Expected match")
   593  	}
   594  
   595  	re = MustCompile(`\x{C4}`, 0)
   596  	if m, err := re.MatchString("Ä"); err != nil {
   597  		t.Fatalf("Unexpected err: %v", err)
   598  	} else if !m {
   599  		t.Fatalf("Expected match")
   600  	}
   601  
   602  	re = MustCompile(`\x{0C5}`, 0)
   603  	if m, err := re.MatchString("Å"); err != nil {
   604  		t.Fatalf("Unexpected err: %v", err)
   605  	} else if !m {
   606  		t.Fatalf("Expected match")
   607  	}
   608  
   609  	re = MustCompile(`\x{00C6}`, 0)
   610  	if m, err := re.MatchString("Æ"); err != nil {
   611  		t.Fatalf("Unexpected err: %v", err)
   612  	} else if !m {
   613  		t.Fatalf("Expected match")
   614  	}
   615  
   616  	re = MustCompile(`\x{1FF}`, 0)
   617  	if m, err := re.MatchString("ǿ"); err != nil {
   618  		t.Fatalf("Unexpected err: %v", err)
   619  	} else if !m {
   620  		t.Fatalf("Expected match")
   621  	}
   622  
   623  	re = MustCompile(`\x{02FF}`, 0)
   624  	if m, err := re.MatchString("˿"); err != nil {
   625  		t.Fatalf("Unexpected err: %v", err)
   626  	} else if !m {
   627  		t.Fatalf("Expected match")
   628  	}
   629  
   630  	re = MustCompile(`\x{1392}`, 0)
   631  	if m, err := re.MatchString("᎒"); err != nil {
   632  		t.Fatalf("Unexpected err: %v", err)
   633  	} else if !m {
   634  		t.Fatalf("Expected match")
   635  	}
   636  
   637  	re = MustCompile(`\x{0010ffff}`, 0)
   638  	if m, err := re.MatchString(string(rune(0x10ffff))); err != nil {
   639  		t.Fatalf("Unexpected err: %v", err)
   640  	} else if !m {
   641  		t.Fatalf("Expected match")
   642  	}
   643  
   644  	if _, err := Compile(`\x2R`, 0); err == nil {
   645  		t.Fatal("Expected error")
   646  	}
   647  	if _, err := Compile(`\x0`, 0); err == nil {
   648  		t.Fatal("Expected error")
   649  	}
   650  	if _, err := Compile(`\x`, 0); err == nil {
   651  		t.Fatal("Expected error")
   652  	}
   653  	if _, err := Compile(`\x{`, 0); err == nil {
   654  		t.Fatal("Expected error")
   655  	}
   656  	if _, err := Compile(`\x{2`, 0); err == nil {
   657  		t.Fatal("Expected error")
   658  	}
   659  	if _, err := Compile(`\x{2R`, 0); err == nil {
   660  		t.Fatal("Expected error")
   661  	}
   662  	if _, err := Compile(`\x{2R}`, 0); err == nil {
   663  		t.Fatal("Expected error")
   664  	}
   665  	if _, err := Compile(`\x{}`, 0); err == nil {
   666  		t.Fatalf("Expected error")
   667  	}
   668  	if _, err := Compile(`\x{10000`, 0); err == nil {
   669  		t.Fatal("Expected error")
   670  	}
   671  	if _, err := Compile(`\x{1234`, 0); err == nil {
   672  		t.Fatal("Expected error")
   673  	}
   674  	if _, err := Compile(`\x{123456789}`, 0); err == nil {
   675  		t.Fatal("Expected error")
   676  	}
   677  
   678  }
   679  
   680  func TestEmptyCharClass(t *testing.T) {
   681  	if _, err := Compile("[]", 0); err == nil {
   682  		t.Fatal("Empty char class isn't valid outside of ECMAScript mode")
   683  	}
   684  }
   685  
   686  func TestECMAEmptyCharClass(t *testing.T) {
   687  	re := MustCompile("[]", ECMAScript)
   688  	if m, err := re.MatchString("a"); err != nil {
   689  		t.Fatal(err)
   690  	} else if m {
   691  		t.Fatal("Expected no match")
   692  	}
   693  }
   694  
   695  func TestDot(t *testing.T) {
   696  	re := MustCompile(".", 0)
   697  	if m, err := re.MatchString("\r"); err != nil {
   698  		t.Fatal(err)
   699  	} else if !m {
   700  		t.Fatal("Expected match")
   701  	}
   702  }
   703  
   704  func TestECMADot(t *testing.T) {
   705  	re := MustCompile(".", ECMAScript)
   706  	if m, err := re.MatchString("\r"); err != nil {
   707  		t.Fatal(err)
   708  	} else if m {
   709  		t.Fatal("Expected no match")
   710  	}
   711  }
   712  
   713  func TestDecimalLookahead(t *testing.T) {
   714  	re := MustCompile(`\1(A)`, 0)
   715  	m, err := re.FindStringMatch("AA")
   716  	if err != nil {
   717  		t.Fatal(err)
   718  	} else if m != nil {
   719  		t.Fatal("Expected no match")
   720  	}
   721  }
   722  
   723  func TestECMADecimalLookahead(t *testing.T) {
   724  	re := MustCompile(`\1(A)`, ECMAScript)
   725  	m, err := re.FindStringMatch("AA")
   726  	if err != nil {
   727  		t.Fatal(err)
   728  	}
   729  
   730  	if c := m.GroupCount(); c != 2 {
   731  		t.Fatalf("Group count !=2 (%d)", c)
   732  	}
   733  
   734  	if s := m.GroupByNumber(0).String(); s != "A" {
   735  		t.Fatalf("Group0 != 'A' ('%s')", s)
   736  	}
   737  
   738  	if s := m.GroupByNumber(1).String(); s != "A" {
   739  		t.Fatalf("Group1 != 'A' ('%s')", s)
   740  	}
   741  }
   742  
   743  func TestECMAOctal(t *testing.T) {
   744  	re := MustCompile(`\100`, ECMAScript)
   745  	if m, err := re.MatchString("@"); err != nil {
   746  		t.Fatal(err)
   747  	} else if !m {
   748  		t.Fatal("Expected match")
   749  	}
   750  
   751  	if m, err := re.MatchString("x"); err != nil {
   752  		t.Fatal(err)
   753  	} else if m {
   754  		t.Fatal("Expected no match")
   755  	}
   756  
   757  	re = MustCompile(`\377`, ECMAScript)
   758  	if m, err := re.MatchString("\u00ff"); err != nil {
   759  		t.Fatal(err)
   760  	} else if !m {
   761  		t.Fatal("Expected match")
   762  	}
   763  
   764  	re = MustCompile(`\400`, ECMAScript)
   765  	if m, err := re.MatchString(" 0"); err != nil {
   766  		t.Fatal(err)
   767  	} else if !m {
   768  		t.Fatal("Expected match")
   769  	}
   770  
   771  }
   772  
   773  func TestECMAInvalidEscape(t *testing.T) {
   774  	re := MustCompile(`\x0`, ECMAScript)
   775  	if m, err := re.MatchString("x0"); err != nil {
   776  		t.Fatal(err)
   777  	} else if !m {
   778  		t.Fatal("Expected match")
   779  	}
   780  
   781  	re = MustCompile(`\x0z`, ECMAScript)
   782  	if m, err := re.MatchString("x0z"); err != nil {
   783  		t.Fatal(err)
   784  	} else if !m {
   785  		t.Fatal("Expected match")
   786  	}
   787  }
   788  
   789  func TestECMANamedGroup(t *testing.T) {
   790  	re := MustCompile(`\k`, ECMAScript)
   791  	if m, err := re.MatchString("k"); err != nil {
   792  		t.Fatal(err)
   793  	} else if !m {
   794  		t.Fatal("Expected match")
   795  	}
   796  
   797  	re = MustCompile(`\k'test'`, ECMAScript)
   798  	if m, err := re.MatchString(`k'test'`); err != nil {
   799  		t.Fatal(err)
   800  	} else if !m {
   801  		t.Fatal("Expected match")
   802  	}
   803  
   804  	re = MustCompile(`\k<test>`, ECMAScript)
   805  	if m, err := re.MatchString(`k<test>`); err != nil {
   806  		t.Fatal(err)
   807  	} else if !m {
   808  		t.Fatal("Expected match")
   809  	}
   810  
   811  	_, err := Compile(`(?<title>\w+), yes \k'title'`, ECMAScript)
   812  	if err == nil {
   813  		t.Fatal("Expected error")
   814  	}
   815  
   816  	re = MustCompile(`(?<title>\w+), yes \k<title>`, ECMAScript)
   817  	if m, err := re.MatchString("sir, yes sir"); err != nil {
   818  		t.Fatal(err)
   819  	} else if !m {
   820  		t.Fatal("Expected match")
   821  	}
   822  
   823  	re = MustCompile(`\k<title>, yes (?<title>\w+)`, ECMAScript)
   824  	if m, err := re.MatchString(", yes sir"); err != nil {
   825  		t.Fatal(err)
   826  	} else if !m {
   827  		t.Fatal("Expected match")
   828  	}
   829  
   830  	_, err = Compile(`\k<(?<name>)>`, ECMAScript)
   831  	if err == nil {
   832  		t.Fatal("Expected error")
   833  	}
   834  
   835  	MustCompile(`\k<(<name>)>`, ECMAScript)
   836  
   837  	_, err = Compile(`\k<(<name>)>`, 0)
   838  	if err == nil {
   839  		t.Fatal("Expected error")
   840  	}
   841  
   842  	re = MustCompile(`\'|\<?`, 0)
   843  	if m, err := re.MatchString("'"); err != nil {
   844  		t.Fatal(err)
   845  	} else if !m {
   846  		t.Fatal("Expected match")
   847  	}
   848  	if m, err := re.MatchString("<"); err != nil {
   849  		t.Fatal(err)
   850  	} else if !m {
   851  		t.Fatal("Expected match")
   852  	}
   853  }
   854  
   855  func TestECMAInvalidEscapeCharClass(t *testing.T) {
   856  	re := MustCompile(`[\x0]`, ECMAScript)
   857  	if m, err := re.MatchString("x"); err != nil {
   858  		t.Fatal(err)
   859  	} else if !m {
   860  		t.Fatal("Expected match")
   861  	}
   862  
   863  	if m, err := re.MatchString("0"); err != nil {
   864  		t.Fatal(err)
   865  	} else if !m {
   866  		t.Fatal("Expected match")
   867  	}
   868  
   869  	if m, err := re.MatchString("z"); err != nil {
   870  		t.Fatal(err)
   871  	} else if m {
   872  		t.Fatal("Expected no match")
   873  	}
   874  }
   875  
   876  func TestECMAScriptXCurlyBraceEscape(t *testing.T) {
   877  	re := MustCompile(`\x{20}`, ECMAScript)
   878  	if m, err := re.MatchString(" "); err != nil {
   879  		t.Fatal(err)
   880  	} else if m {
   881  		t.Fatal("Expected no match")
   882  	}
   883  
   884  	if m, err := re.MatchString("xxxxxxxxxxxxxxxxxxxx"); err != nil {
   885  		t.Fatal(err)
   886  	} else if !m {
   887  		t.Fatal("Expected match")
   888  	}
   889  }
   890  
   891  func TestEcmaScriptUnicodeRange(t *testing.T) {
   892  	r, err := Compile(`([\u{001a}-\u{ffff}]+)`, ECMAScript|Unicode)
   893  	if err != nil {
   894  		panic(err)
   895  	}
   896  	m, err := r.FindStringMatch("qqqq")
   897  	if err != nil {
   898  		panic(err)
   899  	}
   900  	if m == nil {
   901  		t.Fatal("Expected non-nil, got nil")
   902  	}
   903  }
   904  
   905  func TestNegateRange(t *testing.T) {
   906  	re := MustCompile(`[\D]`, 0)
   907  	if m, err := re.MatchString("A"); err != nil {
   908  		t.Fatal(err)
   909  	} else if !m {
   910  		t.Fatal("Expected match")
   911  	}
   912  }
   913  
   914  func TestECMANegateRange(t *testing.T) {
   915  	re := MustCompile(`[\D]`, ECMAScript)
   916  	if m, err := re.MatchString("A"); err != nil {
   917  		t.Fatal(err)
   918  	} else if !m {
   919  		t.Fatal("Expected match")
   920  	}
   921  }
   922  
   923  func TestDollar(t *testing.T) {
   924  	// PCRE/C# allow \n to match to $ at end-of-string in singleline mode...
   925  	// a weird edge-case kept for compatibility, ECMAScript/RE2 mode don't allow it
   926  	re := MustCompile(`ac$`, 0)
   927  	if m, err := re.MatchString("ac\n"); err != nil {
   928  		t.Fatal(err)
   929  	} else if !m {
   930  		t.Fatal("Expected match")
   931  	}
   932  }
   933  func TestECMADollar(t *testing.T) {
   934  	re := MustCompile(`ac$`, ECMAScript)
   935  	if m, err := re.MatchString("ac\n"); err != nil {
   936  		t.Fatal(err)
   937  	} else if m {
   938  		t.Fatal("Expected no match")
   939  	}
   940  }
   941  
   942  func TestThreeByteUnicode_InputOnly(t *testing.T) {
   943  	// confirm the bmprefix properly ignores 3-byte unicode in the input value
   944  	// this used to panic
   945  	re := MustCompile("高", 0)
   946  	if m, err := re.MatchString("📍Test高"); err != nil {
   947  		t.Fatal(err)
   948  	} else if !m {
   949  		t.Fatal("Expected match")
   950  	}
   951  }
   952  
   953  func TestMultibyteUnicode_MatchPartialPattern(t *testing.T) {
   954  	re := MustCompile("猟な", 0)
   955  	if m, err := re.MatchString("なあ🍺な"); err != nil {
   956  		t.Fatal(err)
   957  	} else if m {
   958  		t.Fatal("Expected no match")
   959  	}
   960  }
   961  
   962  func TestMultibyteUnicode_Match(t *testing.T) {
   963  	re := MustCompile("猟な", 0)
   964  	if m, err := re.MatchString("なあ🍺猟な"); err != nil {
   965  		t.Fatal(err)
   966  	} else if !m {
   967  		t.Fatal("Expected match")
   968  	}
   969  }
   970  
   971  func TestAlternationNamedOptions_Errors(t *testing.T) {
   972  	// all of these should give an error "error parsing regexp:"
   973  	data := []string{
   974  		"(?(?e))", "(?(?a)", "(?(?", "(?(", "?(a:b)", "?(a)", "?(a|b)", "?((a)", "?((a)a", "?((a)a|", "?((a)a|b",
   975  		"(?(?i))", "(?(?I))", "(?(?m))", "(?(?M))", "(?(?s))", "(?(?S))", "(?(?x))", "(?(?X))", "(?(?n))", "(?(?N))", " (?(?n))",
   976  	}
   977  	for _, p := range data {
   978  		re, err := Compile(p, 0)
   979  		if err == nil {
   980  			t.Fatal("Expected error, got nil")
   981  		}
   982  		if re != nil {
   983  			t.Fatal("Expected unparsed regexp, got non-nil")
   984  		}
   985  
   986  		if !strings.HasPrefix(err.Error(), "error parsing regexp: ") {
   987  			t.Fatalf("Wanted parse error, got '%v'", err)
   988  		}
   989  	}
   990  }
   991  
   992  func TestAlternationNamedOptions_Success(t *testing.T) {
   993  	data := []struct {
   994  		pattern       string
   995  		input         string
   996  		expectSuccess bool
   997  		matchVal      string
   998  	}{
   999  		{"(?(cat)|dog)", "cat", true, ""},
  1000  		{"(?(cat)|dog)", "catdog", true, ""},
  1001  		{"(?(cat)dog1|dog2)", "catdog1", false, ""},
  1002  		{"(?(cat)dog1|dog2)", "catdog2", true, "dog2"},
  1003  		{"(?(cat)dog1|dog2)", "catdog1dog2", true, "dog2"},
  1004  		{"(?(dog2))", "dog2", true, ""},
  1005  		{"(?(cat)|dog)", "oof", false, ""},
  1006  		{"(?(a:b))", "a", true, ""},
  1007  		{"(?(a:))", "a", true, ""},
  1008  	}
  1009  	for _, p := range data {
  1010  		re := MustCompile(p.pattern, 0)
  1011  		m, err := re.FindStringMatch(p.input)
  1012  
  1013  		if err != nil {
  1014  			t.Fatalf("Unexpected error during match: %v", err)
  1015  		}
  1016  		if want, got := p.expectSuccess, m != nil; want != got {
  1017  			t.Fatalf("Success mismatch for %v, wanted %v, got %v", p.pattern, want, got)
  1018  		}
  1019  		if m != nil {
  1020  			if want, got := p.matchVal, m.String(); want != got {
  1021  				t.Fatalf("Match val mismatch for %v, wanted %v, got %v", p.pattern, want, got)
  1022  			}
  1023  		}
  1024  	}
  1025  }
  1026  
  1027  func TestAlternationConstruct_Matches(t *testing.T) {
  1028  	re := MustCompile("(?(A)A123|C789)", 0)
  1029  	m, err := re.FindStringMatch("A123 B456 C789")
  1030  	if err != nil {
  1031  		t.Fatalf("Unexpected err: %v", err)
  1032  	}
  1033  	if m == nil {
  1034  		t.Fatal("Expected match, got nil")
  1035  	}
  1036  
  1037  	if want, got := "A123", m.String(); want != got {
  1038  		t.Fatalf("Wanted %v, got %v", want, got)
  1039  	}
  1040  
  1041  	m, err = re.FindNextMatch(m)
  1042  	if err != nil {
  1043  		t.Fatalf("Unexpected err in second match: %v", err)
  1044  	}
  1045  	if m == nil {
  1046  		t.Fatal("Expected second match, got nil")
  1047  	}
  1048  	if want, got := "C789", m.String(); want != got {
  1049  		t.Fatalf("Wanted %v, got %v", want, got)
  1050  	}
  1051  
  1052  	m, err = re.FindNextMatch(m)
  1053  	if err != nil {
  1054  		t.Fatalf("Unexpected err in third match: %v", err)
  1055  	}
  1056  	if m != nil {
  1057  		t.Fatal("Did not expect third match")
  1058  	}
  1059  }
  1060  
  1061  func TestStartAtEnd(t *testing.T) {
  1062  	re := MustCompile("(?:)", 0)
  1063  	m, err := re.FindStringMatchStartingAt("t", 1)
  1064  	if err != nil {
  1065  		t.Fatal(err)
  1066  	}
  1067  	if m == nil {
  1068  		t.Fatal("Expected match")
  1069  	}
  1070  }
  1071  
  1072  func TestParserFuzzCrashes(t *testing.T) {
  1073  	var crashes = []string{
  1074  		"(?'-", "(\\c0)", "(\\00(?())", "[\\p{0}", "(\x00?.*.()?(()?)?)*.x\xcb?&(\\s\x80)", "\\p{0}", "[0-[\\p{0}",
  1075  	}
  1076  
  1077  	for _, c := range crashes {
  1078  		t.Log(c)
  1079  		Compile(c, 0)
  1080  	}
  1081  }
  1082  
  1083  func TestParserFuzzHangs(t *testing.T) {
  1084  	var hangs = []string{
  1085  		"\r{865720113}z\xd5{\r{861o", "\r{915355}\r{9153}", "\r{525005}", "\x01{19765625}", "(\r{068828256})", "\r{677525005}",
  1086  	}
  1087  
  1088  	for _, c := range hangs {
  1089  		t.Log(c)
  1090  		Compile(c, 0)
  1091  	}
  1092  }
  1093  
  1094  func BenchmarkParserPrefixLongLen(b *testing.B) {
  1095  	re := MustCompile("\r{100001}T+", 0)
  1096  	inp := strings.Repeat("testing", 10000) + strings.Repeat("\r", 100000) + "TTTT"
  1097  
  1098  	b.ResetTimer()
  1099  	for i := 0; i < b.N; i++ {
  1100  		if m, err := re.MatchString(inp); err != nil {
  1101  			b.Fatalf("Unexpected err: %v", err)
  1102  		} else if m {
  1103  			b.Fatalf("Expected no match")
  1104  		}
  1105  	}
  1106  }
  1107  
  1108  /*
  1109  func TestPcreStuff(t *testing.T) {
  1110  	re := MustCompile(`(?(?=(a))a)`, Debug)
  1111  	inp := unEscapeToMatch(`a`)
  1112  	fmt.Printf("Inp %q\n", inp)
  1113  	m, err := re.FindStringMatch(inp)
  1114  
  1115  	if err != nil {
  1116  		t.Fatalf("Unexpected error: %v", err)
  1117  	}
  1118  	if m == nil {
  1119  		t.Fatalf("Expected match")
  1120  	}
  1121  
  1122  	fmt.Printf("Match %s\n", m.dump())
  1123  	fmt.Printf("Text: %v\n", unEscapeGroup(m.String()))
  1124  
  1125  }
  1126  */
  1127  
  1128  //(.*)(\d+) different FirstChars ([\x00-\t\v-\x08] OR [\x00-\t\v-\uffff\p{Nd}]
  1129  
  1130  func TestControlBracketFail(t *testing.T) {
  1131  	re := MustCompile(`(cat)(\c[*)(dog)`, 0)
  1132  	inp := "asdlkcat\u00FFdogiwod"
  1133  
  1134  	if m, _ := re.MatchString(inp); m {
  1135  		t.Fatal("expected no match")
  1136  	}
  1137  }
  1138  
  1139  func TestControlBracketGroups(t *testing.T) {
  1140  	re := MustCompile(`(cat)(\c[*)(dog)`, 0)
  1141  	inp := "asdlkcat\u001bdogiwod"
  1142  
  1143  	if want, got := 4, re.capsize; want != got {
  1144  		t.Fatalf("Capsize wrong, want %v, got %v", want, got)
  1145  	}
  1146  
  1147  	m, _ := re.FindStringMatch(inp)
  1148  	if m == nil {
  1149  		t.Fatal("expected match")
  1150  	}
  1151  
  1152  	g := m.Groups()
  1153  	want := []string{"cat\u001bdog", "cat", "\u001b", "dog"}
  1154  	for i := 0; i < len(g); i++ {
  1155  		if want[i] != g[i].String() {
  1156  			t.Fatalf("Bad group num %v, want %v, got %v", i, want[i], g[i].String())
  1157  		}
  1158  	}
  1159  }
  1160  
  1161  func TestBadGroupConstruct(t *testing.T) {
  1162  	bad := []string{"(?>-", "(?<", "(?<=", "(?<!", "(?>", "(?)", "(?<)", "(?')", "(?<-"}
  1163  
  1164  	for _, b := range bad {
  1165  		_, err := Compile(b, 0)
  1166  		if err == nil {
  1167  			t.Fatalf("Wanted error, but got no error for pattern: %v", b)
  1168  		}
  1169  	}
  1170  }
  1171  
  1172  func TestEmptyCaptureLargeRepeat(t *testing.T) {
  1173  	// a bug would cause our track to not grow and eventually panic
  1174  	// with large numbers of repeats of a non-capturing group (>16)
  1175  
  1176  	// the issue was that the jump occured to the same statement over and over
  1177  	// and the "grow stack/track" logic only triggered on jumps that moved
  1178  	// backwards
  1179  
  1180  	r := MustCompile(`(?:){40}`, 0)
  1181  	m, err := r.FindStringMatch("1")
  1182  	if err != nil {
  1183  		t.Fatalf("Unexpected error: %v", err)
  1184  	}
  1185  	if want, got := 0, m.Index; want != got {
  1186  		t.Errorf("First Match Index wanted %v got %v", want, got)
  1187  	}
  1188  	if want, got := 0, m.Length; want != got {
  1189  		t.Errorf("First Match Length wanted %v got %v", want, got)
  1190  	}
  1191  
  1192  	m, _ = r.FindNextMatch(m)
  1193  	if want, got := 1, m.Index; want != got {
  1194  		t.Errorf("Second Match Index wanted %v got %v", want, got)
  1195  	}
  1196  	if want, got := 0, m.Length; want != got {
  1197  		t.Errorf("Second Match Length wanted %v got %v", want, got)
  1198  	}
  1199  
  1200  	m, _ = r.FindNextMatch(m)
  1201  	if m != nil {
  1202  		t.Fatal("Expected 2 matches, got more")
  1203  	}
  1204  }
  1205  
  1206  func TestFuzzBytes_NoCompile(t *testing.T) {
  1207  	//some crash cases found from fuzzing
  1208  
  1209  	var testCases = []struct {
  1210  		r []byte
  1211  	}{
  1212  		{
  1213  			r: []byte{0x28, 0x28, 0x29, 0x5c, 0x37, 0x28, 0x3f, 0x28, 0x29, 0x29},
  1214  		},
  1215  		{
  1216  			r: []byte{0x28, 0x5c, 0x32, 0x28, 0x3f, 0x28, 0x30, 0x29, 0x29},
  1217  		},
  1218  		{
  1219  			r: []byte{0x28, 0x3f, 0x28, 0x29, 0x29, 0x5c, 0x31, 0x30, 0x28, 0x3f, 0x28, 0x30, 0x29},
  1220  		},
  1221  		{
  1222  			r: []byte{0x28, 0x29, 0x28, 0x28, 0x29, 0x5c, 0x37, 0x28, 0x3f, 0x28, 0x29, 0x29},
  1223  		},
  1224  	}
  1225  
  1226  	for _, c := range testCases {
  1227  		r := string(c.r)
  1228  		t.Run(r, func(t *testing.T) {
  1229  			_, err := Compile(r, Multiline|ECMAScript|Debug)
  1230  			// should fail compiling
  1231  			if err == nil {
  1232  				t.Fatal("should fail compile, but didn't")
  1233  			}
  1234  		})
  1235  	}
  1236  
  1237  }
  1238  
  1239  func TestFuzzBytes_Match(t *testing.T) {
  1240  
  1241  	var testCases = []struct {
  1242  		r, s []byte
  1243  	}{
  1244  		{
  1245  			r: []byte{0x30, 0xbf, 0x30, 0x2a, 0x30, 0x30},
  1246  			s: []byte{0xf0, 0xb0, 0x80, 0x91, 0xf7},
  1247  		},
  1248  		{
  1249  			r: []byte{0x30, 0xaf, 0xf3, 0x30, 0x2a},
  1250  			s: []byte{0xf3, 0x80, 0x80, 0x87, 0x80, 0x89},
  1251  		},
  1252  	}
  1253  
  1254  	for _, c := range testCases {
  1255  		r := string(c.r)
  1256  		t.Run(r, func(t *testing.T) {
  1257  			re, err := Compile(r, 0)
  1258  
  1259  			if err != nil {
  1260  				t.Fatal("should compile, but didn't")
  1261  			}
  1262  
  1263  			re.MatchString(string(c.s))
  1264  		})
  1265  	}
  1266  }
  1267  
  1268  func TestConcatAccidentalPatternCharge(t *testing.T) {
  1269  	// originally this pattern would parse incorrectly
  1270  	// specifically the closing group would concat the string literals
  1271  	// together but the raw rune slice would blow over the original pattern
  1272  	// so the final bit of pattern parsing would be wrong
  1273  	// fixed in #49
  1274  	r, err := Compile(`(?<=1234\.\*56).*(?=890)`, 0)
  1275  
  1276  	if err != nil {
  1277  		panic(err)
  1278  	}
  1279  
  1280  	m, err := r.FindStringMatch(`1234.*567890`)
  1281  	if err != nil {
  1282  		panic(err)
  1283  	}
  1284  	if m == nil {
  1285  		t.Fatal("Expected non-nil, got nil")
  1286  	}
  1287  }
  1288  
  1289  func TestGoodReverseOrderMessage(t *testing.T) {
  1290  	_, err := Compile(`[h-c]`, ECMAScript)
  1291  	if err == nil {
  1292  		t.Fatal("expected error")
  1293  	}
  1294  	expected := "error parsing regexp: [h-c] range in reverse order in `[h-c]`"
  1295  	if err.Error() != expected {
  1296  		t.Fatalf("expected %q got %q", expected, err.Error())
  1297  	}
  1298  }
  1299  
  1300  func TestParseShortSlashP(t *testing.T) {
  1301  	re := MustCompile(`[!\pL\pN]{1,}`, 0)
  1302  	m, err := re.FindStringMatch("this23! is a! test 1a 2b")
  1303  	if err != nil {
  1304  		t.Fatalf("Unexpected error: %v", err)
  1305  	}
  1306  	if m.String() != "this23!" {
  1307  		t.Fatalf("Expected match")
  1308  	}
  1309  }
  1310  
  1311  func TestParseShortSlashNegateP(t *testing.T) {
  1312  	re := MustCompile(`\PNa`, 0)
  1313  	m, err := re.FindStringMatch("this is a test 1a 2b")
  1314  	if err != nil {
  1315  		t.Fatalf("Unexpected error: %v", err)
  1316  	}
  1317  	if m.String() != " a" {
  1318  		t.Fatalf("Expected match")
  1319  	}
  1320  }
  1321  
  1322  func TestParseShortSlashPEnd(t *testing.T) {
  1323  	re := MustCompile(`\pN`, 0)
  1324  	m, err := re.FindStringMatch("this is a test 1a 2b")
  1325  	if err != nil {
  1326  		t.Fatalf("Unexpected error: %v", err)
  1327  	}
  1328  	if m.String() != "1" {
  1329  		t.Fatalf("Expected match")
  1330  	}
  1331  }
  1332  

View as plain text