summaryrefslogtreecommitdiff
path: root/internal/ui/helpers_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-08 21:59:10 +0300
committerPaul Buetow <paul@buetow.org>2026-04-08 21:59:10 +0300
commit103f369cee209f6f0a15ef953cff138ffa025a26 (patch)
treeba9ee209acab34726fc1d43753aee18bc98a46d0 /internal/ui/helpers_test.go
parentbcfeb2deb2fc089c81971744774095409ad12a43 (diff)
task b: protect regex cache with RWMutex
Diffstat (limited to 'internal/ui/helpers_test.go')
-rw-r--r--internal/ui/helpers_test.go49
1 files changed, 49 insertions, 0 deletions
diff --git a/internal/ui/helpers_test.go b/internal/ui/helpers_test.go
index 9760277..1fe853b 100644
--- a/internal/ui/helpers_test.go
+++ b/internal/ui/helpers_test.go
@@ -3,7 +3,9 @@ package ui
import (
"fmt"
"reflect"
+ "regexp"
"strings"
+ "sync"
"testing"
"time"
@@ -98,6 +100,52 @@ func TestFormatDueText(t *testing.T) {
}
}
+func TestSearchRegexCacheConcurrentAccess(t *testing.T) {
+ searchRegexMu.Lock()
+ searchRegexCache = make(map[string]*regexp.Regexp)
+ searchRegexMu.Unlock()
+ t.Cleanup(func() {
+ searchRegexMu.Lock()
+ searchRegexCache = make(map[string]*regexp.Regexp)
+ searchRegexMu.Unlock()
+ })
+
+ patterns := []string{`alpha`, `beta`}
+ var wg sync.WaitGroup
+ errCh := make(chan error, 128)
+
+ for i := 0; i < 16; i++ {
+ for _, pattern := range patterns {
+ wg.Add(1)
+ go func(pattern string) {
+ defer wg.Done()
+
+ re, err := compileAndCacheRegex(pattern)
+ if err != nil {
+ errCh <- err
+ return
+ }
+ if re == nil || !re.MatchString(pattern) {
+ errCh <- fmt.Errorf("compiled regex for %q did not match", pattern)
+ return
+ }
+ if cached, ok := cachedSearchRegex(pattern); !ok || cached == nil {
+ errCh <- fmt.Errorf("missing cached regex for %q", pattern)
+ }
+ }(pattern)
+ }
+ }
+
+ wg.Wait()
+ close(errCh)
+
+ for err := range errCh {
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
func TestValidateTagName(t *testing.T) {
tests := []struct {
name string
@@ -414,6 +462,7 @@ func TestValidateRecurrence(t *testing.T) {
})
}
}
+
// TestParseFilterInput verifies that parseFilterInput correctly handles
// taskwarrior filter expressions, including attribute filters (proj:xxx),
// tag filters (+tag), quoted values (description:"some text"), and empty input.