package sheet import ( "regexp" "strings" "testing" "time" ) // FuzzSearchRegex tests the regex compilation and search functionality // to ensure it handles malformed patterns gracefully and doesn't suffer // from catastrophic backtracking func FuzzSearchRegex(f *testing.F) { // Add seed corpus with various regex patterns // Valid patterns f.Add("test", "This is a test string") f.Add("(?i)test", "This is a TEST string") f.Add("foo|bar", "foo and bar") f.Add("^start", "start of line\nnext line") f.Add("end$", "at the end\nnext line") f.Add("\\d+", "123 numbers 456") f.Add("[a-z]+", "lowercase UPPERCASE") // Edge cases and potentially problematic patterns f.Add("", "empty pattern") f.Add(".", "any character") f.Add(".*", "match everything") f.Add(".+", "match something") f.Add("\\", "backslash") f.Add("(", "unclosed paren") f.Add(")", "unmatched paren") f.Add("[", "unclosed bracket") f.Add("]", "unmatched bracket") f.Add("[^]", "negated empty class") f.Add("(?", "incomplete group") // Patterns that might cause performance issues f.Add("(a+)+", "aaaaaaaaaaaaaaaaaaaaaaaab") f.Add("(a*)*", "aaaaaaaaaaaaaaaaaaaaaaaab") f.Add("(a|a)*", "aaaaaaaaaaaaaaaaaaaaaaaab") f.Add("(.*)*", "any text here") f.Add("(\\d+)+", "123456789012345678901234567890x") // Unicode patterns f.Add("☺", "Unicode ☺ smiley") f.Add("[一-龯]", "Chinese 中文 characters") f.Add("\\p{L}+", "Unicode letters") // Very long patterns f.Add(strings.Repeat("a", 1000), "long pattern") f.Add(strings.Repeat("(a|b)", 100), "complex pattern") f.Fuzz(func(t *testing.T, pattern string, text string) { // Test 1: Regex compilation should not panic var reg *regexp.Regexp var compileErr error func() { defer func() { if r := recover(); r != nil { t.Errorf("regexp.Compile panicked with pattern %q: %v", pattern, r) } }() reg, compileErr = regexp.Compile(pattern) }() // If compilation failed, that's OK - we're testing error handling if compileErr != nil { // This is expected for invalid patterns return } // Test 2: Create a sheet and test Search method sheet := Sheet{ Title: "test", Text: text, } // Search should not panic var result string done := make(chan bool, 1) go func() { defer func() { if r := recover(); r != nil { t.Errorf("Search panicked with pattern %q on text %q: %v", pattern, text, r) } done <- true }() result = sheet.Search(reg) }() // Timeout after 100ms to catch catastrophic backtracking select { case <-done: // Search completed successfully case <-time.After(100 * time.Millisecond): t.Errorf("Search timed out (possible catastrophic backtracking) with pattern %q on text %q", pattern, text) } // Test 3: Verify search result invariants if result != "" { // The Search function splits by "\n\n", so we need to compare using the same logic resultLines := strings.Split(result, "\n\n") textLines := strings.Split(text, "\n\n") // Every result line should exist in the original text lines for _, rLine := range resultLines { found := false for _, tLine := range textLines { if rLine == tLine { found = true break } } if !found && rLine != "" { t.Errorf("Search result contains line not in original text: %q", rLine) } } } }) }