blob: 1d03fc961f62e9a8fc7ce182bddf67d94e3979b9 [file]
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package bench defines benchmark test cases and utilities for CEL parsers.
package bench
import (
"strings"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/parser"
)
// ParseResult indicates whether a parse is expected to succeed or fail.
type ParseResult int
const (
// ParseResultSuccess indicates an expression that is expected to parse without error.
ParseResultSuccess ParseResult = iota
// ParseResultError indicates an expression that is expected to produce parse error(s).
ParseResultError
)
// TestCase represents an expression to parse and the expected parse result.
type TestCase struct {
Expr string
Result ParseResult
}
// ErrorCase returns a TestCase expecting a parse error.
func ErrorCase(expr string) TestCase {
return TestCase{
Expr: expr,
Result: ParseResultError,
}
}
// SuccessCase returns a TestCase expecting successful parsing.
func SuccessCase(expr string) TestCase {
return TestCase{
Expr: expr,
Result: ParseResultSuccess,
}
}
// Category represents a named group of test cases for benchmarking and verification.
type Category struct {
Name string
Cases []TestCase
}
// OptMapMacro expands `m.optMap(v, f)` into a conditional comprehension.
var OptMapMacro = parser.NewReceiverMacro("optMap", 2, optMapExpander)
func optMapExpander(meh parser.ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common.Error) {
varIdent := args[0]
varName := ""
switch varIdent.Kind() {
case ast.IdentKind:
varName = varIdent.AsIdent()
default:
return nil, meh.NewError(varIdent.ID(), "optMap() variable name must be a simple identifier")
}
mapExpr := args[1]
return meh.NewCall(
operators.Conditional,
meh.NewMemberCall("hasValue", target),
meh.NewCall("optional.of",
meh.NewComprehension(
meh.NewList(),
"#unused",
varName,
meh.NewMemberCall("value", meh.Copy(target)),
meh.NewLiteral(types.False),
meh.NewIdent(varName),
mapExpr,
),
),
meh.NewCall("optional.none"),
), nil
}
// GetCategories returns benchmark and correctness test cases organized by category.
func GetCategories() []Category {
return categories
}
// GetTestCases returns benchmark and correctness test cases flattened across all categories.
func GetTestCases() []TestCase {
var allCases []TestCase
for _, cat := range categories {
allCases = append(allCases, cat.Cases...)
}
return allCases
}
var categories = []Category{
// Simple: common, representative CEL expressions covering basic syntax, operators, calls, and literals
{
Name: "Simple",
Cases: []TestCase{
SuccessCase("x * 2 + y / 3"),
SuccessCase(`foo.bar.baz(1, 2, "abc")`),
SuccessCase(`a > 5 && b < 10 || c == "xyz"`),
SuccessCase("x ? y : z"),
SuccessCase(`{"foo": 1, "bar": [2, 3]}`),
SuccessCase("a[b]"),
SuccessCase("a.b.c"),
SuccessCase("a.`b-c`"),
SuccessCase("\"\\a\\b\\f\\n\\r\\t\\v'\\\"\\\\ Legal escapes \\u2764\""),
},
},
// Complex: expressions with deep chaining, nesting, precedence, and complex structures
{
Name: "Complex",
Cases: []TestCase{
SuccessCase("a" + strings.Repeat(" + a", 49)),
SuccessCase("a" + strings.Repeat(" || a", 49)),
SuccessCase("a" + strings.Repeat(".f", 49)),
SuccessCase(strings.Repeat("(", 20) + "a" + strings.Repeat(")", 20)),
SuccessCase(`SomeMessage{foo: 5, bar: "xyz"}`),
SuccessCase("1 + 2 * 3 - 1 / 2 == 6 % 1"),
SuccessCase("[] + [1, 2, 3] + [4]"),
},
},
// Macros: standard and receiver comprehension macros, optional syntax traversal
{
Name: "Macros",
Cases: []TestCase{
SuccessCase("has(m.f)"),
SuccessCase("[1, 2, 3].all(x, x > 0)"),
SuccessCase("m.map(v, v * 2)"),
SuccessCase("m.filter(v, v > 0)"),
SuccessCase("m.exists_one(v, v == 1)"),
SuccessCase("x.filter(y, y.exists(z, has(z.a)))"),
SuccessCase("a.?b[?0] && a[?c]"),
SuccessCase("m.optMap(v, v + 1)"),
},
},
// Errors: representative syntax errors, invalid tokens, keywords, and unclosed delimiters
{
Name: "Errors",
Cases: []TestCase{
ErrorCase("x * 2 + y /"),
ErrorCase(`foo.bar.baz(1, 2, "abc"`),
ErrorCase("a > 5 && && b < 10"),
ErrorCase(`{"foo": 1, "bar": [2, 3`),
ErrorCase("1 + $"),
ErrorCase("break"),
ErrorCase(`"\xFh"`),
ErrorCase("a" + strings.Repeat(" + a", 49) + " +"),
ErrorCase(strings.Repeat("(", 20) + "a"),
ErrorCase("f(*" + strings.Repeat(", *", 9) + ")"),
},
},
}