(t *testing.T)
| 275 | } |
| 276 | |
| 277 | func TestRegexCosts(t *testing.T) { |
| 278 | tests := []struct { |
| 279 | expr string |
| 280 | vars []cel.EnvOption |
| 281 | in map[string]any |
| 282 | hints map[string]uint64 |
| 283 | estimatedCost checker.CostEstimate |
| 284 | actualCost uint64 |
| 285 | }{ |
| 286 | { |
| 287 | expr: `regex.extract('hello world', 'hello (.*)') == optional.of('world')`, |
| 288 | estimatedCost: checker.CostEstimate{Min: 8, Max: 20}, |
| 289 | actualCost: 8, |
| 290 | }, |
| 291 | // - Estimated Cost (Min: 5): Derived from fixed costs of scanning 10-char |
| 292 | // target string and compiling 2-char regex. Since the inputs are constant, |
| 293 | // the Min estimate is very accurate. |
| 294 | // - Actual Cost (5): Observed cost is the sum of a base call cost (~1), the |
| 295 | // search cost, and the allocation cost for the 2-char result string '22'. |
| 296 | // It aligns perfectly with the minimum estimate. |
| 297 | { |
| 298 | expr: "regex.extract('4122345432', '22').orValue('777') == '22'", |
| 299 | estimatedCost: checker.CostEstimate{Min: 4, Max: 14}, |
| 300 | actualCost: 4, |
| 301 | }, |
| 302 | // .or() condition introduces runtime uncertainty, and since the cost estimator |
| 303 | // can't know which branch the code will take, it must prepare for the most |
| 304 | // expensive possible outcome resulting in an estimate.Max of ~ math.MaxUint64. |
| 305 | { |
| 306 | expr: "regex.extract('4122345432', '22').or(optional.of('777')) == optional.of('22')", |
| 307 | estimatedCost: checker.CostEstimate{Min: 6, Max: 1844674407370955279}, |
| 308 | actualCost: 5, |
| 309 | }, |
| 310 | { |
| 311 | expr: "regex.extract('hello world', 'goodbye (.*)') == optional.none()", |
| 312 | estimatedCost: checker.CostEstimate{Min: 10, Max: 22}, |
| 313 | actualCost: 8, |
| 314 | }, |
| 315 | { |
| 316 | expr: "regex.extractAll('id:123, id:456', 'assa') == []", |
| 317 | estimatedCost: checker.CostEstimate{Min: 24, Max: 38}, |
| 318 | actualCost: 23, |
| 319 | }, |
| 320 | // - Estimated Cost (Min: 25): Cost to scan the 14-char target and compile |
| 321 | // 5-char regex, plus a worst-case allocation cost for the result list's |
| 322 | // contents, which is estimated as the full 14-char size of the target. |
| 323 | // - Actual Cost (28): Observed cost includes the search cost plus the actual |
| 324 | // allocation cost, which is the base list creation cost plus the cost of |
| 325 | // allocating the two result strings, totaling 12 chars of content. |
| 326 | { |
| 327 | expr: `regex.extractAll('id:123, id:456', r'id:\d+') == ['id:123', 'id:456']`, |
| 328 | estimatedCost: checker.CostEstimate{Min: 25, Max: 39}, |
| 329 | actualCost: 27, |
| 330 | }, |
| 331 | { |
| 332 | expr: `regex.extractAll('a b c', r'(\S*)\s*') == ['a', 'b', 'c']`, |
| 333 | estimatedCost: checker.CostEstimate{Min: 24, Max: 29}, |
| 334 | actualCost: 27, |
nothing calls this directly
no test coverage detected