blob: e13cbdd10c565ddfe0aeea2e3b907988749bb94f [file] [log] [blame]
// Copyright 2018 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 interpreter
import (
"testing"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/containers"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/parser"
"github.com/google/cel-go/test"
)
type testInfo struct {
in Activation
expr string
out string
}
var testCases = []testInfo{
{
expr: `true && false`,
out: `false`,
},
{
in: unknownActivation("x"),
expr: `(true || false) && x`,
out: `x`,
},
{
in: unknownActivation("x"),
expr: `(false || false) && x`,
out: `false`,
},
{
in: unknownActivation("a"),
expr: `a && [1, 1u, 1.0].exists(x, type(x) == uint)`,
out: `a`,
},
{
expr: `{'hello': 'world'.size()}`,
out: `{"hello": 5}`,
},
{
expr: `[b'bytes-string']`,
out: `[b"\142\171\164\145\163\055\163\164\162\151\156\147"]`,
},
{
expr: `[b'bytes'] + [b'-' + b'string']`,
out: `[b"\142\171\164\145\163", b"\055\163\164\162\151\156\147"]`,
},
{
expr: `1u + 3u`,
out: `4u`,
},
{
expr: `2 < 3`,
out: `true`,
},
{
in: unknownActivation(),
expr: `test == null`,
out: `test == null`,
},
{
in: unknownActivation(),
expr: `test == null && false`,
out: `false`,
},
{
in: unknownActivation("b", "c"),
expr: `true ? b < 1.2 : c == ['hello']`,
out: `b < 1.2`,
},
{
in: unknownActivation(),
expr: `[1+3, 2+2, 3+1, four]`,
out: `[4, 4, 4, four]`,
},
{
in: unknownActivation(),
expr: `test == {'a': 1, 'field': 2}.field`,
out: `test == 2`,
},
{
in: unknownActivation(),
expr: `test in {'a': 1, 'field': [2, 3]}.field`,
out: `test in [2, 3]`,
},
{
in: unknownActivation(),
expr: `test == {'field': [1 + 2, 2 + 3]}`,
out: `test == {"field": [3, 5]}`,
},
{
in: unknownActivation(),
expr: `test in {'a': 1, 'field': [test, 3]}.field`,
out: `test in {"a": 1, "field": [test, 3]}.field`,
},
// TODO(issues/) the output test relies on tracking macro expansions back to their original
// call patterns.
/* {
in: unknownActivation(),
expr: `[1+3, 2+2, 3+1, four].exists(x, x == four)`,
out: `[4, 4, 4, four].exists(x, x == four)`,
}, */
}
func TestPrune(t *testing.T) {
for i, tst := range testCases {
ast, iss := parser.Parse(common.NewStringSource(tst.expr, "<input>"))
if len(iss.GetErrors()) > 0 {
t.Fatalf(iss.ToDisplayString())
}
state := NewEvalState()
reg := types.NewRegistry()
attrs := NewPartialAttributeFactory(containers.DefaultContainer, reg, reg)
interp := NewStandardInterpreter(containers.DefaultContainer, reg, reg, attrs)
interpretable, _ := interp.NewUncheckedInterpretable(
ast.Expr,
ExhaustiveEval(state))
interpretable.Eval(tst.in)
newExpr := PruneAst(ast.Expr, state)
actual, err := parser.Unparse(newExpr, nil)
if err != nil {
t.Error(err)
}
if !test.Compare(actual, tst.out) {
t.Errorf("prune[%d], diff: %s", i, test.DiffMessage("structure", actual, tst.out))
}
}
}
func unknownActivation(vars ...string) PartialActivation {
pats := make([]*AttributePattern, len(vars), len(vars))
for i, v := range vars {
pats[i] = NewAttributePattern(v)
}
a, _ := NewPartialActivation(map[string]interface{}{}, pats...)
return a
}