github.com/anchore/syft@v1.38.2/internal/regex_helpers_test.go (about) 1 package internal 2 3 import ( 4 "regexp" 5 "strings" 6 "testing" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 ) 11 12 func TestMatchCaptureGroups(t *testing.T) { 13 tests := []struct { 14 name string 15 input string 16 pattern string 17 expected map[string]string 18 }{ 19 { 20 name: "go-case", 21 input: "match this thing", 22 pattern: `(?P<name>match).*(?P<version>thing)`, 23 expected: map[string]string{ 24 "name": "match", 25 "version": "thing", 26 }, 27 }, 28 { 29 name: "only matches the first instance", 30 input: "match this thing batch another think", 31 pattern: `(?P<name>[mb]atch).*?(?P<version>thin[gk])`, 32 expected: map[string]string{ 33 "name": "match", 34 "version": "thing", 35 }, 36 }, 37 { 38 name: "nested capture groups", 39 input: "cool something to match against", 40 pattern: `((?P<name>match) (?P<version>against))`, 41 expected: map[string]string{ 42 "name": "match", 43 "version": "against", 44 }, 45 }, 46 { 47 name: "nested optional capture groups", 48 input: "cool something to match against", 49 pattern: `((?P<name>match) (?P<version>against))?`, 50 expected: map[string]string{ 51 "name": "match", 52 "version": "against", 53 }, 54 }, 55 { 56 name: "nested optional capture groups with larger match", 57 input: "cool something to match against match never", 58 pattern: `.*?((?P<name>match) (?P<version>(against|never)))?`, 59 expected: map[string]string{ 60 "name": "match", 61 "version": "against", 62 }, 63 }, 64 } 65 66 for _, test := range tests { 67 t.Run(test.name, func(t *testing.T) { 68 actual := MatchNamedCaptureGroups(regexp.MustCompile(test.pattern), test.input) 69 assert.Equal(t, test.expected, actual) 70 }) 71 } 72 } 73 74 func TestMatchNamedCaptureGroupsFromReader(t *testing.T) { 75 tests := []struct { 76 name string 77 pattern string 78 input string 79 want map[string]string 80 wantErr require.ErrorAssertionFunc 81 }{ 82 { 83 name: "match single group", 84 pattern: `(?P<key>[^1-9]+)`, 85 input: "key", 86 want: map[string]string{"key": "key"}, 87 wantErr: require.NoError, 88 }, 89 { 90 name: "match multiple groups", 91 pattern: `(?P<key>[^1-9]+):(?P<value>\w+)`, 92 input: "key:value", 93 want: map[string]string{"key": "key", "value": "value"}, 94 wantErr: require.NoError, 95 }, 96 { 97 name: "no match", 98 pattern: `(?P<key>[^1-9]+)`, 99 input: "2345", 100 want: nil, 101 wantErr: require.NoError, 102 }, 103 { 104 name: "error empty reader", 105 pattern: `(?P<key>\w+)`, 106 input: "", 107 want: nil, 108 wantErr: require.NoError, 109 }, 110 } 111 for _, tt := range tests { 112 t.Run(tt.name, func(t *testing.T) { 113 re := regexp.MustCompile(tt.pattern) 114 r := strings.NewReader(tt.input) 115 got, err := MatchNamedCaptureGroupsFromReader(re, r) 116 tt.wantErr(t, err) 117 assert.Equal(t, tt.want, got) 118 }) 119 } 120 } 121 122 func TestMatchAnyFromReader(t *testing.T) { 123 tests := []struct { 124 name string 125 input string 126 patterns []*regexp.Regexp 127 want bool 128 wantErr require.ErrorAssertionFunc 129 }{ 130 { 131 name: "match single pattern", 132 input: "hello world", 133 patterns: []*regexp.Regexp{regexp.MustCompile(`hello`)}, 134 want: true, 135 wantErr: require.NoError, 136 }, 137 { 138 name: "match multiple patterns", 139 input: "test case", 140 patterns: []*regexp.Regexp{regexp.MustCompile(`case`), regexp.MustCompile(`test`)}, 141 want: true, 142 wantErr: require.NoError, 143 }, 144 { 145 name: "no match", 146 input: "nothing here", 147 patterns: []*regexp.Regexp{regexp.MustCompile(`absent`)}, 148 want: false, 149 wantErr: require.NoError, 150 }, 151 { 152 name: "error empty reader", 153 input: "", 154 patterns: []*regexp.Regexp{regexp.MustCompile(`match`)}, 155 want: false, 156 wantErr: require.NoError, 157 }, 158 } 159 for _, tt := range tests { 160 t.Run(tt.name, func(t *testing.T) { 161 r := strings.NewReader(tt.input) 162 got, err := MatchAnyFromReader(r, tt.patterns...) 163 tt.wantErr(t, err) 164 assert.Equal(t, tt.want, got) 165 }) 166 } 167 } 168 169 func TestProcessReaderInChunks_ChunkBoundaries(t *testing.T) { 170 tests := []struct { 171 name string 172 input string 173 chunkSize int 174 expectedCalls []string 175 returnOnChunk int 176 wantErr require.ErrorAssertionFunc 177 }{ 178 { 179 name: "go case", 180 input: "123456789012345", 181 chunkSize: 4, 182 returnOnChunk: 2, 183 expectedCalls: []string{"1234", "345678", "789012"}, 184 wantErr: require.NoError, 185 }, 186 { 187 name: "no match", 188 input: "123456789012345", 189 chunkSize: 4, 190 returnOnChunk: -1, 191 expectedCalls: []string{"1234", "345678", "789012", "12345"}, 192 wantErr: require.NoError, 193 }, 194 } 195 for _, tt := range tests { 196 t.Run(tt.name, func(t *testing.T) { 197 var actualCalls []string 198 var current int 199 handler := func(data []byte) (bool, error) { 200 actualCalls = append(actualCalls, string(data)) 201 if current == tt.returnOnChunk { 202 return true, nil 203 } 204 current++ 205 return false, nil 206 } 207 r := strings.NewReader(tt.input) 208 got, err := processReaderInChunks(r, tt.chunkSize, handler) 209 tt.wantErr(t, err) 210 if tt.returnOnChunk == -1 { 211 assert.False(t, got) 212 } else { 213 assert.True(t, got) 214 } 215 assert.Equal(t, tt.expectedCalls, actualCalls) 216 }) 217 } 218 }