From 1eac29493195f6fce5c1c27dc3ec6408f8c03db2 Mon Sep 17 00:00:00 2001 From: Jonathan Tatum Date: Wed, 21 Oct 2026 19:05:06 +0000 Subject: [PATCH 1/2] Add basic conformance tests for the sets extension --- tests/simple/testdata/sets_ext.textproto | 340 +++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 tests/simple/testdata/sets_ext.textproto diff --git a/tests/simple/testdata/sets_ext.textproto b/tests/simple/testdata/sets_ext.textproto new file mode 100644 index 0000000..146f4a8 --- /dev/null +++ b/tests/simple/testdata/sets_ext.textproto @@ -0,0 +1,340 @@ +# proto-file: ../../../proto/cel/expr/conformance/test/simple.proto +# proto-message: cel.expr.conformance.test.SimpleTestFile + +name: "sets_ext" +description: "Tests for the sets extension library." + +# sets.contains(list(T), list(T)) -> bool +# Returns whether the first list argument (arg0) contains all elements in the +# second list argument (arg1), i.e., whether arg0 is a superset of arg1. +section: { + name: "contains" + description: "sets.contains: since version 0" + test: { + name: "empty_empty" + expr: "sets.contains([], [])" + } + test: { + name: "nonempty_empty" + expr: "sets.contains([1], [])" + } + test: { + name: "empty_nonempty" + expr: "!sets.contains([], [1])" + } + test: { + name: "single_equal" + expr: "sets.contains([1], [1])" + } + test: { + name: "single_not_equal" + expr: "!sets.contains([1], [2])" + } + test: { + name: "duplicates_in_sublist" + expr: "sets.contains([1], [1, 1])" + } + test: { + name: "duplicates_in_list" + expr: "sets.contains([1, 1], [1])" + } + test: { + name: "subset" + expr: "sets.contains([1, 2, 3, 4], [2, 3])" + } + test: { + name: "not_subset" + expr: "!sets.contains([1, 2], [2, 3])" + } + test: { + name: "superset_not_contained" + expr: "!sets.contains([1], [1, 2])" + } + test: { + name: "heterogeneous_numeric" + expr: "sets.contains([1, 2.0, 3u], [1.0, 2u, 3])" + } + test: { + name: "heterogeneous_numeric_duplicates" + expr: "sets.contains([1, 2], [2u, 2.0])" + } + test: { + name: "heterogeneous_numeric_fractional_false" + expr: "!sets.contains([1], [1.1, 1u])" + } + test: { + name: "heterogeneous_types_true" + expr: "sets.contains([1, '1'], [1])" + } + test: { + name: "heterogeneous_types_false" + expr: "!sets.contains([1], ['1', 1])" + } + test: { + name: "strings" + expr: "sets.contains(['a', 'b', 'c'], ['b', 'a'])" + } + test: { + name: "strings_false" + expr: "!sets.contains(['a', 'b'], ['b', 'c'])" + } + test: { + name: "bools" + expr: "sets.contains([true, false], [false])" + } + test: { + name: "bytes" + expr: "sets.contains([b'a', b'b'], [b'b'])" + } + test: { + name: "nested_lists" + expr: "sets.contains([[1], [2, 3]], [[2, 3.0]])" + } + test: { + name: "nested_lists_false" + expr: "!sets.contains([[1], [2], [3]], [[2, 3]])" + } + test: { + name: "maps" + expr: "sets.contains([{'a': 'b'}, {'c': 'd'}], [{'a': 'b'}])" + } + test: { + name: "maps_heterogeneous_numeric" + expr: "sets.contains([{1: 1}], [{1: 1u}, {1: 1.0}])" + } + test: { + name: "maps_false" + expr: "!sets.contains([{'a': 'b'}], [{'a': 'b'}, {'c': 'd'}])" + } + test: { + name: "proto_messages" + container: "cel.expr.conformance.proto3" + expr: "sets.contains([TestAllTypes{single_int64: 1, single_uint64: 2u}, TestAllTypes{single_int64: 3}], [TestAllTypes{single_int64: 1, single_uint64: 2u}])" + } + test: { + name: "proto_messages_false" + container: "cel.expr.conformance.proto3" + expr: "!sets.contains([TestAllTypes{single_int64: 1, single_uint64: 2u}], [TestAllTypes{single_int64: 2, single_uint64: 3u}])" + } +} + +# sets.equivalent(list(T), list(T)) -> bool +# Returns whether the first list argument (arg0) and the second list argument +# (arg1) are set equivalent, i.e., every element in arg0 has an equal element in +# arg1 and every element in arg1 has an equal element in arg0. +section: { + name: "equivalent" + description: "sets.equivalent: since version 0" + test: { + name: "empty_empty" + expr: "sets.equivalent([], [])" + } + test: { + name: "nonempty_empty" + expr: "!sets.equivalent([1], [])" + } + test: { + name: "empty_nonempty" + expr: "!sets.equivalent([], [1])" + } + test: { + name: "single_equal" + expr: "sets.equivalent([1], [1])" + } + test: { + name: "single_not_equal" + expr: "!sets.equivalent([1], [2])" + } + test: { + name: "duplicates_right" + expr: "sets.equivalent([1], [1, 1])" + } + test: { + name: "duplicates_left" + expr: "sets.equivalent([1, 1], [1])" + } + test: { + name: "duplicates_both_reordered" + expr: "sets.equivalent([1, 1, 2], [2, 2, 1])" + } + test: { + name: "strict_superset_left" + expr: "!sets.equivalent([2, 1], [1])" + } + test: { + name: "strict_subset_left" + expr: "!sets.equivalent([1], [1, 2])" + } + test: { + name: "heterogeneous_numeric_single" + expr: "sets.equivalent([1], [1u, 1.0])" + } + test: { + name: "heterogeneous_numeric_reordered" + expr: "sets.equivalent([1, 2, 3], [3u, 2.0, 1])" + } + test: { + name: "heterogeneous_numeric_missing_element" + expr: "!sets.equivalent([1, 2], [2u, 2, 2.0])" + } + test: { + name: "heterogeneous_numeric_fractional_false" + expr: "!sets.equivalent([1, 2], [1u, 2, 2.3])" + } + test: { + name: "strings" + expr: "sets.equivalent(['foo', 'bar'], ['bar', 'foo'])" + } + test: { + name: "strings_false" + expr: "!sets.equivalent(['foo'], ['bar'])" + } + test: { + name: "bools" + expr: "sets.equivalent([false, true], [true, false])" + } + test: { + name: "bools_false" + expr: "!sets.equivalent([true], [false])" + } + test: { + name: "bytes" + expr: "sets.equivalent([b'foo', b'bar'], [b'bar', b'foo'])" + } + test: { + name: "nulls" + expr: "sets.equivalent([null], [null, null])" + } + test: { + name: "durations" + expr: "sets.equivalent([duration('0s'), duration('1s')], [duration('1s'), duration('0s')])" + } + test: { + name: "timestamps" + expr: "sets.equivalent([timestamp('1970-01-01T00:00:00Z'), timestamp('1970-01-01T00:00:01Z')], [timestamp('1970-01-01T00:00:01Z'), timestamp('1970-01-01T00:00:00Z')])" + } + test: { + name: "nested_lists" + expr: "sets.equivalent([[1.0], [2, 3]], [[1], [2, 3.0]])" + } + test: { + name: "nested_lists_order_matters" + expr: "!sets.equivalent([[false, true]], [[true, false]])" + } + test: { + name: "maps" + expr: "sets.equivalent([{'foo': true, 'bar': false}], [{'bar': false, 'foo': true}])" + } + test: { + name: "maps_heterogeneous_numeric" + expr: "sets.equivalent([{1: 1}], [{1: 1u}, {1: 1.0}])" + } + test: { + name: "proto_messages" + container: "cel.expr.conformance.proto3" + expr: "sets.equivalent([TestAllTypes{single_int64: 1, single_uint64: 2u}, TestAllTypes{single_int64: 1, single_uint64: 2u}], [TestAllTypes{single_int64: 1, single_uint64: 2u}])" + } + test: { + name: "proto_messages_false" + container: "cel.expr.conformance.proto3" + expr: "!sets.equivalent([TestAllTypes{single_int64: 1, single_uint64: 2u}], [TestAllTypes{single_int64: 2, single_uint64: 3u}])" + } +} + +# sets.intersects(list(T), list(T)) -> bool +# Returns whether the first list argument (arg0) has at least one element whose +# value is equal to an element in the second list argument (arg1). +section: { + name: "intersects" + description: "sets.intersects: since version 0" + test: { + name: "empty_empty" + expr: "!sets.intersects([], [])" + } + test: { + name: "nonempty_empty" + expr: "!sets.intersects([1], [])" + } + test: { + name: "empty_nonempty" + expr: "!sets.intersects([], [1])" + } + test: { + name: "single_equal" + expr: "sets.intersects([1], [1])" + } + test: { + name: "single_not_equal" + expr: "!sets.intersects([1], [2])" + } + test: { + name: "duplicates_right" + expr: "sets.intersects([1], [1, 1])" + } + test: { + name: "duplicates_left" + expr: "sets.intersects([1, 1], [1])" + } + test: { + name: "overlap" + expr: "sets.intersects([1], [1, 2])" + } + test: { + name: "disjoint" + expr: "!sets.intersects([1, 3], [2, 4])" + } + test: { + name: "heterogeneous_numeric" + expr: "sets.intersects([1], [1.0, 2])" + } + test: { + name: "heterogeneous_numeric_multiple" + expr: "sets.intersects([1, 2], [2u, 2, 2.0])" + } + test: { + name: "heterogeneous_numeric_with_fractional" + expr: "sets.intersects([1, 2], [1u, 2, 2.3])" + } + test: { + name: "heterogeneous_numeric_false" + expr: "!sets.intersects([1], [1.1, 2u])" + } + test: { + name: "heterogeneous_types_false" + expr: "!sets.intersects([1], ['1', 2])" + } + test: { + name: "strings" + expr: "sets.intersects(['a', 'b'], ['b', 'c'])" + } + test: { + name: "strings_false" + expr: "!sets.intersects(['a', 'b'], ['c', 'd'])" + } + test: { + name: "nested_lists" + expr: "sets.intersects([[1], [2, 3]], [[1, 2], [2, 3.0]])" + } + test: { + name: "maps" + expr: "sets.intersects([{'a': 'b'}, {'a': 'b'}], [{'a': 'b'}])" + } + test: { + name: "maps_heterogeneous_numeric" + expr: "sets.intersects([{1: 1}], [{1: 1u}, {1: 1.0}])" + } + test: { + name: "maps_false" + expr: "!sets.intersects([{'a': 'b'}], [{'c': 'd'}])" + } + test: { + name: "proto_messages" + container: "cel.expr.conformance.proto3" + expr: "sets.intersects([TestAllTypes{single_int64: 1, single_uint64: 2u}], [TestAllTypes{single_int64: 1, single_uint64: 2u}, TestAllTypes{single_int64: 3}])" + } + test: { + name: "proto_messages_false" + container: "cel.expr.conformance.proto3" + expr: "!sets.intersects([TestAllTypes{single_int64: 1, single_uint64: 2u}], [TestAllTypes{single_int64: 2, single_uint64: 3u}])" + } +} From 427ce8e891ce3d17b95fc5c6f05f45329a3f2352 Mon Sep 17 00:00:00 2001 From: Jonathan Tatum Date: Wed, 21 Oct 2026 21:01:36 +0000 Subject: [PATCH 2/2] additional tests --- tests/simple/testdata/sets_ext.textproto | 153 +++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/tests/simple/testdata/sets_ext.textproto b/tests/simple/testdata/sets_ext.textproto index 146f4a8..dab91e9 100644 --- a/tests/simple/testdata/sets_ext.textproto +++ b/tests/simple/testdata/sets_ext.textproto @@ -62,6 +62,34 @@ section: { name: "heterogeneous_numeric_fractional_false" expr: "!sets.contains([1], [1.1, 1u])" } + test: { + name: "numeric_negatives" + expr: "sets.contains([-1, -9223372036854775808], [-1.0, -9223372036854775808])" + } + test: { + name: "numeric_negatives_and_uint_false" + expr: "!sets.contains([18446744073709551615u, 0u, 1], [-1, -9223372036854775808])" + } + test: { + name: "numeric_uint64_max" + expr: "sets.contains([18446744073709551615u, 0u], [18446744073709551615u])" + } + test: { + name: "numeric_uint64_max_false" + expr: "!sets.contains([-1, 9223372036854775807, 18446744073709590000.0], [18446744073709551615u])" + } + test: { + name: "double_nan_false" + expr: "!sets.contains([double('NaN')], [double('NaN')])" + } + test: { + name: "double_inf" + expr: "sets.contains([double('Infinity'), double('-Infinity')], [double('Infinity')])" + } + test: { + name: "double_inf_false" + expr: "!sets.contains([double('Infinity'), 9223372036854775807, 18446744073709551615u], [double('-Infinity')])" + } test: { name: "heterogeneous_types_true" expr: "sets.contains([1, '1'], [1])" @@ -102,10 +130,33 @@ section: { name: "maps_heterogeneous_numeric" expr: "sets.contains([{1: 1}], [{1: 1u}, {1: 1.0}])" } + test: { + name: "maps_heterogeneous_keys" + expr: "sets.contains([{'a': 1, 1: 2, 2u: 3, true: 4}, {'b': 5}], [{true: 4, 2u: 3.0, 1: 2u, 'a': 1.0}])" + } + test: { + name: "maps_heterogeneous_keys_false" + expr: "!sets.contains([{'a': 1, 1: 2}], [{'a': 1, '1': 2}])" + } test: { name: "maps_false" expr: "!sets.contains([{'a': 'b'}], [{'a': 'b'}, {'c': 'd'}])" } + test: { + name: "proto_wrappers" + container: "cel.expr.conformance.proto3" + expr: "sets.contains([TestAllTypes{single_int64_wrapper: 1}.single_int64_wrapper, TestAllTypes{}.single_int64_wrapper], [1, null])" + } + test: { + name: "proto_wrappers_messages" + container: "cel.expr.conformance.proto3" + expr: "sets.contains([google.protobuf.Int64Value{value: 1}, google.protobuf.Int64Value{}], [1u, 0.0])" + } + test: { + name: "proto_wrappers_null_not_default_false" + container: "cel.expr.conformance.proto3" + expr: "!sets.contains([TestAllTypes{}.single_int64_wrapper], [0])" + } test: { name: "proto_messages" container: "cel.expr.conformance.proto3" @@ -181,6 +232,34 @@ section: { name: "heterogeneous_numeric_fractional_false" expr: "!sets.equivalent([1, 2], [1u, 2, 2.3])" } + test: { + name: "numeric_negatives" + expr: "sets.equivalent([-1, -9223372036854775808], [-9223372036854775808, -1.0])" + } + test: { + name: "numeric_negatives_and_uint_false" + expr: "!sets.equivalent([-1, -1.0], [18446744073709551615u])" + } + test: { + name: "numeric_uint64_max" + expr: "sets.equivalent([18446744073709551615u], [18446744073709551615u, 18446744073709551615u])" + } + test: { + name: "numeric_uint64_max_false" + expr: "!sets.equivalent([18446744073709551615u], [-1, 9223372036854775807, 18446744073709590000.0])" + } + test: { + name: "double_nan_false" + expr: "!sets.equivalent([double('NaN')], [double('NaN')])" + } + test: { + name: "double_inf" + expr: "sets.equivalent([double('Infinity'), double('-Infinity')], [double('-Infinity'), double('Infinity')])" + } + test: { + name: "double_inf_false" + expr: "!sets.equivalent([double('Infinity')], [double('-Infinity')])" + } test: { name: "strings" expr: "sets.equivalent(['foo', 'bar'], ['bar', 'foo'])" @@ -229,6 +308,29 @@ section: { name: "maps_heterogeneous_numeric" expr: "sets.equivalent([{1: 1}], [{1: 1u}, {1: 1.0}])" } + test: { + name: "maps_heterogeneous_keys" + expr: "sets.equivalent([{'a': 1, 1: 2, 2u: 3, true: 4}], [{true: 4, 2u: 3.0, 1: 2u, 'a': 1.0}])" + } + test: { + name: "maps_heterogeneous_keys_false" + expr: "!sets.equivalent([{'a': 1, 1: 2}], [{'a': 1, '1': 2}])" + } + test: { + name: "proto_wrappers" + container: "cel.expr.conformance.proto3" + expr: "sets.equivalent([TestAllTypes{single_int64_wrapper: 1}.single_int64_wrapper, TestAllTypes{}.single_int64_wrapper], [null, 1u, 1.0])" + } + test: { + name: "proto_wrappers_messages" + container: "cel.expr.conformance.proto3" + expr: "sets.equivalent([google.protobuf.Int64Value{value: 1}, google.protobuf.Int64Value{}], [0, 1.0])" + } + test: { + name: "proto_wrappers_null_not_default_false" + container: "cel.expr.conformance.proto3" + expr: "!sets.equivalent([TestAllTypes{}.single_int64_wrapper], [0])" + } test: { name: "proto_messages" container: "cel.expr.conformance.proto3" @@ -299,6 +401,34 @@ section: { name: "heterogeneous_numeric_false" expr: "!sets.intersects([1], [1.1, 2u])" } + test: { + name: "numeric_negatives" + expr: "sets.intersects([-1, -9223372036854775808], [-1.0, 1])" + } + test: { + name: "numeric_negatives_and_uint_false" + expr: "!sets.intersects([-1, -9223372036854775808], [18446744073709551615u, 0u, 1])" + } + test: { + name: "numeric_uint64_max" + expr: "sets.intersects([18446744073709551615u], [0u, 18446744073709551615u])" + } + test: { + name: "numeric_uint64_max_false" + expr: "!sets.intersects([18446744073709551615u], [-1, 9223372036854775807, 18446744073709590000.0])" + } + test: { + name: "double_nan_false" + expr: "!sets.intersects([double('NaN')], [double('NaN')])" + } + test: { + name: "double_inf" + expr: "sets.intersects([double('Infinity'), 1.0], [double('Infinity'), double('-Infinity')])" + } + test: { + name: "double_inf_false" + expr: "!sets.intersects([double('Infinity')], [double('-Infinity'), 9223372036854775807, 18446744073709551615u])" + } test: { name: "heterogeneous_types_false" expr: "!sets.intersects([1], ['1', 2])" @@ -323,10 +453,33 @@ section: { name: "maps_heterogeneous_numeric" expr: "sets.intersects([{1: 1}], [{1: 1u}, {1: 1.0}])" } + test: { + name: "maps_heterogeneous_keys" + expr: "sets.intersects([{'a': 1, 1: 2, 2u: 3, true: 4}], [{'b': 5}, {true: 4, 2u: 3.0, 1: 2u, 'a': 1.0}])" + } + test: { + name: "maps_heterogeneous_keys_false" + expr: "!sets.intersects([{'a': 1, 1: 2}], [{'a': 1, '1': 2}])" + } test: { name: "maps_false" expr: "!sets.intersects([{'a': 'b'}], [{'c': 'd'}])" } + test: { + name: "proto_wrappers" + container: "cel.expr.conformance.proto3" + expr: "sets.intersects([TestAllTypes{single_int64_wrapper: 1}.single_int64_wrapper, TestAllTypes{}.single_int64_wrapper], [2, null])" + } + test: { + name: "proto_wrappers_messages" + container: "cel.expr.conformance.proto3" + expr: "sets.intersects([google.protobuf.Int64Value{value: 1}], [2u, 1.0])" + } + test: { + name: "proto_wrappers_null_not_default_false" + container: "cel.expr.conformance.proto3" + expr: "!sets.intersects([TestAllTypes{}.single_int64_wrapper], [0, 0u, 0.0])" + } test: { name: "proto_messages" container: "cel.expr.conformance.proto3"