From 9c1fdab7326705bd57303146b618950dd2661054 Mon Sep 17 00:00:00 2001 From: "S.Lott" Date: Tue, 19 May 2026 13:54:28 -0700 Subject: [PATCH] New Features from 0.25.2 release of cel-spec This has a new set of `.textproto` files, and revised `.feature` files. The `wip.toml` is updated to flag new cel-spec test cases as "work-in-process" with the `@wip` tag on the tests. --- features/Makefile | 21 +- features/README.rst | 38 +- features/bindings_ext.feature | 26 + features/bindings_ext.textproto | 59 + features/comparisons.feature | 2 - features/conversions.feature | 14 - features/dynamic.feature | 8 +- features/fields.feature | 4 +- features/git.log | 5 + features/math_ext.feature | 166 +-- features/namespace.feature | 115 +- features/namespace.textproto | 203 ++- features/parse.feature | 1270 +++++++++++++++++ features/parse.textproto | 1234 ++++++++++++++++ features/proto2.feature | 108 +- features/proto2.textproto | 63 +- features/proto2_ext.feature | 36 +- features/proto3.feature | 72 +- features/proto3.textproto | 60 + features/string_ext.feature | 261 +++- features/string_ext.textproto | 120 +- tools/README.rst | 30 +- tools/refresh_spec.py | 76 + ...urvey.ipynb => survey_feature_files.ipynb} | 23 + tools/test_refresh_spec.py | 120 ++ tools/wip.toml | 289 +++- 26 files changed, 4167 insertions(+), 256 deletions(-) create mode 100644 features/git.log create mode 100644 tools/refresh_spec.py rename tools/{test_survey.ipynb => survey_feature_files.ipynb} (99%) create mode 100644 tools/test_refresh_spec.py diff --git a/features/Makefile b/features/Makefile index 3028201..a50251e 100644 --- a/features/Makefile +++ b/features/Makefile @@ -70,26 +70,33 @@ SOURCES := $(patsubst %.feature,%.textproto,$(FEATURES)) .DELETE_ON_ERROR: -all : $(FEATURES) +HELP_TEXT += " all creates all features\n" +.PHONY: all +all : $(FEATURES) git.log -HELP_TEXT += " %.textproto copies the .textproto from the cel-spec directory\n" +HELP_TEXT += " git.log creates a git.log file with the last 5 lines of the git log\n" +git.log : + cd $(CEL_SPEC_PATH) && git log --format=oneline --abbrev-commit -n 5 --decorate=full > $(HERE)$@ + +HELP_TEXT += " %.textproto copies the .textproto from the cel-spec directory\n" %.textproto : $(CEL_SIMPLE_TESTDATA)/%.textproto cp $(CEL_SIMPLE_TESTDATA)/$@ $@ -HELP_TEXT += " %.feature generates Gherkin feature from %.textproto source\n" +HELP_TEXT += " %.feature generates Gherkin feature from %.textproto source\n" %.feature : %.textproto uv run $(TOOLS)/gherkinize.py -v $^ -o $@ -HELP_TEXT += " clean cleans generated feature files\n" +HELP_TEXT += " clean-features cleans generated feature files\n" .PHONY: clean-features clean-features: rm $(FEATURES) -HELP_TEXT += " clean cleans generated feature files and textproto sources\n" +HELP_TEXT += " clean cleans generated feature files and textproto sources\n" .PHONY: clean clean: - rm $(FEATURES) - rm $(SOURCES) + -rm $(FEATURES) + -rm $(SOURCES) + -rm git.log .PHONY: conformance-feature-list conformance-feature-list: diff --git a/features/README.rst b/features/README.rst index 1c1a724..7f00a03 100644 --- a/features/README.rst +++ b/features/README.rst @@ -5,8 +5,7 @@ Acceptance Test Suite We start with https://github.com/google/cel-spec/tree/master/tests/simple/testdata as the acceptance test suite. -These files are captured as of commit 9f069b3e. -This is from May 5, 2025, version 0.24.0. +See the ``git.log`` file for the last 5 log entries in the ``google/cel-spec`` project. We parse the text serialization of protobuf files to create Gherkin test scenarios. @@ -31,7 +30,14 @@ As an alternative, this can be done by running **behave** manually, using the `` :: - PYTHONPATH=src uv run behave features + PYTHONPATH=src uv run behave -Denv="py312" features + +:: + + PYTHONPATH=src uv run behave -Denv="py312" features/json_query.feature + +The value of ``env`` should match the default Python in your virtual environment. +This is required to test the ``json_query.feature``. To run a subset of the features, pick a specific file. @@ -39,9 +45,8 @@ To run a subset of the features, pick a specific file. PYTHONPATH=src uv run behave features/basic.feature -To run the ``@wip`` tests, include ``--tags=wip``. +To skip the Work-in-Process features, include ``--tags=~wip``. -To skip the ``@wip`` tests, include ``tags=~wip``. Building the Conformance Test Features ====================================== @@ -51,17 +56,30 @@ See the ``tools/README.rst`` for more information on running this application. Here's the bigger picture workflow: -1. Create a ``google/cel-spec`` project parallel to this project's directory. +1. Create a ``../google/cel-spec`` project parallel to this project's directory. This should be based on https://github.com/google/cel-spec (Or, create it anywhere and set ``CEL_SPEC_PATH`` to refer to this directory.) -2. Find the tag for the most recent release (e.g. ``v0.24.0``) +2. Run the ``tools/refresh_spec.``py to find the most recent tag and pull the current files. + +5. Run ``make`` to copy the ``.textproto`` files to this directory and create ``.feature`` files from them. + This will **also** create a ``git.log`` file with the last 5 log entries to help pinpoint + the commit on which the acceptance test suite is based. -3. Do ``git pull tag v0.24.0`` to get the released version. + Remove the ``.textproto`` and ``.feature`` files and rebuild them: -4. Update this ``README.rst`` to reflect the version tag actually used. + :: -5. Run ``make`` to copy the ``.textproto`` files to this directory and create ``feature`` files from them. + make clean all + +Changing the ``@wip`` tags in the feature files. + +The ``@wip`` tags are added by ``gherkinize.py`` based on a ``wip.toml`` configuration file. +As features are added, update the ``wip.toml`` file and rebuild the ``.feature`` files, without touching the ``.textproto`` files. + +:: + make clean-features all +This will reset the tags in the ``.feature`` files. diff --git a/features/bindings_ext.feature b/features/bindings_ext.feature index 5c7b178..b3b84ac 100644 --- a/features/bindings_ext.feature +++ b/features/bindings_ext.feature @@ -35,3 +35,29 @@ Scenario: bind/macro_not_exists When CEL expression 'cel.bind(valid_elems, [1, 2, 3], ![4, 5].exists(e, e in valid_elems))' is evaluated Then value is celpy.celtypes.BoolType(source=True) +@wip +Scenario: bind/shadowing + + Given type_env parameter "x" is celpy.celtypes.IntType + and bindings parameter "x" is celpy.celtypes.IntType(source=1) + When CEL expression 'cel.bind(x, 0, x == 0)' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: bind/shadowing_namespace_resolution + + Given type_env parameter "com.example.x" is celpy.celtypes.IntType + and bindings parameter "com.example.x" is celpy.celtypes.IntType(source=1) + and container is 'com.example' + When CEL expression 'cel.bind(x, 0, x == 0)' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: bind/shadowing_namespace_resolution_selector + + Given type_env parameter "com.example.x.y" is celpy.celtypes.IntType + and bindings parameter "com.example.x.y" is celpy.celtypes.IntType(source=1) + and container is 'com.example' + When CEL expression "cel.bind(x, {'y': 0}, x.y == 0)" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + diff --git a/features/bindings_ext.textproto b/features/bindings_ext.textproto index 3a3d93f..62bf65c 100644 --- a/features/bindings_ext.textproto +++ b/features/bindings_ext.textproto @@ -40,4 +40,63 @@ section: { bool_value: true } } + test: { + name: "shadowing" + expr: "cel.bind(x, 0, x == 0)" + type_env: { + name: "x" + ident: { + type: { primitive: INT64 } + } + } + bindings: { + key: "x" + value: { + value: { int64_value: 1 } + } + } + value: { + bool_value: true + } + } + test: { + name: "shadowing_namespace_resolution" + expr: "cel.bind(x, 0, x == 0)" + container: "com.example" + type_env: { + name: "com.example.x" + ident: { + type: { primitive: INT64 } + } + } + bindings: { + key: "com.example.x" + value: { + value: { int64_value: 1 } + } + } + value: { + bool_value: true + } + } + test: { + name: "shadowing_namespace_resolution_selector" + expr: "cel.bind(x, {'y': 0}, x.y == 0)" + container: "com.example" + type_env: { + name: "com.example.x.y" + ident: { + type: { primitive: INT64 } + } + } + bindings: { + key: "com.example.x.y" + value: { + value: { int64_value: 1 } + } + } + value: { + bool_value: true + } + } } diff --git a/features/comparisons.feature b/features/comparisons.feature index 534cc79..5f7df45 100644 --- a/features/comparisons.feature +++ b/features/comparisons.feature @@ -350,13 +350,11 @@ Scenario: eq_literal/not_eq_dyn_int_null When CEL expression 'dyn(1) == null' is evaluated Then value is celpy.celtypes.BoolType(source=False) -@wip Scenario: eq_literal/not_eq_dyn_list_null When CEL expression 'dyn([]) == null' is evaluated Then value is celpy.celtypes.BoolType(source=False) -@wip Scenario: eq_literal/not_eq_dyn_map_null When CEL expression 'dyn({}) == null' is evaluated diff --git a/features/conversions.feature b/features/conversions.feature index bd84b2b..690dd31 100644 --- a/features/conversions.feature +++ b/features/conversions.feature @@ -170,25 +170,21 @@ Scenario: int/double_round_neg When CEL expression 'int(-123.456)' is evaluated Then value is celpy.celtypes.IntType(source=-123) -@wip Scenario: int/double_truncate When CEL expression 'int(1.9)' is evaluated Then value is celpy.celtypes.IntType(source=1) -@wip Scenario: int/double_truncate_neg When CEL expression 'int(-7.9)' is evaluated Then value is celpy.celtypes.IntType(source=-7) -@wip Scenario: int/double_half_pos When CEL expression 'int(11.5)' is evaluated Then value is celpy.celtypes.IntType(source=11) -@wip Scenario: int/double_half_neg When CEL expression 'int(-3.5)' is evaluated @@ -451,13 +447,11 @@ Scenario: uint/double When CEL expression 'uint(3.14159265)' is evaluated Then value is celpy.celtypes.UintType(source=3) -@wip Scenario: uint/double_truncate When CEL expression 'uint(1.9)' is evaluated Then value is celpy.celtypes.UintType(source=1) -@wip Scenario: uint/double_half When CEL expression 'uint(25.5)' is evaluated @@ -499,25 +493,21 @@ Scenario: bool/string_1 When CEL expression "bool('1')" is evaluated Then value is celpy.celtypes.BoolType(source=True) -@wip Scenario: bool/string_t When CEL expression "bool('t')" is evaluated Then value is celpy.celtypes.BoolType(source=True) -@wip Scenario: bool/string_true_lowercase When CEL expression "bool('true')" is evaluated Then value is celpy.celtypes.BoolType(source=True) -@wip Scenario: bool/string_true_uppercase When CEL expression "bool('TRUE')" is evaluated Then value is celpy.celtypes.BoolType(source=True) -@wip Scenario: bool/string_true_pascalcase When CEL expression "bool('True')" is evaluated @@ -528,25 +518,21 @@ Scenario: bool/string_0 When CEL expression "bool('0')" is evaluated Then value is celpy.celtypes.BoolType(source=False) -@wip Scenario: bool/string_f When CEL expression "bool('f')" is evaluated Then value is celpy.celtypes.BoolType(source=False) -@wip Scenario: bool/string_false_lowercase When CEL expression "bool('false')" is evaluated Then value is celpy.celtypes.BoolType(source=False) -@wip Scenario: bool/string_false_uppercase When CEL expression "bool('FALSE')" is evaluated Then value is celpy.celtypes.BoolType(source=False) -@wip Scenario: bool/string_false_pascalcase When CEL expression "bool('False')" is evaluated diff --git a/features/dynamic.feature b/features/dynamic.feature index 0d3eb9c..7963ae2 100644 --- a/features/dynamic.feature +++ b/features/dynamic.feature @@ -888,7 +888,7 @@ Scenario: struct/field_assign_proto2 Given container is 'cel.expr.conformance.proto2' When CEL expression "TestAllTypes{single_struct: {'un': 1.0, 'deux': 2.0}}" is evaluated - Then value is TestAllTypes(single_struct=celpy.celtypes.MapType({'deux': celpy.celtypes.DoubleType(source=2.0), 'un': celpy.celtypes.DoubleType(source=1.0)})) + Then value is TestAllTypes(single_struct=celpy.celtypes.MapType({'un': celpy.celtypes.DoubleType(source=1.0), 'deux': celpy.celtypes.DoubleType(source=2.0)})) Scenario: struct/field_assign_proto2_empty @@ -927,7 +927,7 @@ Scenario: struct/field_assign_proto3 Given container is 'cel.expr.conformance.proto3' When CEL expression "TestAllTypes{single_struct: {'un': 1.0, 'deux': 2.0}}" is evaluated - Then value is TestAllTypes(single_struct=celpy.celtypes.MapType({'deux': celpy.celtypes.DoubleType(source=2.0), 'un': celpy.celtypes.DoubleType(source=1.0)})) + Then value is TestAllTypes(single_struct=celpy.celtypes.MapType({'un': celpy.celtypes.DoubleType(source=1.0), 'deux': celpy.celtypes.DoubleType(source=2.0)})) Scenario: struct/field_assign_proto3_empty @@ -1290,7 +1290,7 @@ Scenario: value_struct/field_assign_proto2 Given container is 'cel.expr.conformance.proto2' When CEL expression "TestAllTypes{single_value: {'un': 1.0, 'deux': 2.0}}" is evaluated - Then value is TestAllTypes(single_value=celpy.celtypes.MapType({'deux': celpy.celtypes.DoubleType(source=2.0), 'un': celpy.celtypes.DoubleType(source=1.0)})) + Then value is TestAllTypes(single_value=celpy.celtypes.MapType({'un': celpy.celtypes.DoubleType(source=1.0), 'deux': celpy.celtypes.DoubleType(source=2.0)})) Scenario: value_struct/field_assign_proto2_empty @@ -1314,7 +1314,7 @@ Scenario: value_struct/field_assign_proto3 Given container is 'cel.expr.conformance.proto3' When CEL expression "TestAllTypes{single_value: {'un': 1.0, 'deux': 2.0}}" is evaluated - Then value is TestAllTypes(single_value=celpy.celtypes.MapType({'deux': celpy.celtypes.DoubleType(source=2.0), 'un': celpy.celtypes.DoubleType(source=1.0)})) + Then value is TestAllTypes(single_value=celpy.celtypes.MapType({'un': celpy.celtypes.DoubleType(source=1.0), 'deux': celpy.celtypes.DoubleType(source=2.0)})) Scenario: value_struct/field_assign_proto3_empty diff --git a/features/fields.feature b/features/fields.feature index 9338119..aa41675 100644 --- a/features/fields.feature +++ b/features/fields.feature @@ -276,8 +276,8 @@ Scenario: qualified_identifier_resolution/qualified_identifier_resolution_unchec Given disable_check parameter is True and type_env parameter "a.b.c" is celpy.celtypes.StringType and type_env parameter "a.b" is celpy.celtypes.MapType - and bindings parameter "a.b.c" is celpy.celtypes.StringType(source='yeah') and bindings parameter "a.b" is celpy.celtypes.MapType({'c': celpy.celtypes.StringType(source='oops')}) + and bindings parameter "a.b.c" is celpy.celtypes.StringType(source='yeah') When CEL expression 'a.b.c' is evaluated Then value is celpy.celtypes.StringType(source='yeah') @@ -303,8 +303,8 @@ Scenario: qualified_identifier_resolution/ident_with_longest_prefix_check Given type_env parameter "a.b.c" is celpy.celtypes.StringType and type_env parameter "a.b" is celpy.celtypes.MapType - and bindings parameter "a.b.c" is celpy.celtypes.StringType(source='yeah') and bindings parameter "a.b" is celpy.celtypes.MapType({'c': celpy.celtypes.StringType(source='oops')}) + and bindings parameter "a.b.c" is celpy.celtypes.StringType(source='yeah') When CEL expression 'a.b.c' is evaluated Then value is celpy.celtypes.StringType(source='yeah') diff --git a/features/git.log b/features/git.log new file mode 100644 index 0000000..4ecbc35 --- /dev/null +++ b/features/git.log @@ -0,0 +1,5 @@ +40e3bde (HEAD -> refs/heads/master, refs/remotes/origin/master, refs/remotes/origin/HEAD) Workaround inconsistent \? in proto textformat (#512) +cb51b41 (tag: refs/tags/v0.25.2) Add conformance test cases for null assignability around repeated fields and maps (#510) +8e848ac Networking extension conformance tests (#507) +8d28382 Add clarification on comprehension scoping rules. (#506) +121e265 Add automated BCR publishing on release tags (#498) diff --git a/features/math_ext.feature b/features/math_ext.feature index c235a44..1996682 100644 --- a/features/math_ext.feature +++ b/features/math_ext.feature @@ -27,13 +27,13 @@ Scenario: greatest_int_result/binary_same_args Scenario: greatest_int_result/binary_with_decimal When CEL expression 'math.greatest(1, 1.0) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/binary_with_uint When CEL expression 'math.greatest(1, 1u) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/binary_first_arg_greater @@ -75,67 +75,67 @@ Scenario: greatest_int_result/binary_second_arg_int_min Scenario: greatest_int_result/ternary_same_args When CEL expression 'math.greatest(1, 1, 1) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/ternary_with_decimal When CEL expression 'math.greatest(1, 1.0, 1.0) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/ternary_with_uint When CEL expression 'math.greatest(1, 1u, 1u) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/ternary_first_arg_greatest When CEL expression 'math.greatest(10, 1, 3) == 10' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/ternary_third_arg_greatest When CEL expression 'math.greatest(1, 3, 10) == 10' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/ternary_with_negatives When CEL expression 'math.greatest(-1, -2, -3) == -1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/ternary_int_max When CEL expression 'math.greatest(9223372036854775807, 1, 5) == 9223372036854775807' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/ternary_int_min When CEL expression 'math.greatest(-9223372036854775807, -1, -5) == -1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/quaternary_mixed When CEL expression 'math.greatest(5.4, 10, 3u, -5.0, 9223372036854775807) == 9223372036854775807' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/quaternary_mixed_array When CEL expression 'math.greatest([5.4, 10, 3u, -5.0, 3.5]) == 10' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_int_result/quaternary_mixed_dyn_array When CEL expression 'math.greatest([dyn(5.4), dyn(10), dyn(3u), dyn(-5.0), dyn(3.5)]) == 10' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # greatest_double_result -- @@ -162,13 +162,13 @@ Scenario: greatest_double_result/binary_same_args Scenario: greatest_double_result/binary_with_int When CEL expression 'math.greatest(1.0, 1) == 1.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/binary_with_uint When CEL expression 'math.greatest(1.0, 1u) == 1.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/binary_first_arg_greater @@ -210,67 +210,67 @@ Scenario: greatest_double_result/binary_second_arg_double_min Scenario: greatest_double_result/ternary_same_args When CEL expression 'math.greatest(1.0, 1.0, 1.0) == 1.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/ternary_with_int When CEL expression 'math.greatest(1.0, 1, 1) == 1.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/ternary_with_uint When CEL expression 'math.greatest(1.0, 1u, 1u) == 1.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/ternary_first_arg_greatest When CEL expression 'math.greatest(10.5, 1.5, 3.5) == 10.5' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/ternary_third_arg_greatest When CEL expression 'math.greatest(1.5, 3.5, 10.5) == 10.5' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/ternary_with_negatives When CEL expression 'math.greatest(-1.5, -2.5, -3.5) == -1.5' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/ternary_double_max When CEL expression 'math.greatest(1.797693e308, 1, 5) == 1.797693e308' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/ternary_double_min When CEL expression 'math.greatest(-1.797693e308, -1, -5) == -1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/quaternary_mixed When CEL expression 'math.greatest(5.4, 10, 3u, -5.0, 1.797693e308) == 1.797693e308' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/quaternary_mixed_array When CEL expression 'math.greatest([5.4, 10.5, 3u, -5.0, 3.5]) == 10.5' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_double_result/quaternary_mixed_dyn_array When CEL expression 'math.greatest([dyn(5.4), dyn(10.5), dyn(3u), dyn(-5.0), dyn(3.5)]) == 10.5' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # greatest_uint_result -- @@ -291,13 +291,13 @@ Scenario: greatest_uint_result/binary_same_args Scenario: greatest_uint_result/binary_with_decimal When CEL expression 'math.greatest(1u, 1.0) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_uint_result/binary_with_int When CEL expression 'math.greatest(1u, 1) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_uint_result/binary_first_arg_greater @@ -327,55 +327,55 @@ Scenario: greatest_uint_result/binary_second_arg_uint_max Scenario: greatest_uint_result/ternary_same_args When CEL expression 'math.greatest(1u, 1u, 1u) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_uint_result/ternary_with_decimal When CEL expression 'math.greatest(1u, 1.0, 1.0) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_uint_result/ternary_with_int When CEL expression 'math.greatest(1u, 1, 1) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_uint_result/ternary_first_arg_greatest When CEL expression 'math.greatest(10u, 1u, 3u) == 10u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_uint_result/ternary_third_arg_greatest When CEL expression 'math.greatest(1u, 3u, 10u) == 10u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_uint_result/ternary_int_max When CEL expression 'math.greatest(18446744073709551615u, 1u, 5u) == 18446744073709551615u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_uint_result/quaternary_mixed When CEL expression 'math.greatest(5.4, 10, 3u, -5.0, 18446744073709551615u) == 18446744073709551615u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_uint_result/quaternary_mixed_array When CEL expression 'math.greatest([5.4, 10u, 3u, -5.0, 3.5]) == 10u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: greatest_uint_result/quaternary_mixed_dyn_array When CEL expression 'math.greatest([dyn(5.4), dyn(10u), dyn(3u), dyn(-5.0), dyn(3.5)]) == 10u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # least_int_result -- @@ -402,13 +402,13 @@ Scenario: least_int_result/binary_same_args Scenario: least_int_result/binary_with_decimal When CEL expression 'math.least(1, 1.0) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/binary_with_uint When CEL expression 'math.least(1, 1u) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/binary_first_arg_least @@ -450,67 +450,67 @@ Scenario: least_int_result/binary_second_arg_int_min Scenario: least_int_result/ternary_same_args When CEL expression 'math.least(1, 1, 1) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/ternary_with_decimal When CEL expression 'math.least(1, 1.0, 1.0) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/ternary_with_uint When CEL expression 'math.least(1, 1u, 1u) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/ternary_first_arg_least When CEL expression 'math.least(0, 1, 3) == 0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/ternary_third_arg_least When CEL expression 'math.least(1, 3, 0) == 0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/ternary_with_negatives When CEL expression 'math.least(-1, -2, -3) == -3' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/ternary_int_max When CEL expression 'math.least(9223372036854775807, 1, 5) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/ternary_int_min When CEL expression 'math.least(-9223372036854775808, -1, -5) == -9223372036854775808' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/quaternary_mixed When CEL expression 'math.least(5.4, 10, 3u, -5.0, 9223372036854775807) == -5.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/quaternary_mixed_array When CEL expression 'math.least([5.4, 10, 3u, -5.0, 3.5]) == -5.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_int_result/quaternary_mixed_dyn_array When CEL expression 'math.least([dyn(5.4), dyn(10), dyn(3u), dyn(-5.0), dyn(3.5)]) == -5.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # least_double_result -- @@ -537,13 +537,13 @@ Scenario: least_double_result/binary_same_args Scenario: least_double_result/binary_with_int When CEL expression 'math.least(1.0, 1) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/binary_with_uint When CEL expression 'math.least(1, 1u) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/binary_first_arg_least @@ -585,67 +585,67 @@ Scenario: least_double_result/binary_second_arg_double_min Scenario: least_double_result/ternary_same_args When CEL expression 'math.least(1.5, 1.5, 1.5) == 1.5' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/ternary_with_int When CEL expression 'math.least(1.0, 1, 1) == 1.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/ternary_with_uint When CEL expression 'math.least(1.0, 1u, 1u) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/ternary_first_arg_least When CEL expression 'math.least(0.5, 1.5, 3.5) == 0.5' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/ternary_third_arg_least When CEL expression 'math.least(1.5, 3.5, 0.5) == 0.5' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/ternary_with_negatives When CEL expression 'math.least(-1.5, -2.5, -3.5) == -3.5' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/ternary_double_max When CEL expression 'math.least(1.797693e308, 1, 5) == 1' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/ternary_double_min When CEL expression 'math.least(-1.797693e308, -1, -5) == -1.797693e308' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/quaternary_mixed When CEL expression 'math.least(5.4, 10, 3u, -5.0, 1.797693e308) == -5.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/quaternary_mixed_array When CEL expression 'math.least([5.4, 10.5, 3u, -5.0, 3.5]) == -5.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_double_result/quaternary_mixed_dyn_array When CEL expression 'math.least([dyn(5.4), dyn(10.5), dyn(3u), dyn(-5.0), dyn(3.5)]) == -5.0' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # least_uint_result -- @@ -666,13 +666,13 @@ Scenario: least_uint_result/binary_same_args Scenario: least_uint_result/binary_with_decimal When CEL expression 'math.least(1u, 1.0) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_uint_result/binary_with_int When CEL expression 'math.least(1u, 1) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_uint_result/binary_first_arg_least @@ -702,55 +702,55 @@ Scenario: least_uint_result/binary_second_arg_uint_max Scenario: least_uint_result/ternary_same_args When CEL expression 'math.least(1u, 1u, 1u) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_uint_result/ternary_with_decimal When CEL expression 'math.least(1u, 1.0, 1.0) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_uint_result/ternary_with_int When CEL expression 'math.least(1u, 1, 1) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_uint_result/ternary_first_arg_least When CEL expression 'math.least(1u, 10u, 3u) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_uint_result/ternary_third_arg_least When CEL expression 'math.least(10u, 3u, 1u) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_uint_result/ternary_uint_max When CEL expression 'math.least(18446744073709551615u, 1u, 5u) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_uint_result/quaternary_mixed When CEL expression 'math.least(5.4, 10, 3u, 1u, 18446744073709551615u) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_uint_result/quaternary_mixed_array When CEL expression 'math.least([5.4, 10u, 3u, 1u, 3.5]) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: least_uint_result/quaternary_mixed_dyn_array When CEL expression 'math.least([dyn(5.4), dyn(10u), dyn(3u), dyn(1u), dyn(3.5)]) == 1u' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # ceil -- @@ -829,7 +829,7 @@ Scenario: round/positive_up Scenario: round/nan When CEL expression 'math.isNaN(math.round(0.0/0.0))' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) Scenario: round/dyn_error @@ -855,7 +855,7 @@ Scenario: trunc/positive Scenario: trunc/nan When CEL expression 'math.isNaN(math.trunc(0.0/0.0))' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) Scenario: trunc/dyn_error @@ -963,13 +963,13 @@ Scenario: sign/dyn_error Scenario: isNaN/true When CEL expression 'math.isNaN(0.0/0.0)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: isNaN/false When CEL expression '!math.isNaN(1.0/0.0)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) Scenario: isNaN/dyn_error @@ -983,13 +983,13 @@ Scenario: isNaN/dyn_error Scenario: isInf/true When CEL expression 'math.isInf(1.0/0.0)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: isInf/false When CEL expression '!math.isInf(0.0/0.0)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) Scenario: isInf/dyn_error @@ -1003,19 +1003,19 @@ Scenario: isInf/dyn_error Scenario: isFinite/true When CEL expression 'math.isFinite(1.0/1.5)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: isFinite/false_nan When CEL expression '!math.isFinite(0.0/0.0)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: isFinite/false_inf When CEL expression '!math.isFinite(-1.0/0.0)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) Scenario: isFinite/dyn_error diff --git a/features/namespace.feature b/features/namespace.feature index 77ece74..99a2a5c 100644 --- a/features/namespace.feature +++ b/features/namespace.feature @@ -19,8 +19,8 @@ Scenario: namespace/self_eval_container_lookup Given type_env parameter "x.y" is celpy.celtypes.BoolType and type_env parameter "y" is celpy.celtypes.StringType - and bindings parameter "y" is celpy.celtypes.StringType(source='false') and bindings parameter "x.y" is celpy.celtypes.BoolType(source=True) + and bindings parameter "y" is celpy.celtypes.StringType(source='false') and container is 'x' When CEL expression 'y' is evaluated Then value is celpy.celtypes.BoolType(source=True) @@ -30,9 +30,120 @@ Scenario: namespace/self_eval_container_lookup_unchecked Given disable_check parameter is True and type_env parameter "x.y" is celpy.celtypes.BoolType and type_env parameter "y" is celpy.celtypes.BoolType - and bindings parameter "y" is celpy.celtypes.BoolType(source=False) and bindings parameter "x.y" is celpy.celtypes.BoolType(source=True) + and bindings parameter "y" is celpy.celtypes.BoolType(source=False) and container is 'x' When CEL expression 'y' is evaluated Then value is celpy.celtypes.BoolType(source=True) + +# namespace_shadowing -- Variable shadowing in comprehensions + +@wip +Scenario: namespace_shadowing/basic + + Given type_env parameter "com.example.y" is celpy.celtypes.BoolType + and type_env parameter "y" is celpy.celtypes.StringType + and bindings parameter "y" is celpy.celtypes.StringType(source='string') + and bindings parameter "com.example.y" is celpy.celtypes.BoolType(source=True) + and container is 'com.example' + When CEL expression 'y' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: namespace_shadowing/disambiguation + + Given type_env parameter "com.example.y" is celpy.celtypes.StringType + and type_env parameter "y" is celpy.celtypes.StringType + and bindings parameter "y" is celpy.celtypes.StringType(source='y') + and bindings parameter "com.example.y" is celpy.celtypes.StringType(source='com.example.y') + and container is 'com.example' + When CEL expression '.y' is evaluated + Then value is celpy.celtypes.StringType(source='y') + +@wip +Scenario: namespace_shadowing/comprehension_shadowing + + Given type_env parameter "com.example.y" is celpy.celtypes.IntType + and bindings parameter "com.example.y" is celpy.celtypes.IntType(source=42) + and container is 'com.example' + When CEL expression '[0].exists(y, y == 0)' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: namespace_shadowing/comprehension_shadowing_disambiguation + + Given type_env parameter "y" is celpy.celtypes.StringType + and bindings parameter "y" is celpy.celtypes.StringType(source='y') + and container is 'com.example' + When CEL expression "['compre'].exists(y, .y == 'y')" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: namespace_shadowing/comprehension_shadowing_parse_only + + Given disable_check parameter is True + and type_env parameter "com.example.y" is celpy.celtypes.IntType + and bindings parameter "com.example.y" is celpy.celtypes.IntType(source=42) + and container is 'com.example' + When CEL expression '[0].exists(y, y == 0)' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: namespace_shadowing/comprehension_shadowing_selector + + Given type_env parameter "y.z" is celpy.celtypes.IntType + and bindings parameter "y.z" is celpy.celtypes.IntType(source=42) + When CEL expression "[{'z': 0}].exists(y, y.z == 0)" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: namespace_shadowing/comprehension_shadowing_selector_parse_only + + Given disable_check parameter is True + and type_env parameter "y.z" is celpy.celtypes.IntType + and bindings parameter "y.z" is celpy.celtypes.IntType(source=42) + When CEL expression "[{'z': 0}].exists(y, y.z == 0)" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: namespace_shadowing/comprehension_shadowing_namespaced_selector + + Given type_env parameter "com.example.y.z" is celpy.celtypes.IntType + and bindings parameter "com.example.y.z" is celpy.celtypes.IntType(source=42) + and container is 'com.example' + When CEL expression "[{'z': 0}].exists(y, y.z == 0)" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: namespace_shadowing/comprehension_shadowing_namespaced_selector_parse_only + + Given disable_check parameter is True + and type_env parameter "com.example.y.z" is celpy.celtypes.IntType + and bindings parameter "com.example.y.z" is celpy.celtypes.IntType(source=42) + and container is 'com.example' + When CEL expression "[{'z': 0}].exists(y, y.z == 0)" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: namespace_shadowing/comprehension_shadowing_namespaced_selector_disambiguation + + Given type_env parameter "com.example.y.z" is celpy.celtypes.StringType + and type_env parameter "y.z" is celpy.celtypes.StringType + and bindings parameter "com.example.y.z" is celpy.celtypes.StringType(source='com.example.y.z') + and bindings parameter "y.z" is celpy.celtypes.StringType(source='y.z') + and container is 'com.example' + When CEL expression "[{'z': 'compre'}].exists(y, .y.z == 'y.z')" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: namespace_shadowing/comprehension_shadowing_nesting + + Given type_env parameter "com.example.y" is celpy.celtypes.IntType + and type_env parameter "y" is celpy.celtypes.IntType + and bindings parameter "y" is celpy.celtypes.IntType(source=42) + and bindings parameter "com.example.y" is celpy.celtypes.IntType(source=42) + and container is 'com.example' + When CEL expression '[1].exists(y, [0].exists(y, y == 0))' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + diff --git a/features/namespace.textproto b/features/namespace.textproto index 15cc3ac..4cacd1a 100644 --- a/features/namespace.textproto +++ b/features/namespace.textproto @@ -11,7 +11,7 @@ section { expr: "x.y" value: { bool_value: true } type_env: { - name: "x.y", + name: "x.y" ident: { type: { primitive: BOOL } } } bindings: { @@ -28,11 +28,11 @@ section { expr: "y" container: "x" type_env: { - name: "x.y", + name: "x.y" ident: { type: { primitive: BOOL } } } type_env: { - name: "y", + name: "y" ident: { type: { primitive: STRING } } } bindings: { @@ -50,11 +50,11 @@ section { expr: "y" container: "x" type_env: { - name: "x.y", + name: "x.y" ident: { type: { primitive: BOOL } } } type_env: { - name: "y", + name: "y" ident: { type: { primitive: BOOL } } } bindings: { @@ -65,7 +65,198 @@ section { key: "y" value: { value: { bool_value: false } } } - disable_check: true ## ensure unchecked ASTs resolve the same as checked ASTs + disable_check: true ## ensure unchecked ASTs resolve the same as checked ASTs + value: { bool_value: true } + } +} +section { + name: "namespace_shadowing" + description: "Variable shadowing in comprehensions" + test { + name: "basic" + expr: "y" + container: "com.example" + type_env: { + name: "com.example.y" + ident: { type: { primitive: BOOL } } + } + type_env: { + name: "y" + ident: { type: { primitive: STRING } } + } + bindings: { + key: "com.example.y" + value: { value: { bool_value: true } } + } + bindings: { + key: "y" + value: { value: { string_value: "string" } } + } + value: { bool_value: true } + } + test { + name: "disambiguation" + expr: ".y" + container: "com.example" + type_env: { + name: "com.example.y" + ident: { type: { primitive: STRING } } + } + type_env: { + name: "y" + ident: { type: { primitive: STRING } } + } + bindings: { + key: "com.example.y" + value: { value: { string_value: "com.example.y" } } + } + bindings: { + key: "y" + value: { value: { string_value: "y" } } + } + value: { string_value: "y" } + } + test { + name: "comprehension_shadowing" + expr: "[0].exists(y, y == 0)" + container: "com.example" + type_env: { + name: "com.example.y" + ident: { type: { primitive: INT64 } } + } + bindings: { + key: "com.example.y" + value: { value: { int64_value: 42 } } + } + value: { bool_value: true } + } + test { + name: "comprehension_shadowing_disambiguation" + expr: "['compre'].exists(y, .y == 'y')" + container: "com.example" + type_env: { + name: "y" + ident: { type: { primitive: STRING } } + } + bindings: { + key: "y" + value: { value: { string_value: "y" } } + } + value: { bool_value: true } + } + test { + name: "comprehension_shadowing_parse_only" + expr: "[0].exists(y, y == 0)" + container: "com.example" + type_env: { + name: "com.example.y" + ident: { type: { primitive: INT64 } } + } + bindings: { + key: "com.example.y" + value: { value: { int64_value: 42 } } + } + disable_check: true + value: { bool_value: true } + } + test { + name: "comprehension_shadowing_selector" + expr: "[{'z': 0}].exists(y, y.z == 0)" + type_env: { + name: "y.z" + ident: { type: { primitive: INT64 } } + } + bindings: { + key: "y.z" + value: { value: { int64_value: 42 } } + } + value: { bool_value: true } + } + test { + name: "comprehension_shadowing_selector_parse_only" + expr: "[{'z': 0}].exists(y, y.z == 0)" + type_env: { + name: "y.z" + ident: { type: { primitive: INT64 } } + } + bindings: { + key: "y.z" + value: { value: { int64_value: 42 } } + } + disable_check: true + value: { bool_value: true } + } + test { + name: "comprehension_shadowing_namespaced_selector" + expr: "[{'z': 0}].exists(y, y.z == 0)" + container: "com.example" + type_env: { + name: "com.example.y.z" + ident: { type: { primitive: INT64 } } + } + bindings: { + key: "com.example.y.z" + value: { value: { int64_value: 42 } } + } + value: { bool_value: true } + } + test { + name: "comprehension_shadowing_namespaced_selector_parse_only" + expr: "[{'z': 0}].exists(y, y.z == 0)" + container: "com.example" + type_env: { + name: "com.example.y.z" + ident: { type: { primitive: INT64 } } + } + bindings: { + key: "com.example.y.z" + value: { value: { int64_value: 42 } } + } + disable_check: true + value: { bool_value: true } + } + test { + name: "comprehension_shadowing_namespaced_selector_disambiguation" + expr: "[{'z': 'compre'}].exists(y, .y.z == 'y.z')" + container: "com.example" + type_env: { + name: "com.example.y.z" + ident: { type: { primitive: STRING } } + } + type_env: { + name: "y.z" + ident: { type: { primitive: STRING } } + } + bindings: { + key: "com.example.y.z" + value: { value: { string_value: "com.example.y.z" } } + } + bindings: { + key: "y.z" + value: { value: { string_value: "y.z" } } + } + value: { bool_value: true } + } + test { + name: "comprehension_shadowing_nesting" + expr: "[1].exists(y, [0].exists(y, y == 0))" + container: "com.example" + type_env: { + name: "com.example.y" + ident: { type: { primitive: INT64 } } + } + type_env: { + name: "y" + ident: { type: { primitive: INT64 } } + } + bindings: { + key: "com.example.y" + value: { value: { int64_value: 42 } } + } + bindings: { + key: "y" + value: { value: { int64_value: 42 } } + } value: { bool_value: true } } } diff --git a/features/parse.feature b/features/parse.feature index 0698e57..88f0f15 100644 --- a/features/parse.feature +++ b/features/parse.feature @@ -135,6 +135,876 @@ Scenario: repeat/message_literal Then value is celpy.celtypes.IntType(source=30) +# string_literals -- Check that string literals are properly parsed + +@wip +Scenario: string_literals/single_quoted + + When CEL expression "'hello'" is evaluated + Then value is celpy.celtypes.StringType(source='hello') + +@wip +Scenario: string_literals/double_quoted + + When CEL expression '"hello"' is evaluated + Then value is celpy.celtypes.StringType(source='hello') + +@wip +Scenario: string_literals/triple_single_quoted + + When CEL expression "'''hello'''" is evaluated + Then value is celpy.celtypes.StringType(source='hello') + +@wip +Scenario: string_literals/triple_double_quoted + + When CEL expression '"""hello"""' is evaluated + Then value is celpy.celtypes.StringType(source='hello') + +@wip +Scenario: string_literals/single_quoted_escaped_punctuation + + When CEL expression '\' \\\\ \\? \\" \\\' \\` \'' is evaluated + Then value is celpy.celtypes.StringType(source=' \\ ? " \' ` ') + +@wip +Scenario: string_literals/double_quoted_escaped_punctuation + + When CEL expression '" \\\\ \\? \\" \\\' \\` "' is evaluated + Then value is celpy.celtypes.StringType(source=' \\ ? " \' ` ') + +@wip +Scenario: string_literals/triple_single_quoted_escaped_punctuation + + When CEL expression '\'\'\' \\\\ \\? \\" \\\' \\` \'\'\'' is evaluated + Then value is celpy.celtypes.StringType(source=' \\ ? " \' ` ') + +@wip +Scenario: string_literals/triple_double_quoted_escaped_punctuation + + When CEL expression '""" \\\\ \\? \\" \\\' \\` """' is evaluated + Then value is celpy.celtypes.StringType(source=' \\ ? " \' ` ') + +@wip +Scenario: string_literals/triple_single_quoted_unescaped_punctuation + + When CEL expression '\'\'\' ? " \' ` \'\'\'' is evaluated + Then value is celpy.celtypes.StringType(source=' ? " \' ` ') + +@wip +Scenario: string_literals/triple_double_quoted_unescaped_punctuation + + When CEL expression '""" ? " \' ` """' is evaluated + Then value is celpy.celtypes.StringType(source=' ? " \' ` ') + +@wip +Scenario: string_literals/single_quoted_escaped_special_control_characters + + When CEL expression "' \\a \\b \\f \\t \\v '" is evaluated + Then value is celpy.celtypes.StringType(source=' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: string_literals/double_quoted_escaped_special_control_characters + + When CEL expression '" \\a \\b \\f \\t \\v "' is evaluated + Then value is celpy.celtypes.StringType(source=' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: string_literals/single_quoted_unescaped_special_control_characters + + When CEL expression "' \x07 \x08 \x0c \t \x0b '" is evaluated + Then value is celpy.celtypes.StringType(source=' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: string_literals/double_quoted_unescaped_special_control_characters + + When CEL expression '" \x07 \x08 \x0c \t \x0b "' is evaluated + Then value is celpy.celtypes.StringType(source=' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: string_literals/triple_single_quoted_escaped_special_control_characters + + When CEL expression "''' \\a \\b \\f \\t \\v '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: string_literals/triple_double_quoted_escaped_special_control_characters + + When CEL expression '""" \\a \\b \\f \\t \\v """' is evaluated + Then value is celpy.celtypes.StringType(source=' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: string_literals/triple_single_quoted_unescaped_special_control_characters + + When CEL expression "''' \x07 \x08 \x0c \t \x0b '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: string_literals/triple_double_quoted_unescaped_special_control_characters + + When CEL expression '""" \x07 \x08 \x0c \t \x0b """' is evaluated + Then value is celpy.celtypes.StringType(source=' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: string_literals/single_quoted_escaped_line_feed + + When CEL expression "' \\n '" is evaluated + Then value is celpy.celtypes.StringType(source=' \n ') + +@wip +Scenario: string_literals/double_quoted_escaped_line_feed + + When CEL expression '" \\n "' is evaluated + Then value is celpy.celtypes.StringType(source=' \n ') + +@wip +Scenario: string_literals/triple_single_quoted_escaped_line_feed + + When CEL expression "''' \\n '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \n ') + +@wip +Scenario: string_literals/triple_double_quoted_escaped_line_feed + + When CEL expression '""" \\n """' is evaluated + Then value is celpy.celtypes.StringType(source=' \n ') + +@wip +Scenario: string_literals/triple_single_quoted_unescaped_line_feed + + When CEL expression "''' \n '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \n ') + +@wip +Scenario: string_literals/triple_double_quoted_unescaped_line_feed + + When CEL expression '""" \n """' is evaluated + Then value is celpy.celtypes.StringType(source=' \n ') + +@wip +Scenario: string_literals/single_quoted_escaped_carriage_return + + When CEL expression "' \\r '" is evaluated + Then value is celpy.celtypes.StringType(source=' \r ') + +@wip +Scenario: string_literals/double_quoted_escaped_carriage_return + + When CEL expression '" \\r "' is evaluated + Then value is celpy.celtypes.StringType(source=' \r ') + +@wip +Scenario: string_literals/triple_single_quoted_escaped_carriage_return + + When CEL expression "''' \\r '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \r ') + +@wip +Scenario: string_literals/triple_double_quoted_escaped_carriage_return + + When CEL expression '""" \\r """' is evaluated + Then value is celpy.celtypes.StringType(source=' \r ') + +@wip +Scenario: string_literals/single_quoted_escaped_windows_line_end + + When CEL expression "' \\r\\n '" is evaluated + Then value is celpy.celtypes.StringType(source=' \r\n ') + +@wip +Scenario: string_literals/double_quoted_escaped_windows_line_end + + When CEL expression '" \\r\\n "' is evaluated + Then value is celpy.celtypes.StringType(source=' \r\n ') + +@wip +Scenario: string_literals/triple_single_quoted_escaped_windows_line_end + + When CEL expression "''' \\r\\n '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \r\n ') + +@wip +Scenario: string_literals/triple_double_quoted_escaped_windows_line_end + + When CEL expression '""" \\r\\n """' is evaluated + Then value is celpy.celtypes.StringType(source=' \r\n ') + +@wip +Scenario: string_literals/single_quoted_escaped_all_control_characters + + When CEL expression "' \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F '" is evaluated + Then value is celpy.celtypes.StringType(source=' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: string_literals/double_quoted_escaped_all_control_characters + + When CEL expression '" \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F "' is evaluated + Then value is celpy.celtypes.StringType(source=' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: string_literals/single_quoted_unescaped_all_control_characters + + When CEL expression "' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f '" is evaluated + Then value is celpy.celtypes.StringType(source=' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: string_literals/double_quoted_unescaped_all_control_characters + + When CEL expression '" \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f "' is evaluated + Then value is celpy.celtypes.StringType(source=' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: string_literals/triple_single_quoted_escaped_all_control_characters + + When CEL expression "''' \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: string_literals/triple_double_quoted_escaped_all_control_characters + + When CEL expression '""" \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F """' is evaluated + Then value is celpy.celtypes.StringType(source=' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: string_literals/triple_single_quoted_unescaped_all_control_characters + + When CEL expression "''' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: string_literals/triple_double_quoted_unescaped_all_control_characters + + When CEL expression '""" \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f """' is evaluated + Then value is celpy.celtypes.StringType(source=' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: string_literals/single_quoted_octal_escapes + + When CEL expression "' \\000 \\012 \\177 '" is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/double_quoted_octal_escapes + + When CEL expression '" \\000 \\012 \\177 "' is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/triple_single_quoted_octal_escapes + + When CEL expression "''' \\000 \\012 \\177 '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/triple_double_quoted_octal_escapes + + When CEL expression '""" \\000 \\012 \\177 """' is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/single_quoted_lower_x_escapes + + When CEL expression "' \\x00 \\x0A \\x7F '" is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/double_quoted_lower_x_escapes + + When CEL expression '" \\x00 \\x0A \\x7F "' is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/triple_single_quoted_lower_x_escapes + + When CEL expression "''' \\x00 \\x0A \\x7F '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/triple_double_quoted_lower_x_escapes + + When CEL expression '""" \\x00 \\x0A \\x7F """' is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/single_quoted_upper_x_escapes + + When CEL expression "' \\X00 \\X0A \\X7F '" is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/double_quoted_upper_x_escapes + + When CEL expression '" \\X00 \\X0A \\X7F "' is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/triple_single_quoted_upper_x_escapes + + When CEL expression "''' \\X00 \\X0A \\X7F '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/triple_double_quoted_upper_x_escapes + + When CEL expression '""" \\X00 \\X0A \\X7F """' is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f ') + +@wip +Scenario: string_literals/single_quoted_lower_u_escapes + + When CEL expression "' \\u0000 \\u000A \\u007F \\u0100 \\uFFFB '" is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f Ā \ufffb ') + +@wip +Scenario: string_literals/double_quoted_lower_u_escapes + + When CEL expression '" \\u0000 \\u000A \\u007F \\u0100 \\uFFFB "' is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f Ā \ufffb ') + +@wip +Scenario: string_literals/triple_single_quoted_lower_u_escapes + + When CEL expression "''' \\u0000 \\u000A \\u007F \\u0100 \\uFFFB '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f Ā \ufffb ') + +@wip +Scenario: string_literals/triple_double_quoted_lower_u_escapes + + When CEL expression '""" \\u0000 \\u000A \\u007F \\u0100 \\uFFFB """' is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f Ā \ufffb ') + +@wip +Scenario: string_literals/single_quoted_upper_u_escapes + + When CEL expression "' \\U00000000 \\U0000000A \\U0000007F \\U00000100 \\U0000FFFB \\U00010000 \\U0001F62C '" is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f Ā \ufffb 𐀀 😬 ') + +@wip +Scenario: string_literals/double_quoted_upper_u_escapes + + When CEL expression '" \\U00000000 \\U0000000A \\U0000007F \\U00000100 \\U0000FFFB \\U00010000 \\U0001F62C "' is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f Ā \ufffb 𐀀 😬 ') + +@wip +Scenario: string_literals/triple_single_quoted_upper_u_escapes + + When CEL expression "''' \\U00000000 \\U0000000A \\U0000007F \\U00000100 \\U0000FFFB \\U00010000 \\U0001F62C '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f Ā \ufffb 𐀀 😬 ') + +@wip +Scenario: string_literals/triple_double_quoted_upper_u_escapes + + When CEL expression '""" \\U00000000 \\U0000000A \\U0000007F \\U00000100 \\U0000FFFB \\U00010000 \\U0001F62C """' is evaluated + Then value is celpy.celtypes.StringType(source=' \x00 \n \x7f Ā \ufffb 𐀀 😬 ') + +@wip +Scenario: string_literals/mixed_case_hex_single_quoted_escapes + + When CEL expression "' \\x4a \\x4B \\X4c \\X4D \\u01aB \\U000001aB '" is evaluated + Then value is celpy.celtypes.StringType(source=' J K L M ƫ ƫ ') + +@wip +Scenario: string_literals/mixed_case_hex_double_quoted_escapes + + When CEL expression '" \\x4a \\x4B \\X4c \\X4D \\u01aB \\U000001aB "' is evaluated + Then value is celpy.celtypes.StringType(source=' J K L M ƫ ƫ ') + +@wip +Scenario: string_literals/mixed_case_hex_triple_single_quoted_escapes + + When CEL expression "''' \\x4a \\x4B \\X4c \\X4D \\u01aB \\U000001aB '''" is evaluated + Then value is celpy.celtypes.StringType(source=' J K L M ƫ ƫ ') + +@wip +Scenario: string_literals/mixed_case_hex_triple_double_quoted_escapes + + When CEL expression '""" \\x4a \\x4B \\X4c \\X4D \\u01aB \\U000001aB """' is evaluated + Then value is celpy.celtypes.StringType(source=' J K L M ƫ ƫ ') + +@wip +Scenario: string_literals/unassigned_code_point_single_quoted_escapes + + When CEL expression "' \\U00088888 '" is evaluated + Then value is celpy.celtypes.StringType(source=' \U00088888 ') + +@wip +Scenario: string_literals/unassigned_code_point_double_quoted_escapes + + When CEL expression '" \\U00088888 "' is evaluated + Then value is celpy.celtypes.StringType(source=' \U00088888 ') + +@wip +Scenario: string_literals/unassigned_code_point_triple_single_quoted_escapes + + When CEL expression "''' \\U00088888 '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \U00088888 ') + +@wip +Scenario: string_literals/unassigned_code_point_triple_double_quoted_escapes + + When CEL expression '""" \\U00088888 """' is evaluated + Then value is celpy.celtypes.StringType(source=' \U00088888 ') + +@wip +Scenario: string_literals/unassigned_code_point_single_quoted_unescaped + + When CEL expression "' \U00088888 '" is evaluated + Then value is celpy.celtypes.StringType(source=' \U00088888 ') + +@wip +Scenario: string_literals/unassigned_code_point_double_quoted_unescaped + + When CEL expression '" \U00088888 "' is evaluated + Then value is celpy.celtypes.StringType(source=' \U00088888 ') + +@wip +Scenario: string_literals/unassigned_code_point_triple_single_quoted_unescaped + + When CEL expression "''' \U00088888 '''" is evaluated + Then value is celpy.celtypes.StringType(source=' \U00088888 ') + +@wip +Scenario: string_literals/unassigned_code_point_triple_double_quoted_unescaped + + When CEL expression '""" \U00088888 """' is evaluated + Then value is celpy.celtypes.StringType(source=' \U00088888 ') + +@wip +Scenario: string_literals/raw_single_quoted_escapes + + When CEL expression 'r\' \\\\ \\\\? \\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 \'' is evaluated + Then value is celpy.celtypes.StringType(source=' \\\\ \\\\? \\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + +@wip +Scenario: string_literals/raw_double_quoted_escapes + + When CEL expression 'r" \\\\ \\\\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 "' is evaluated + Then value is celpy.celtypes.StringType(source=" \\\\ \\\\? \\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ") + +@wip +Scenario: string_literals/raw_triple_single_quoted_escapes + + When CEL expression 'r\'\'\' \\\\ \\\\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 \'\'\'' is evaluated + Then value is celpy.celtypes.StringType(source=' \\\\ \\\\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + +@wip +Scenario: string_literals/raw_triple_double_quoted_escapes + + When CEL expression 'r""" \\\\ \\\\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 """' is evaluated + Then value is celpy.celtypes.StringType(source=' \\\\ \\\\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + +@wip +Scenario: string_literals/upper_raw_single_quoted_escapes + + When CEL expression 'R\' \\\\ \\\\? \\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 \'' is evaluated + Then value is celpy.celtypes.StringType(source=' \\\\ \\\\? \\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + +@wip +Scenario: string_literals/upper_raw_double_quoted_escapes + + When CEL expression 'R" \\\\ \\\\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 "' is evaluated + Then value is celpy.celtypes.StringType(source=" \\\\ \\\\? \\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ") + +@wip +Scenario: string_literals/upper_raw_triple_single_quoted_escapes + + When CEL expression 'R\'\'\' \\\\ \\\\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 \'\'\'' is evaluated + Then value is celpy.celtypes.StringType(source=' \\\\ \\\\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + +@wip +Scenario: string_literals/upper_raw_triple_double_quoted_escapes + + When CEL expression 'R""" \\\\ \\\\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 """' is evaluated + Then value is celpy.celtypes.StringType(source=' \\\\ \\\\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + + +# bytes_literals -- Check that bytes literals are properly parsed + +@wip +Scenario: bytes_literals/single_quoted + + When CEL expression "b'hello'" is evaluated + Then value is celpy.celtypes.BytesType(source=b'hello') + +@wip +Scenario: bytes_literals/double_quoted + + When CEL expression 'b"hello"' is evaluated + Then value is celpy.celtypes.BytesType(source=b'hello') + +@wip +Scenario: bytes_literals/triple_single_quoted + + When CEL expression "b'''hello'''" is evaluated + Then value is celpy.celtypes.BytesType(source=b'hello') + +@wip +Scenario: bytes_literals/triple_double_quoted + + When CEL expression 'b"""hello"""' is evaluated + Then value is celpy.celtypes.BytesType(source=b'hello') + +@wip +Scenario: bytes_literals/single_quoted_escaped_punctuation + + When CEL expression 'b\' \\\\ \\\\? \\" \\\' \\` \'' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\ \\? " \' ` ') + +@wip +Scenario: bytes_literals/double_quoted_escaped_punctuation + + When CEL expression 'b" \\\\ \\\\? \\" \\\' \\` "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\ \\? " \' ` ') + +@wip +Scenario: bytes_literals/triple_single_quoted_escaped_punctuation + + When CEL expression 'b\'\'\' \\\\ \\\\? \\" \\\' \\` \'\'\'' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\ \\? " \' ` ') + +@wip +Scenario: bytes_literals/triple_double_quoted_escaped_punctuation + + When CEL expression 'b""" \\\\ \\\\? \\" \\\' \\` """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\ \\? " \' ` ') + +@wip +Scenario: bytes_literals/triple_single_quoted_unescaped_punctuation + + When CEL expression 'b\'\'\' ? " \' ` \'\'\'' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\? " \' ` ') + +@wip +Scenario: bytes_literals/triple_double_quoted_unescaped_punctuation + + When CEL expression 'b""" ? " \' ` """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\? " \' ` ') + +@wip +Scenario: bytes_literals/single_quoted_escaped_special_control_characters + + When CEL expression "b' \\a \\b \\f \\t \\v '" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: bytes_literals/double_quoted_escaped_special_control_characters + + When CEL expression 'b" \\a \\b \\f \\t \\v "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: bytes_literals/single_quoted_unescaped_special_control_characters + + When CEL expression "b' \x07 \x08 \x0c \t \x0b '" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: bytes_literals/double_quoted_unescaped_special_control_characters + + When CEL expression 'b" \x07 \x08 \x0c \t \x0b "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: bytes_literals/triple_single_quoted_escaped_special_control_characters + + When CEL expression "b''' \\a \\b \\f \\t \\v '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: bytes_literals/triple_double_quoted_escaped_special_control_characters + + When CEL expression 'b""" \\a \\b \\f \\t \\v """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: bytes_literals/triple_single_quoted_unescaped_special_control_characters + + When CEL expression "b''' \x07 \x08 \x0c \t \x0b '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: bytes_literals/triple_double_quoted_unescaped_special_control_characters + + When CEL expression 'b""" \x07 \x08 \x0c \t \x0b """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x07 \x08 \x0c \t \x0b ') + +@wip +Scenario: bytes_literals/single_quoted_escaped_line_feed + + When CEL expression "b' \\n '" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \n ') + +@wip +Scenario: bytes_literals/double_quoted_escaped_line_feed + + When CEL expression 'b" \\n "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \n ') + +@wip +Scenario: bytes_literals/triple_single_quoted_escaped_line_feed + + When CEL expression "b''' \\n '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \n ') + +@wip +Scenario: bytes_literals/triple_double_quoted_escaped_line_feed + + When CEL expression 'b""" \\n """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \n ') + +@wip +Scenario: bytes_literals/triple_single_quoted_unescaped_line_feed + + When CEL expression "b''' \n '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \n ') + +@wip +Scenario: bytes_literals/triple_double_quoted_unescaped_line_feed + + When CEL expression 'b""" \n """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \n ') + +@wip +Scenario: bytes_literals/single_quoted_escaped_carriage_return + + When CEL expression "b' \\r '" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \r ') + +@wip +Scenario: bytes_literals/double_quoted_escaped_carriage_return + + When CEL expression 'b" \\r "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \r ') + +@wip +Scenario: bytes_literals/triple_single_quoted_escaped_carriage_return + + When CEL expression "b''' \\r '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \r ') + +@wip +Scenario: bytes_literals/triple_double_quoted_escaped_carriage_return + + When CEL expression 'b""" \\r """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \r ') + +@wip +Scenario: bytes_literals/single_quoted_escaped_windows_line_end + + When CEL expression "b' \\r\\n '" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \r\n ') + +@wip +Scenario: bytes_literals/double_quoted_escaped_windows_line_end + + When CEL expression 'b" \\r\\n "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \r\n ') + +@wip +Scenario: bytes_literals/triple_single_quoted_escaped_windows_line_end + + When CEL expression "b''' \\r\\n '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \r\n ') + +@wip +Scenario: bytes_literals/triple_double_quoted_escaped_windows_line_end + + When CEL expression 'b""" \\r\\n """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \r\n ') + +@wip +Scenario: bytes_literals/single_quoted_escaped_all_control_characters + + When CEL expression "b' \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F '" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: bytes_literals/double_quoted_escaped_all_control_characters + + When CEL expression 'b" \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: bytes_literals/single_quoted_unescaped_all_control_characters + + When CEL expression "b' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f '" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: bytes_literals/double_quoted_unescaped_all_control_characters + + When CEL expression 'b" \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: bytes_literals/triple_single_quoted_escaped_all_control_characters + + When CEL expression "b''' \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: bytes_literals/triple_double_quoted_escaped_all_control_characters + + When CEL expression 'b""" \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: bytes_literals/triple_single_quoted_unescaped_all_control_characters + + When CEL expression "b''' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: bytes_literals/triple_double_quoted_unescaped_all_control_characters + + When CEL expression 'b""" \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \t \x0b \x0c \x0e \x0f \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f \x7f ') + +@wip +Scenario: bytes_literals/single_quoted_octal_escapes + + When CEL expression "b' \\000 \\012 \\177 \\377 '" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/double_quoted_octal_escapes + + When CEL expression 'b" \\000 \\012 \\177 \\377 "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/triple_single_quoted_octal_escapes + + When CEL expression "b''' \\000 \\012 \\177 \\377 '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/triple_double_quoted_octal_escapes + + When CEL expression 'b""" \\000 \\012 \\177 \\377 """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/single_quoted_lower_x_escapes + + When CEL expression "b' \\x00 \\x0A \\x7F \\xFF '" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/double_quoted_lower_x_escapes + + When CEL expression 'b" \\x00 \\x0A \\x7F \\xFF "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/triple_single_quoted_lower_x_escapes + + When CEL expression "b''' \\x00 \\x0A \\x7F \\xFF '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/triple_double_quoted_lower_x_escapes + + When CEL expression 'b""" \\x00 \\x0A \\x7F \\xFF """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/single_quoted_upper_x_escapes + + When CEL expression "b' \\X00 \\X0A \\X7F \\XFF '" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/double_quoted_upper_x_escapes + + When CEL expression 'b" \\X00 \\X0A \\X7F \\XFF "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/triple_single_quoted_upper_x_escapes + + When CEL expression "b''' \\X00 \\X0A \\X7F \\XFF '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/triple_double_quoted_upper_x_escapes + + When CEL expression 'b""" \\X00 \\X0A \\X7F \\XFF """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \x00 \n \x7f \xff ') + +@wip +Scenario: bytes_literals/mixed_case_hex_single_quoted_escapes + + When CEL expression "B' \\x4a \\x4B \\X4c \\X4D '" is evaluated + Then value is celpy.celtypes.BytesType(source=b' J K L M ') + +@wip +Scenario: bytes_literals/mixed_case_hex_double_quoted_escapes + + When CEL expression 'B" \\x4a \\x4B \\X4c \\X4D "' is evaluated + Then value is celpy.celtypes.BytesType(source=b' J K L M ') + +@wip +Scenario: bytes_literals/mixed_case_hex_triple_single_quoted_escapes + + When CEL expression "B''' \\x4a \\x4B \\X4c \\X4D '''" is evaluated + Then value is celpy.celtypes.BytesType(source=b' J K L M ') + +@wip +Scenario: bytes_literals/mixed_case_hex_triple_double_quoted_escapes + + When CEL expression 'B""" \\x4a \\x4B \\X4c \\X4D """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' J K L M ') + +@wip +Scenario: bytes_literals/raw_single_quoted_escapes + + When CEL expression 'br\' \\\\ \\? \\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 \'' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\\\ \\? \\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + +@wip +Scenario: bytes_literals/raw_double_quoted_escapes + + When CEL expression 'br" \\\\ \\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 "' is evaluated + Then value is celpy.celtypes.BytesType(source=b" \\\\ \\? \\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ") + +@wip +Scenario: bytes_literals/raw_triple_single_quoted_escapes + + When CEL expression 'br\'\'\' \\\\ \\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 \'\'\'' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\\\ \\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + +@wip +Scenario: bytes_literals/raw_triple_double_quoted_escapes + + When CEL expression 'br""" \\\\ \\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\\\ \\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + +@wip +Scenario: bytes_literals/upper_raw_single_quoted_escapes + + When CEL expression 'bR\' \\\\ \\? \\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 \'' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\\\ \\? \\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + +@wip +Scenario: bytes_literals/upper_raw_double_quoted_escapes + + When CEL expression 'bR" \\\\ \\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 "' is evaluated + Then value is celpy.celtypes.BytesType(source=b" \\\\ \\? \\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ") + +@wip +Scenario: bytes_literals/upper_raw_triple_single_quoted_escapes + + When CEL expression 'bR\'\'\' \\\\ \\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 \'\'\'' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\\\ \\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + +@wip +Scenario: bytes_literals/upper_raw_triple_double_quoted_escapes + + When CEL expression 'bR""" \\\\ \\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 """' is evaluated + Then value is celpy.celtypes.BytesType(source=b' \\\\ \\? \\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 ') + + # whitespace -- Check that whitespace is ignored by the grammar. @wip @@ -182,3 +1052,403 @@ Scenario: comments/new_line_terminated When CEL expression '[// @\n.// @\ncel.// @\nexpr// @\n.conformance.// @\nproto3.// @\nTestAllTypes// @\n{// @\nsingle_int64// @\n:// @\nint// @\n(// @\n17// @\n)// @\n}// @\n.// @\nsingle_int64// @\n]// @\n[// @\n0// @\n]// @\n==// @\n(// @\n18// @\n-// @\n1// @\n)// @\n&&// @\n!// @\nfalse// @\n?// @\n1// @\n:// @\n2' is evaluated Then value is celpy.celtypes.IntType(source=1) + +# selectors -- Check that reserved identifiers are permitted as selectors as long as they are not language keywords + +@wip +Scenario: selectors/as + Check that `as` can be used as a selector. + + When CEL expression "{ 'as': 1 }.as" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/break + Check that `break` can be used as a selector. + + When CEL expression "{ 'break': 1 }.break" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/const + Check that `const` can be used as a selector. + + When CEL expression "{ 'const': 1 }.const" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/continue + Check that `continue` can be used as a selector. + + When CEL expression "{ 'continue': 1 }.continue" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/else + Check that `else` can be used as a selector. + + When CEL expression "{ 'else': 1 }.else" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/for + Check that `for` can be used as a selector. + + When CEL expression "{ 'for': 1 }.for" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/function + Check that `function` can be used as a selector. + + When CEL expression "{ 'function': 1 }.function" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/if + Check that `if` can be used as a selector. + + When CEL expression "{ 'if': 1 }.if" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/import + Check that `import` can be used as a selector. + + When CEL expression "{ 'import': 1 }.import" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/let + Check that `let` can be used as a selector. + + When CEL expression "{ 'let': 1 }.let" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/loop + Check that `loop` can be used as a selector. + + When CEL expression "{ 'loop': 1 }.loop" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/package + Check that `package` can be used as a selector. + + When CEL expression "{ 'package': 1 }.package" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/namespace + Check that `namespace` can be used as a selector. + + When CEL expression "{ 'namespace': 1 }.namespace" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/return + Check that `return` can be used as a selector. + + When CEL expression "{ 'return': 1 }.return" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/var + Check that `var` can be used as a selector. + + When CEL expression "{ 'var': 1 }.var" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/void + Check that `void` can be used as a selector. + + When CEL expression "{ 'void': 1 }.void" is evaluated + Then value is celpy.celtypes.IntType(source=1) + +@wip +Scenario: selectors/while + Check that `while` can be used as a selector. + + When CEL expression "{ 'while': 1 }.while" is evaluated + Then value is celpy.celtypes.IntType(source=1) + + +# receiver_function_names -- Check that reserved identifiers are permitted as receiver function names as long as they are not language keywords + +@wip +Scenario: receiver_function_names/as + Check that `as` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.as() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/break + Check that `break` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.break() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/const + Check that `const` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.const() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/continue + Check that `continue` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.continue() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/else + Check that `else` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.else() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/for + Check that `for` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.for() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/function + Check that `function` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.function() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/if + Check that `if` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.if() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/import + Check that `import` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.import() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/let + Check that `let` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.let() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/loop + Check that `loop` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.loop() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/package + Check that `package` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.package() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/namespace + Check that `namespace` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.namespace() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/return + Check that `return` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.return() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/var + Check that `var` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.var() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/void + Check that `void` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.void() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: receiver_function_names/while + Check that `while` can be used as a receiver function. + + Given disable_check parameter is True + When CEL expression 'a.while() || true' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + + +# struct_field_names -- Check that reserved identifiers are permitted as struct field names as long as they are not language keywords + +@wip +Scenario: struct_field_names/as + Check that `as` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ as: true }' is evaluated + Then value is TestAllTypes(as=True) + +@wip +Scenario: struct_field_names/break + Check that `break` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ break: true }' is evaluated + Then value is TestAllTypes(break=True) + +@wip +Scenario: struct_field_names/const + Check that `const` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ const: true }' is evaluated + Then value is TestAllTypes(const=True) + +@wip +Scenario: struct_field_names/continue + Check that `continue` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ continue: true }' is evaluated + Then value is TestAllTypes(continue=True) + +@wip +Scenario: struct_field_names/else + Check that `else` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ else: true }' is evaluated + Then value is TestAllTypes(else=True) + +@wip +Scenario: struct_field_names/for + Check that `for` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ for: true }' is evaluated + Then value is TestAllTypes(for=True) + +@wip +Scenario: struct_field_names/function + Check that `function` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ function: true }' is evaluated + Then value is TestAllTypes(function=True) + +@wip +Scenario: struct_field_names/if + Check that `if` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ if: true }' is evaluated + Then value is TestAllTypes(if=True) + +@wip +Scenario: struct_field_names/import + Check that `import` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ import: true }' is evaluated + Then value is TestAllTypes(import=True) + +@wip +Scenario: struct_field_names/let + Check that `let` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ let: true }' is evaluated + Then value is TestAllTypes(let=True) + +@wip +Scenario: struct_field_names/loop + Check that `loop` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ loop: true }' is evaluated + Then value is TestAllTypes(loop=True) + +@wip +Scenario: struct_field_names/package + Check that `package` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ package: true }' is evaluated + Then value is TestAllTypes(package=True) + +@wip +Scenario: struct_field_names/namespace + Check that `namespace` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ namespace: true }' is evaluated + Then value is TestAllTypes(namespace=True) + +@wip +Scenario: struct_field_names/return + Check that `return` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ return: true }' is evaluated + Then value is TestAllTypes(return=True) + +@wip +Scenario: struct_field_names/var + Check that `var` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ var: true }' is evaluated + Then value is TestAllTypes(var=True) + +@wip +Scenario: struct_field_names/void + Check that `void` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ void: true }' is evaluated + Then value is TestAllTypes(void=True) + +@wip +Scenario: struct_field_names/while + Check that `while` can be used as a struct field name. + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{ while: true }' is evaluated + Then value is TestAllTypes(while=True) + diff --git a/features/parse.textproto b/features/parse.textproto index 76c0ad8..5b77b28 100644 --- a/features/parse.textproto +++ b/features/parse.textproto @@ -130,6 +130,820 @@ section { value { int64_value: 30 } } } +section { + name: "string_literals" + description: "Check that string literals are properly parsed" + test { + name: "single_quoted" + expr: "'hello'" + value: { string_value: "hello" } + } + test { + name: "double_quoted" + expr: '"hello"' + value: { string_value: "hello" } + } + test { + name: "triple_single_quoted" + expr: "'''hello'''" + value: { string_value: "hello" } + } + test { + name: "triple_double_quoted" + expr: '"""hello"""' + value: { string_value: "hello" } + } + test { + name: "single_quoted_escaped_punctuation" + # ' \\ \? \" \' \` ' + expr: "' \\\\ \\? \\\" \\\' \\` '" + value: { string_value: " \\ ? \" \' ` " } + } + test { + name: "double_quoted_escaped_punctuation" + expr: '" \\\\ \\? \\\" \\\' \\` "' + value: { string_value: " \\ ? \" \' ` " } + } + test { + name: "triple_single_quoted_escaped_punctuation" + expr: "''' \\\\ \\? \\\" \\\' \\` '''" + value: { string_value: " \\ ? \" \' ` " } + } + test { + name: "triple_double_quoted_escaped_punctuation" + expr: '""" \\\\ \\? \\\" \\\' \\` """' + value: { string_value: " \\ ? \" \' ` " } + } + test { + name: "triple_single_quoted_unescaped_punctuation" + expr: "''' ? \" \' ` '''" + value: { string_value: " ? \" \' ` " } + } + test { + name: "triple_double_quoted_unescaped_punctuation" + expr: '""" ? \" \' ` """' + value: { string_value: " ? \" \' ` " } + } + + test { + name: "single_quoted_escaped_special_control_characters" + expr: "' \\a \\b \\f \\t \\v '" + value: { string_value: " \a \b \f \t \v " } + } + test { + name: "double_quoted_escaped_special_control_characters" + expr: '" \\a \\b \\f \\t \\v "' + value: { string_value: " \a \b \f \t \v " } + } + test { + name: "single_quoted_unescaped_special_control_characters" + expr: "' \a \b \f \t \v '" + value: { string_value: " \a \b \f \t \v " } + } + test { + name: "double_quoted_unescaped_special_control_characters" + expr: '" \a \b \f \t \v "' + value: { string_value: " \a \b \f \t \v " } + } + + test { + name: "triple_single_quoted_escaped_special_control_characters" + expr: "''' \\a \\b \\f \\t \\v '''" + value: { string_value: " \a \b \f \t \v " } + } + test { + name: "triple_double_quoted_escaped_special_control_characters" + expr: '""" \\a \\b \\f \\t \\v """' + value: { string_value: " \a \b \f \t \v " } + } + test { + name: "triple_single_quoted_unescaped_special_control_characters" + expr: "''' \a \b \f \t \v '''" + value: { string_value: " \a \b \f \t \v " } + } + test { + name: "triple_double_quoted_unescaped_special_control_characters" + expr: '""" \a \b \f \t \v """' + value: { string_value: " \a \b \f \t \v " } + } + + test { + name: "single_quoted_escaped_line_feed" + expr: "' \\n '" + value: { string_value: " \n " } + } + test { + name: "double_quoted_escaped_line_feed" + expr: '" \\n "' + value: { string_value: " \n " } + } + + test { + name: "triple_single_quoted_escaped_line_feed" + expr: "''' \\n '''" + value: { string_value: " \n " } + } + test { + name: "triple_double_quoted_escaped_line_feed" + expr: '""" \\n """' + value: { string_value: " \n " } + } + test { + name: "triple_single_quoted_unescaped_line_feed" + expr: "''' \n '''" + value: { string_value: " \n " } + } + test { + name: "triple_double_quoted_unescaped_line_feed" + expr: '""" \n """' + value: { string_value: " \n " } + } + + test { + name: "single_quoted_escaped_carriage_return" + expr: "' \\r '" + value: { string_value: " \r " } + } + test { + name: "double_quoted_escaped_carriage_return" + expr: '" \\r "' + value: { string_value: " \r " } + } + + test { + name: "triple_single_quoted_escaped_carriage_return" + expr: "''' \\r '''" + value: { string_value: " \r " } + } + test { + name: "triple_double_quoted_escaped_carriage_return" + expr: '""" \\r """' + value: { string_value: " \r " } + } + + # See https://github.com/google/cel-spec/issues/490 + # test { + # name: "triple_single_quoted_unescaped_carriage_return" + # expr: "''' \r '''" + # value: { string_value: " \r " } + # } + # test { + # name: "triple_double_quoted_unescaped_carriage_return" + # expr: '""" \r """' + # value: { string_value: " \r " } + # } + + test { + name: "single_quoted_escaped_windows_line_end" + expr: "' \\r\\n '" + value: { string_value: " \r\n " } + } + test { + name: "double_quoted_escaped_windows_line_end" + expr: '" \\r\\n "' + value: { string_value: " \r\n " } + } + + test { + name: "triple_single_quoted_escaped_windows_line_end" + expr: "''' \\r\\n '''" + value: { string_value: " \r\n " } + } + test { + name: "triple_double_quoted_escaped_windows_line_end" + expr: '""" \\r\\n """' + value: { string_value: " \r\n " } + } + + # See https://github.com/google/cel-spec/issues/490 + # test { + # name: "triple_single_quoted_unescaped_windows_line_end" + # expr: "''' \r\n '''" + # value: { string_value: " \r\n " } + # } + # test { + # name: "triple_double_quoted_unescaped_windows_line_end" + # expr: '""" \r\n """' + # value: { string_value: " \r\n " } + # } + + test { + name: "single_quoted_escaped_all_control_characters" + expr: "' \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F '" + value: { string_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "double_quoted_escaped_all_control_characters" + expr: '" \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F "' + value: { string_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "single_quoted_unescaped_all_control_characters" + expr: "' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F '" + value: { string_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "double_quoted_unescaped_all_control_characters" + expr: '" \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F "' + value: { string_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + + test { + name: "triple_single_quoted_escaped_all_control_characters" + expr: "''' \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F '''" + value: { string_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "triple_double_quoted_escaped_all_control_characters" + expr: '""" \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F """' + value: { string_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "triple_single_quoted_unescaped_all_control_characters" + expr: "''' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F '''" + value: { string_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "triple_double_quoted_unescaped_all_control_characters" + expr: '""" \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F """' + value: { string_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + + test { + name: "single_quoted_octal_escapes" + expr: "' \\000 \\012 \\177 '" + value: { string_value: " \x00 \x0A \x7F " } + } + test { + name: "double_quoted_octal_escapes" + expr: '" \\000 \\012 \\177 "' + value: { string_value: " \x00 \x0A \x7F " } + } + test { + name: "triple_single_quoted_octal_escapes" + expr: "''' \\000 \\012 \\177 '''" + value: { string_value: " \x00 \x0A \x7F " } + } + test { + name: "triple_double_quoted_octal_escapes" + expr: '""" \\000 \\012 \\177 """' + value: { string_value: " \x00 \x0A \x7F " } + } + + test { + name: "single_quoted_lower_x_escapes" + expr: "' \\x00 \\x0A \\x7F '" + value: { string_value: " \x00 \x0A \x7F " } + } + test { + name: "double_quoted_lower_x_escapes" + expr: '" \\x00 \\x0A \\x7F "' + value: { string_value: " \x00 \x0A \x7F " } + } + test { + name: "triple_single_quoted_lower_x_escapes" + expr: "''' \\x00 \\x0A \\x7F '''" + value: { string_value: " \x00 \x0A \x7F " } + } + test { + name: "triple_double_quoted_lower_x_escapes" + expr: '""" \\x00 \\x0A \\x7F """' + value: { string_value: " \x00 \x0A \x7F " } + } + + test { + name: "single_quoted_upper_x_escapes" + expr: "' \\X00 \\X0A \\X7F '" + value: { string_value: " \x00 \x0A \x7F " } + } + test { + name: "double_quoted_upper_x_escapes" + expr: '" \\X00 \\X0A \\X7F "' + value: { string_value: " \x00 \x0A \x7F " } + } + test { + name: "triple_single_quoted_upper_x_escapes" + expr: "''' \\X00 \\X0A \\X7F '''" + value: { string_value: " \x00 \x0A \x7F " } + } + test { + name: "triple_double_quoted_upper_x_escapes" + expr: '""" \\X00 \\X0A \\X7F """' + value: { string_value: " \x00 \x0A \x7F " } + } + + test { + name: "single_quoted_lower_u_escapes" + expr: "' \\u0000 \\u000A \\u007F \\u0100 \\uFFFB '" + value: { string_value: " \x00 \x0A \x7F \u0100 \uFFFB " } + } + test { + name: "double_quoted_lower_u_escapes" + expr: '" \\u0000 \\u000A \\u007F \\u0100 \\uFFFB "' + value: { string_value: " \x00 \x0A \x7F \u0100 \uFFFB " } + } + test { + name: "triple_single_quoted_lower_u_escapes" + expr: "''' \\u0000 \\u000A \\u007F \\u0100 \\uFFFB '''" + value: { string_value: " \x00 \x0A \x7F \u0100 \uFFFB " } + } + test { + name: "triple_double_quoted_lower_u_escapes" + expr: '""" \\u0000 \\u000A \\u007F \\u0100 \\uFFFB """' + value: { string_value: " \x00 \x0A \x7F \u0100 \uFFFB " } + } + + test { + name: "single_quoted_upper_u_escapes" + expr: "' \\U00000000 \\U0000000A \\U0000007F \\U00000100 \\U0000FFFB \\U00010000 \\U0001F62C '" + value: { string_value: " \x00 \x0A \x7F \U00000100 \U0000FFFB \U00010000 \U0001F62C " } + } + test { + name: "double_quoted_upper_u_escapes" + expr: '" \\U00000000 \\U0000000A \\U0000007F \\U00000100 \\U0000FFFB \\U00010000 \\U0001F62C "' + value: { string_value: " \x00 \x0A \x7F \U00000100 \U0000FFFB \U00010000 \U0001F62C " } + } + test { + name: "triple_single_quoted_upper_u_escapes" + expr: "''' \\U00000000 \\U0000000A \\U0000007F \\U00000100 \\U0000FFFB \\U00010000 \\U0001F62C '''" + value: { string_value: " \x00 \x0A \x7F \U00000100 \U0000FFFB \U00010000 \U0001F62C " } + } + test { + name: "triple_double_quoted_upper_u_escapes" + expr: '""" \\U00000000 \\U0000000A \\U0000007F \\U00000100 \\U0000FFFB \\U00010000 \\U0001F62C """' + value: { string_value: " \x00 \x0A \x7F \U00000100 \U0000FFFB \U00010000 \U0001F62C " } + } + + test { + name: "mixed_case_hex_single_quoted_escapes" + expr: "' \\x4a \\x4B \\X4c \\X4D \\u01aB \\U000001aB '" + value: { string_value: " \x4a \x4B \x4c \x4D \u01aB \U000001aB " } + } + test { + name: "mixed_case_hex_double_quoted_escapes" + expr: '" \\x4a \\x4B \\X4c \\X4D \\u01aB \\U000001aB "' + value: { string_value: " \x4a \x4B \x4c \x4D \u01aB \U000001aB " } + } + test { + name: "mixed_case_hex_triple_single_quoted_escapes" + expr: "''' \\x4a \\x4B \\X4c \\X4D \\u01aB \\U000001aB '''" + value: { string_value: " \x4a \x4B \x4c \x4D \u01aB \U000001aB " } + } + test { + name: "mixed_case_hex_triple_double_quoted_escapes" + expr: '""" \\x4a \\x4B \\X4c \\X4D \\u01aB \\U000001aB """' + value: { string_value: " \x4a \x4B \x4c \x4D \u01aB \U000001aB " } + } + + test { + name: "unassigned_code_point_single_quoted_escapes" + expr: "' \\U00088888 '" + value: { string_value: " \U00088888 " } + } + test { + name: "unassigned_code_point_double_quoted_escapes" + expr: '" \\U00088888 "' + value: { string_value: " \U00088888 " } + } + test { + name: "unassigned_code_point_triple_single_quoted_escapes" + expr: "''' \\U00088888 '''" + value: { string_value: " \U00088888 " } + } + test { + name: "unassigned_code_point_triple_double_quoted_escapes" + expr: '""" \\U00088888 """' + value: { string_value: " \U00088888 " } + } + + test { + name: "unassigned_code_point_single_quoted_unescaped" + expr: "' \U00088888 '" + value: { string_value: " \U00088888 " } + } + test { + name: "unassigned_code_point_double_quoted_unescaped" + expr: '" \U00088888 "' + value: { string_value: " \U00088888 " } + } + test { + name: "unassigned_code_point_triple_single_quoted_unescaped" + expr: "''' \U00088888 '''" + value: { string_value: " \U00088888 " } + } + test { + name: "unassigned_code_point_triple_double_quoted_unescaped" + expr: '""" \U00088888 """' + value: { string_value: " \U00088888 " } + } + + test { + name: "raw_single_quoted_escapes" + expr: "r' \\\\ \\\? \\\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 '" + value: { string_value: " \\\\ \\\? \\\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "raw_double_quoted_escapes" + expr: 'r" \\\\ \\\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 "' + value: { string_value: " \\\\ \\\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "raw_triple_single_quoted_escapes" + expr: "r''' \\\\ \\\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 '''" + value: { string_value: " \\\\ \\\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "raw_triple_double_quoted_escapes" + expr: 'r""" \\\\ \\\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 """' + value: { string_value: " \\\\ \\\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + + test { + name: "upper_raw_single_quoted_escapes" + expr: "R' \\\\ \\\? \\\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 '" + value: { string_value: " \\\\ \\\? \\\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "upper_raw_double_quoted_escapes" + expr: 'R" \\\\ \\\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 "' + value: { string_value: " \\\\ \\\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "upper_raw_triple_single_quoted_escapes" + expr: "R''' \\\\ \\\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 '''" + value: { string_value: " \\\\ \\\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "upper_raw_triple_double_quoted_escapes" + expr: 'R""" \\\\ \\\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 """' + value: { string_value: " \\\\ \\\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } +} +section { + name: "bytes_literals" + description: "Check that bytes literals are properly parsed" + test { + name: "single_quoted" + expr: "b'hello'" + value: { bytes_value: "hello" } + } + test { + name: "double_quoted" + expr: 'b"hello"' + value: { bytes_value: "hello" } + } + test { + name: "triple_single_quoted" + expr: "b'''hello'''" + value: { bytes_value: "hello" } + } + test { + name: "triple_double_quoted" + expr: 'b"""hello"""' + value: { bytes_value: "hello" } + } + + test { + name: "single_quoted_escaped_punctuation" + expr: "b' \\\\ \\\? \\\" \\\' \\` '" + value: { bytes_value: " \\ \? \" \' ` " } + } + test { + name: "double_quoted_escaped_punctuation" + expr: 'b" \\\\ \\\? \\\" \\\' \\` "' + value: { bytes_value: " \\ \? \" \' ` " } + } + test { + name: "triple_single_quoted_escaped_punctuation" + expr: "b''' \\\\ \\\? \\\" \\\' \\` '''" + value: { bytes_value: " \\ \? \" \' ` " } + } + test { + name: "triple_double_quoted_escaped_punctuation" + expr: 'b""" \\\\ \\\? \\\" \\\' \\` """' + value: { bytes_value: " \\ \? \" \' ` " } + } + test { + name: "triple_single_quoted_unescaped_punctuation" + expr: "b''' ? \" \' ` '''" + value: { bytes_value: " \? \" \' ` " } + } + test { + name: "triple_double_quoted_unescaped_punctuation" + expr: 'b""" ? \" \' ` """' + value: { bytes_value: " \? \" \' ` " } + } + + test { + name: "single_quoted_escaped_special_control_characters" + expr: "b' \\a \\b \\f \\t \\v '" + value: { bytes_value: " \a \b \f \t \v " } + } + test { + name: "double_quoted_escaped_special_control_characters" + expr: 'b" \\a \\b \\f \\t \\v "' + value: { bytes_value: " \a \b \f \t \v " } + } + test { + name: "single_quoted_unescaped_special_control_characters" + expr: "b' \a \b \f \t \v '" + value: { bytes_value: " \a \b \f \t \v " } + } + test { + name: "double_quoted_unescaped_special_control_characters" + expr: 'b" \a \b \f \t \v "' + value: { bytes_value: " \a \b \f \t \v " } + } + + test { + name: "triple_single_quoted_escaped_special_control_characters" + expr: "b''' \\a \\b \\f \\t \\v '''" + value: { bytes_value: " \a \b \f \t \v " } + } + test { + name: "triple_double_quoted_escaped_special_control_characters" + expr: 'b""" \\a \\b \\f \\t \\v """' + value: { bytes_value: " \a \b \f \t \v " } + } + test { + name: "triple_single_quoted_unescaped_special_control_characters" + expr: "b''' \a \b \f \t \v '''" + value: { bytes_value: " \a \b \f \t \v " } + } + test { + name: "triple_double_quoted_unescaped_special_control_characters" + expr: 'b""" \a \b \f \t \v """' + value: { bytes_value: " \a \b \f \t \v " } + } + + test { + name: "single_quoted_escaped_line_feed" + expr: "b' \\n '" + value: { bytes_value: " \n " } + } + test { + name: "double_quoted_escaped_line_feed" + expr: 'b" \\n "' + value: { bytes_value: " \n " } + } + + test { + name: "triple_single_quoted_escaped_line_feed" + expr: "b''' \\n '''" + value: { bytes_value: " \n " } + } + test { + name: "triple_double_quoted_escaped_line_feed" + expr: 'b""" \\n """' + value: { bytes_value: " \n " } + } + test { + name: "triple_single_quoted_unescaped_line_feed" + expr: "b''' \n '''" + value: { bytes_value: " \n " } + } + test { + name: "triple_double_quoted_unescaped_line_feed" + expr: 'b""" \n """' + value: { bytes_value: " \n " } + } + + test { + name: "single_quoted_escaped_carriage_return" + expr: "b' \\r '" + value: { bytes_value: " \r " } + } + test { + name: "double_quoted_escaped_carriage_return" + expr: 'b" \\r "' + value: { bytes_value: " \r " } + } + + test { + name: "triple_single_quoted_escaped_carriage_return" + expr: "b''' \\r '''" + value: { bytes_value: " \r " } + } + test { + name: "triple_double_quoted_escaped_carriage_return" + expr: 'b""" \\r """' + value: { bytes_value: " \r " } + } + + # See https://github.com/google/cel-spec/issues/490 + # test { + # name: "triple_single_quoted_unescaped_carriage_return" + # expr: "b''' \r '''" + # value: { bytes_value: " \r " } + # } + # test { + # name: "triple_double_quoted_unescaped_carriage_return" + # expr: 'b""" \r """' + # value: { bytes_value: " \r " } + # } + + test { + name: "single_quoted_escaped_windows_line_end" + expr: "b' \\r\\n '" + value: { bytes_value: " \r\n " } + } + test { + name: "double_quoted_escaped_windows_line_end" + expr: 'b" \\r\\n "' + value: { bytes_value: " \r\n " } + } + + test { + name: "triple_single_quoted_escaped_windows_line_end" + expr: "b''' \\r\\n '''" + value: { bytes_value: " \r\n " } + } + test { + name: "triple_double_quoted_escaped_windows_line_end" + expr: 'b""" \\r\\n """' + value: { bytes_value: " \r\n " } + } + + # See https://github.com/google/cel-spec/issues/490 + # test { + # name: "triple_single_quoted_unescaped_windows_line_end" + # expr: "b''' \r\n '''" + # value: { bytes_value: " \r\n " } + # } + # test { + # name: "triple_double_quoted_unescaped_windows_line_end" + # expr: 'b""" \r\n """' + # value: { bytes_value: " \r\n " } + # } + + test { + name: "single_quoted_escaped_all_control_characters" + expr: "b' \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F '" + value: { bytes_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "double_quoted_escaped_all_control_characters" + expr: 'b" \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F "' + value: { bytes_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "single_quoted_unescaped_all_control_characters" + expr: "b' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F '" + value: { bytes_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "double_quoted_unescaped_all_control_characters" + expr: 'b" \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F "' + value: { bytes_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + + test { + name: "triple_single_quoted_escaped_all_control_characters" + expr: "b''' \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F '''" + value: { bytes_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "triple_double_quoted_escaped_all_control_characters" + expr: 'b""" \\x01 \\x02 \\x03 \\x04 \\x05 \\x06 \\x07 \\x08 \\x09 \\x0B \\x0C \\x0E \\x0F \\x10 \\x11 \\x12 \\x13 \\x14 \\x15 \\x16 \\x17 \\x18 \\x19 \\x1A \\x1B \\x1C \\x1D \\x1E \\x1f \\x7F """' + value: { bytes_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "triple_single_quoted_unescaped_all_control_characters" + expr: "b''' \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F '''" + value: { bytes_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + test { + name: "triple_double_quoted_unescaped_all_control_characters" + expr: 'b""" \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F """' + value: { bytes_value: " \x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 \x09 \x0B \x0C \x0E \x0F \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 \x19 \x1A \x1B \x1C \x1D \x1E \x1f \x7F " } + } + + test { + name: "single_quoted_octal_escapes" + expr: "b' \\000 \\012 \\177 \\377 '" + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + test { + name: "double_quoted_octal_escapes" + expr: 'b" \\000 \\012 \\177 \\377 "' + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + test { + name: "triple_single_quoted_octal_escapes" + expr: "b''' \\000 \\012 \\177 \\377 '''" + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + test { + name: "triple_double_quoted_octal_escapes" + expr: 'b""" \\000 \\012 \\177 \\377 """' + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + + test { + name: "single_quoted_lower_x_escapes" + expr: "b' \\x00 \\x0A \\x7F \\xFF '" + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + test { + name: "double_quoted_lower_x_escapes" + expr: 'b" \\x00 \\x0A \\x7F \\xFF "' + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + test { + name: "triple_single_quoted_lower_x_escapes" + expr: "b''' \\x00 \\x0A \\x7F \\xFF '''" + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + test { + name: "triple_double_quoted_lower_x_escapes" + expr: 'b""" \\x00 \\x0A \\x7F \\xFF """' + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + + test { + name: "single_quoted_upper_x_escapes" + expr: "b' \\X00 \\X0A \\X7F \\XFF '" + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + test { + name: "double_quoted_upper_x_escapes" + expr: 'b" \\X00 \\X0A \\X7F \\XFF "' + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + test { + name: "triple_single_quoted_upper_x_escapes" + expr: "b''' \\X00 \\X0A \\X7F \\XFF '''" + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + test { + name: "triple_double_quoted_upper_x_escapes" + expr: 'b""" \\X00 \\X0A \\X7F \\XFF """' + value: { bytes_value: " \x00 \x0A \x7F \xFF " } + } + + test { + name: "mixed_case_hex_single_quoted_escapes" + expr: "B' \\x4a \\x4B \\X4c \\X4D '" + value: { bytes_value: " \x4a \x4B \x4c \x4D " } + } + test { + name: "mixed_case_hex_double_quoted_escapes" + expr: 'B" \\x4a \\x4B \\X4c \\X4D "' + value: { bytes_value: " \x4a \x4B \x4c \x4D " } + } + test { + name: "mixed_case_hex_triple_single_quoted_escapes" + expr: "B''' \\x4a \\x4B \\X4c \\X4D '''" + value: { bytes_value: " \x4a \x4B \x4c \x4D " } + } + test { + name: "mixed_case_hex_triple_double_quoted_escapes" + expr: 'B""" \\x4a \\x4B \\X4c \\X4D """' + value: { bytes_value: " \x4a \x4B \x4c \x4D " } + } + + test { + name: "raw_single_quoted_escapes" + expr: "br' \\\\ \\? \\\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 '" + value: { bytes_value: " \\\\ \\? \\\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "raw_double_quoted_escapes" + expr: 'br" \\\\ \\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 "' + value: { bytes_value: " \\\\ \\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "raw_triple_single_quoted_escapes" + expr: "br''' \\\\ \\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 '''" + value: { bytes_value: " \\\\ \\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "raw_triple_double_quoted_escapes" + expr: 'br""" \\\\ \\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 """' + value: { bytes_value: " \\\\ \\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + + test { + name: "upper_raw_single_quoted_escapes" + expr: "bR' \\\\ \\? \\\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 '" + value: { bytes_value: " \\\\ \\? \\\" \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "upper_raw_double_quoted_escapes" + expr: 'bR" \\\\ \\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 "' + value: { bytes_value: " \\\\ \\? \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "upper_raw_triple_single_quoted_escapes" + expr: "bR''' \\\\ \\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 '''" + value: { bytes_value: " \\\\ \\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } + test { + name: "upper_raw_triple_double_quoted_escapes" + expr: 'bR""" \\\\ \\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 """' + value: { bytes_value: " \\\\ \\? \\\" \\\' \\` \\a \\b \\f \\t \\v \\n \\r \\000 \\x00 \\X00 \\u0000 \\U00000000 " } + } +} section { name: "whitespace" description: "Check that whitespace is ignored by the grammar." @@ -174,3 +988,423 @@ section { value { int64_value: 1 } } } +section { + name: "selectors" + description: "Check that reserved identifiers are permitted as selectors as long as they are not language keywords" + test { + name: "as" + description: "Check that `as` can be used as a selector." + expr: "{ 'as': 1 }.as" + value { int64_value: 1 } + } + test { + name: "break" + description: "Check that `break` can be used as a selector." + expr: "{ 'break': 1 }.break" + value { int64_value: 1 } + } + test { + name: "const" + description: "Check that `const` can be used as a selector." + expr: "{ 'const': 1 }.const" + value { int64_value: 1 } + } + test { + name: "continue" + description: "Check that `continue` can be used as a selector." + expr: "{ 'continue': 1 }.continue" + value { int64_value: 1 } + } + test { + name: "else" + description: "Check that `else` can be used as a selector." + expr: "{ 'else': 1 }.else" + value { int64_value: 1 } + } + test { + name: "for" + description: "Check that `for` can be used as a selector." + expr: "{ 'for': 1 }.for" + value { int64_value: 1 } + } + test { + name: "function" + description: "Check that `function` can be used as a selector." + expr: "{ 'function': 1 }.function" + value { int64_value: 1 } + } + test { + name: "if" + description: "Check that `if` can be used as a selector." + expr: "{ 'if': 1 }.if" + value { int64_value: 1 } + } + test { + name: "import" + description: "Check that `import` can be used as a selector." + expr: "{ 'import': 1 }.import" + value { int64_value: 1 } + } + test { + name: "let" + description: "Check that `let` can be used as a selector." + expr: "{ 'let': 1 }.let" + value { int64_value: 1 } + } + test { + name: "loop" + description: "Check that `loop` can be used as a selector." + expr: "{ 'loop': 1 }.loop" + value { int64_value: 1 } + } + test { + name: "package" + description: "Check that `package` can be used as a selector." + expr: "{ 'package': 1 }.package" + value { int64_value: 1 } + } + test { + name: "namespace" + description: "Check that `namespace` can be used as a selector." + expr: "{ 'namespace': 1 }.namespace" + value { int64_value: 1 } + } + test { + name: "return" + description: "Check that `return` can be used as a selector." + expr: "{ 'return': 1 }.return" + value { int64_value: 1 } + } + test { + name: "var" + description: "Check that `var` can be used as a selector." + expr: "{ 'var': 1 }.var" + value { int64_value: 1 } + } + test { + name: "void" + description: "Check that `void` can be used as a selector." + expr: "{ 'void': 1 }.void" + value { int64_value: 1 } + } + test { + name: "while" + description: "Check that `while` can be used as a selector." + expr: "{ 'while': 1 }.while" + value { int64_value: 1 } + } +} +section { + name: "receiver_function_names" + description: "Check that reserved identifiers are permitted as receiver function names as long as they are not language keywords" + test { + name: "as" + description: "Check that `as` can be used as a receiver function." + expr: "a.as() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "break" + description: "Check that `break` can be used as a receiver function." + expr: "a.break() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "const" + description: "Check that `const` can be used as a receiver function." + expr: "a.const() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "continue" + description: "Check that `continue` can be used as a receiver function." + expr: "a.continue() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "else" + description: "Check that `else` can be used as a receiver function." + expr: "a.else() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "for" + description: "Check that `for` can be used as a receiver function." + expr: "a.for() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "function" + description: "Check that `function` can be used as a receiver function." + expr: "a.function() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "if" + description: "Check that `if` can be used as a receiver function." + expr: "a.if() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "import" + description: "Check that `import` can be used as a receiver function." + expr: "a.import() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "let" + description: "Check that `let` can be used as a receiver function." + expr: "a.let() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "loop" + description: "Check that `loop` can be used as a receiver function." + expr: "a.loop() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "package" + description: "Check that `package` can be used as a receiver function." + expr: "a.package() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "namespace" + description: "Check that `namespace` can be used as a receiver function." + expr: "a.namespace() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "return" + description: "Check that `return` can be used as a receiver function." + expr: "a.return() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "var" + description: "Check that `var` can be used as a receiver function." + expr: "a.var() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "void" + description: "Check that `void` can be used as a receiver function." + expr: "a.void() || true" + disable_check: true + value { bool_value: true } + } + test { + name: "while" + description: "Check that `while` can be used as a receiver function." + expr: "a.while() || true" + disable_check: true + value { bool_value: true } + } +} +section { + name: "struct_field_names" + description: "Check that reserved identifiers are permitted as struct field names as long as they are not language keywords" + test { + name: "as" + description: "Check that `as` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ as: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { as: true } + } + } + } + test { + name: "break" + description: "Check that `break` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ break: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { break: true } + } + } + } + test { + name: "const" + description: "Check that `const` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ const: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { const: true } + } + } + } + test { + name: "continue" + description: "Check that `continue` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ continue: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { continue: true } + } + } + } + test { + name: "else" + description: "Check that `else` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ else: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { else: true } + } + } + } + test { + name: "for" + description: "Check that `for` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ for: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { for: true } + } + } + } + test { + name: "function" + description: "Check that `function` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ function: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { function: true } + } + } + } + test { + name: "if" + description: "Check that `if` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ if: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { if: true } + } + } + } + test { + name: "import" + description: "Check that `import` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ import: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { import: true } + } + } + } + test { + name: "let" + description: "Check that `let` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ let: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { let: true } + } + } + } + test { + name: "loop" + description: "Check that `loop` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ loop: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { loop: true } + } + } + } + test { + name: "package" + description: "Check that `package` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ package: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { package: true } + } + } + } + test { + name: "namespace" + description: "Check that `namespace` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ namespace: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { namespace: true } + } + } + } + test { + name: "return" + description: "Check that `return` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ return: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { return: true } + } + } + } + test { + name: "var" + description: "Check that `var` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ var: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { var: true } + } + } + } + test { + name: "void" + description: "Check that `void` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ void: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { void: true } + } + } + } + test { + name: "while" + description: "Check that `while` can be used as a struct field name." + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{ while: true }" + value { + object_value { + [type.googleapis.com/cel.expr.conformance.proto3.TestAllTypes] { while: true } + } + } + } +} diff --git a/features/proto2.feature b/features/proto2.feature index f96580d..e27316e 100644 --- a/features/proto2.feature +++ b/features/proto2.feature @@ -339,7 +339,7 @@ Scenario: empty_field/repeated_scalar @wip Scenario: empty_field/repeated_enum - Given container is 'cel.expr.conformance.proto3' + Given container is 'cel.expr.conformance.proto2' When CEL expression 'TestAllTypes{}.repeated_nested_enum' is evaluated Then value is [] @@ -560,6 +560,76 @@ Scenario: set_null/single_timestamp When CEL expression 'TestAllTypes{single_timestamp: null} == TestAllTypes{}' is evaluated Then value is celpy.celtypes.BoolType(source=True) +@wip +Scenario: set_null/repeated_field_timestamp_null_pruned + + Given container is 'cel.expr.conformance.proto2' + When CEL expression 'TestAllTypes{repeated_timestamp: [timestamp(1), null]}.repeated_timestamp == [timestamp(1)]' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/repeated_field_duration_null_pruned + + Given container is 'cel.expr.conformance.proto2' + When CEL expression "TestAllTypes{repeated_duration: [duration('1s'), null]}.repeated_duration == [duration('1s')]" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/repeated_field_wrapper_null_pruned + + Given container is 'cel.expr.conformance.proto2' + When CEL expression 'TestAllTypes{repeated_int32_wrapper: [1, null]}.repeated_int32_wrapper == [1]' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/repeated_field_anytype_null_retained + + Given container is 'cel.expr.conformance.proto2' + When CEL expression 'TestAllTypes{repeated_any: [1, null]}.repeated_any == [1, null]' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/repeated_field_jsontype_null_retained + + Given container is 'cel.expr.conformance.proto2' + When CEL expression 'TestAllTypes{repeated_value: [google.protobuf.Value{bool_value: true}, null]}.repeated_value == [true, null]' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/map_timestamp_null_pruned + + Given container is 'cel.expr.conformance.proto2' + When CEL expression 'TestAllTypes{map_bool_timestamp: {true: null, false: timestamp(1)}}.map_bool_timestamp == {false: timestamp(1)}' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/map_duration_null_pruned + + Given container is 'cel.expr.conformance.proto2' + When CEL expression "TestAllTypes{map_bool_duration: {true: null, false: duration('1s')}}.map_bool_duration == {false: duration('1s')}" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/map_wrapper_null_pruned + + Given container is 'cel.expr.conformance.proto2' + When CEL expression 'TestAllTypes{map_bool_int32_wrapper: {true: null, false: 1}}.map_bool_int32_wrapper == {false: 1}' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/map_anytype_null_retained + + Given container is 'cel.expr.conformance.proto2' + When CEL expression 'TestAllTypes{map_bool_any: {true: null, false: 1}}.map_bool_any == {true: null, false: 1}' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/map_jsontype_null_retained + + Given container is 'cel.expr.conformance.proto2' + When CEL expression 'TestAllTypes{map_bool_value: {true: null, false: google.protobuf.Value{bool_value: true}}}.map_bool_value == {true: null, false: true}' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + @wip Scenario: set_null/single_scalar @@ -626,7 +696,7 @@ Scenario: extensions_has/package_scoped_int32 Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(int32_ext=42) When CEL expression 'has(msg.`cel.expr.conformance.proto2.int32_ext`)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_has/package_scoped_nested_ext @@ -634,7 +704,7 @@ Scenario: extensions_has/package_scoped_nested_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_ext=TestAllTypes()) When CEL expression 'has(msg.`cel.expr.conformance.proto2.nested_ext`)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_has/package_scoped_test_all_types_ext @@ -642,7 +712,7 @@ Scenario: extensions_has/package_scoped_test_all_types_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(test_all_types_ext=TestAllTypes()) When CEL expression 'has(msg.`cel.expr.conformance.proto2.test_all_types_ext`)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_has/package_scoped_test_all_types_nested_enum_ext @@ -650,7 +720,7 @@ Scenario: extensions_has/package_scoped_test_all_types_nested_enum_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_enum_ext=1) When CEL expression 'has(msg.`cel.expr.conformance.proto2.nested_enum_ext`)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_has/package_scoped_repeated_test_all_types @@ -658,7 +728,7 @@ Scenario: extensions_has/package_scoped_repeated_test_all_types Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(repeated_test_all_types=[TestAllTypes(single_int64=1), TestAllTypes(single_bool=True)]) When CEL expression 'has(msg.`cel.expr.conformance.proto2.repeated_test_all_types`)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_has/message_scoped_int64 @@ -666,7 +736,7 @@ Scenario: extensions_has/message_scoped_int64 Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(int64_ext=42) When CEL expression 'has(msg.`cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.int64_ext`)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_has/message_scoped_nested_ext @@ -674,7 +744,7 @@ Scenario: extensions_has/message_scoped_nested_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(message_scoped_nested_ext=TestAllTypes()) When CEL expression 'has(msg.`cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.message_scoped_nested_ext`)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_has/message_scoped_nested_enum_ext @@ -682,7 +752,7 @@ Scenario: extensions_has/message_scoped_nested_enum_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_enum_ext=1) When CEL expression 'has(msg.`cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.nested_enum_ext`)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_has/message_scoped_repeated_test_all_types @@ -690,7 +760,7 @@ Scenario: extensions_has/message_scoped_repeated_test_all_types Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(message_scoped_repeated_test_all_types=[TestAllTypes(single_int64=1), TestAllTypes(single_bool=True)]) When CEL expression 'has(msg.`cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.message_scoped_repeated_test_all_types`)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # extensions_get -- Tests for accessing proto2 extension fields. @@ -701,7 +771,7 @@ Scenario: extensions_get/package_scoped_int32 Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(int32_ext=42) When CEL expression 'msg.`cel.expr.conformance.proto2.int32_ext` == 42' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_get/package_scoped_nested_ext @@ -709,7 +779,7 @@ Scenario: extensions_get/package_scoped_nested_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_ext=TestAllTypes()) When CEL expression 'msg.`cel.expr.conformance.proto2.nested_ext` == cel.expr.conformance.proto2.TestAllTypes{}' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_get/package_scoped_test_all_types_ext @@ -717,7 +787,7 @@ Scenario: extensions_get/package_scoped_test_all_types_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(test_all_types_ext=TestAllTypes()) When CEL expression 'msg.`cel.expr.conformance.proto2.test_all_types_ext` == cel.expr.conformance.proto2.TestAllTypes{}' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_get/package_scoped_test_all_types_nested_enum_ext @@ -725,7 +795,7 @@ Scenario: extensions_get/package_scoped_test_all_types_nested_enum_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_enum_ext=1) When CEL expression 'msg.`cel.expr.conformance.proto2.nested_enum_ext` == cel.expr.conformance.proto2.TestAllTypes.NestedEnum.BAR' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_get/package_scoped_repeated_test_all_types @@ -733,7 +803,7 @@ Scenario: extensions_get/package_scoped_repeated_test_all_types Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(repeated_test_all_types=[TestAllTypes(single_int64=1), TestAllTypes(single_bool=True)]) When CEL expression 'msg.`cel.expr.conformance.proto2.repeated_test_all_types` == [cel.expr.conformance.proto2.TestAllTypes{single_int64: 1}, cel.expr.conformance.proto2.TestAllTypes{single_bool: true}]' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_get/message_scoped_int64 @@ -741,7 +811,7 @@ Scenario: extensions_get/message_scoped_int64 Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(int64_ext=42) When CEL expression 'msg.`cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.int64_ext` == 42' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_get/message_scoped_nested_ext @@ -749,7 +819,7 @@ Scenario: extensions_get/message_scoped_nested_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(message_scoped_nested_ext=TestAllTypes()) When CEL expression 'msg.`cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.message_scoped_nested_ext` == cel.expr.conformance.proto2.TestAllTypes{}' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_get/message_scoped_nested_enum_ext @@ -757,7 +827,7 @@ Scenario: extensions_get/message_scoped_nested_enum_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_enum_ext=1) When CEL expression 'msg.`cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.nested_enum_ext` == cel.expr.conformance.proto2.TestAllTypes.NestedEnum.BAR' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: extensions_get/message_scoped_repeated_test_all_types @@ -765,5 +835,5 @@ Scenario: extensions_get/message_scoped_repeated_test_all_types Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(message_scoped_repeated_test_all_types=[TestAllTypes(single_int64=1), TestAllTypes(single_bool=True)]) When CEL expression 'msg.`cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.message_scoped_repeated_test_all_types` == [cel.expr.conformance.proto2.TestAllTypes{single_int64: 1}, cel.expr.conformance.proto2.TestAllTypes{single_bool: true}]' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) diff --git a/features/proto2.textproto b/features/proto2.textproto index 0662b06..c3cd118 100644 --- a/features/proto2.textproto +++ b/features/proto2.textproto @@ -590,7 +590,7 @@ section { test { name: "repeated_enum" - container: "cel.expr.conformance.proto3" + container: "cel.expr.conformance.proto2" expr: "TestAllTypes{}.repeated_nested_enum" value: { list_value: {} } } @@ -798,6 +798,67 @@ section { expr: "TestAllTypes{single_timestamp: null} == TestAllTypes{}" value: { bool_value: true } } + test { + name: "repeated_field_timestamp_null_pruned" + container: "cel.expr.conformance.proto2" + expr: "TestAllTypes{repeated_timestamp: [timestamp(1), null]}.repeated_timestamp == [timestamp(1)]" + value: { bool_value: true } + } + test { + name: "repeated_field_duration_null_pruned" + container: "cel.expr.conformance.proto2" + expr: "TestAllTypes{repeated_duration: [duration('1s'), null]}.repeated_duration == [duration('1s')]" + value: { bool_value: true } + } + test { + name: "repeated_field_wrapper_null_pruned" + container: "cel.expr.conformance.proto2" + expr: "TestAllTypes{repeated_int32_wrapper: [1, null]}.repeated_int32_wrapper == [1]" + value: { bool_value: true } + } + test { + name: "repeated_field_anytype_null_retained" + container: "cel.expr.conformance.proto2" + expr: "TestAllTypes{repeated_any: [1, null]}.repeated_any == [1, null]" + value: { bool_value: true } + } + test { + name: "repeated_field_jsontype_null_retained" + container: "cel.expr.conformance.proto2" + expr: "TestAllTypes{repeated_value: [google.protobuf.Value{bool_value: true}, null]}.repeated_value == [true, null]" + value: { bool_value: true } + } + test { + name: "map_timestamp_null_pruned" + container: "cel.expr.conformance.proto2" + expr: "TestAllTypes{map_bool_timestamp: {true: null, false: timestamp(1)}}.map_bool_timestamp == {false: timestamp(1)}" + value: { bool_value: true } + } + test { + name: "map_duration_null_pruned" + container: "cel.expr.conformance.proto2" + expr: "TestAllTypes{map_bool_duration: {true: null, false: duration('1s')}}.map_bool_duration == {false: duration('1s')}" + value: { bool_value: true } + } + test { + name: "map_wrapper_null_pruned" + container: "cel.expr.conformance.proto2" + expr: "TestAllTypes{map_bool_int32_wrapper: {true: null, false: 1}}.map_bool_int32_wrapper == {false: 1}" + value: { bool_value: true } + } + test { + name: "map_anytype_null_retained" + container: "cel.expr.conformance.proto2" + expr: "TestAllTypes{map_bool_any: {true: null, false: 1}}.map_bool_any == {true: null, false: 1}" + value: { bool_value: true } + } + test { + name: "map_jsontype_null_retained" + container: "cel.expr.conformance.proto2" + expr: "TestAllTypes{map_bool_value: {true: null, false: google.protobuf.Value{bool_value: true}}}.map_bool_value == {true: null, false: true}" + value: { bool_value: true } + } + test { name: "single_scalar" container: "cel.expr.conformance.proto2" diff --git a/features/proto2_ext.feature b/features/proto2_ext.feature index d112fe5..12cd485 100644 --- a/features/proto2_ext.feature +++ b/features/proto2_ext.feature @@ -11,7 +11,7 @@ Scenario: has_ext/package_scoped_int32 Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(int32_ext=42) When CEL expression 'proto.hasExt(msg, cel.expr.conformance.proto2.int32_ext)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: has_ext/package_scoped_nested_ext @@ -19,7 +19,7 @@ Scenario: has_ext/package_scoped_nested_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_ext=TestAllTypes()) When CEL expression 'proto.hasExt(msg, cel.expr.conformance.proto2.nested_ext)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: has_ext/package_scoped_test_all_types_ext @@ -27,7 +27,7 @@ Scenario: has_ext/package_scoped_test_all_types_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(test_all_types_ext=TestAllTypes()) When CEL expression 'proto.hasExt(msg, cel.expr.conformance.proto2.test_all_types_ext)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: has_ext/package_scoped_test_all_types_nested_enum_ext @@ -35,7 +35,7 @@ Scenario: has_ext/package_scoped_test_all_types_nested_enum_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_enum_ext=1) When CEL expression 'proto.hasExt(msg, cel.expr.conformance.proto2.nested_enum_ext)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: has_ext/package_scoped_repeated_test_all_types @@ -43,7 +43,7 @@ Scenario: has_ext/package_scoped_repeated_test_all_types Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(repeated_test_all_types=[TestAllTypes(single_int64=1), TestAllTypes(single_bool=True)]) When CEL expression 'proto.hasExt(msg, cel.expr.conformance.proto2.repeated_test_all_types)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: has_ext/message_scoped_int64 @@ -51,7 +51,7 @@ Scenario: has_ext/message_scoped_int64 Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(int64_ext=42) When CEL expression 'proto.hasExt(msg, cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.int64_ext)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: has_ext/message_scoped_nested_ext @@ -59,7 +59,7 @@ Scenario: has_ext/message_scoped_nested_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(message_scoped_nested_ext=TestAllTypes()) When CEL expression 'proto.hasExt(msg, cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.message_scoped_nested_ext)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: has_ext/message_scoped_nested_enum_ext @@ -67,7 +67,7 @@ Scenario: has_ext/message_scoped_nested_enum_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_enum_ext=1) When CEL expression 'proto.hasExt(msg, cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.nested_enum_ext)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: has_ext/message_scoped_repeated_test_all_types @@ -75,7 +75,7 @@ Scenario: has_ext/message_scoped_repeated_test_all_types Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(message_scoped_repeated_test_all_types=[TestAllTypes(single_int64=1), TestAllTypes(single_bool=True)]) When CEL expression 'proto.hasExt(msg, cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.message_scoped_repeated_test_all_types)' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # get_ext -- @@ -86,7 +86,7 @@ Scenario: get_ext/package_scoped_int32 Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(int32_ext=42) When CEL expression 'proto.getExt(msg, cel.expr.conformance.proto2.int32_ext) == 42' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: get_ext/package_scoped_nested_ext @@ -94,7 +94,7 @@ Scenario: get_ext/package_scoped_nested_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_ext=TestAllTypes()) When CEL expression 'proto.getExt(msg, cel.expr.conformance.proto2.nested_ext) == cel.expr.conformance.proto2.TestAllTypes{}' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: get_ext/package_scoped_test_all_types_ext @@ -102,7 +102,7 @@ Scenario: get_ext/package_scoped_test_all_types_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(test_all_types_ext=TestAllTypes()) When CEL expression 'proto.getExt(msg, cel.expr.conformance.proto2.test_all_types_ext) == cel.expr.conformance.proto2.TestAllTypes{}' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: get_ext/package_scoped_test_all_types_nested_enum_ext @@ -110,7 +110,7 @@ Scenario: get_ext/package_scoped_test_all_types_nested_enum_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_enum_ext=1) When CEL expression 'proto.getExt(msg, cel.expr.conformance.proto2.nested_enum_ext) == cel.expr.conformance.proto2.TestAllTypes.NestedEnum.BAR' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: get_ext/package_scoped_repeated_test_all_types @@ -118,7 +118,7 @@ Scenario: get_ext/package_scoped_repeated_test_all_types Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(repeated_test_all_types=[TestAllTypes(single_int64=1), TestAllTypes(single_bool=True)]) When CEL expression 'proto.getExt(msg, cel.expr.conformance.proto2.repeated_test_all_types) == [cel.expr.conformance.proto2.TestAllTypes{single_int64: 1}, cel.expr.conformance.proto2.TestAllTypes{single_bool: true}]' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: get_ext/message_scoped_int64 @@ -126,7 +126,7 @@ Scenario: get_ext/message_scoped_int64 Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(int64_ext=42) When CEL expression 'proto.getExt(msg, cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.int64_ext) == 42' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: get_ext/message_scoped_nested_ext @@ -134,7 +134,7 @@ Scenario: get_ext/message_scoped_nested_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(message_scoped_nested_ext=TestAllTypes()) When CEL expression 'proto.getExt(msg, cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.message_scoped_nested_ext) == cel.expr.conformance.proto2.TestAllTypes{}' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: get_ext/message_scoped_nested_enum_ext @@ -142,7 +142,7 @@ Scenario: get_ext/message_scoped_nested_enum_ext Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(nested_enum_ext=1) When CEL expression 'proto.getExt(msg, cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.nested_enum_ext) == cel.expr.conformance.proto2.TestAllTypes.NestedEnum.BAR' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: get_ext/message_scoped_repeated_test_all_types @@ -150,5 +150,5 @@ Scenario: get_ext/message_scoped_repeated_test_all_types Given type_env parameter "msg" is celpy.celtypes.MessageType and bindings parameter "msg" is TestAllTypes(message_scoped_repeated_test_all_types=[TestAllTypes(single_int64=1), TestAllTypes(single_bool=True)]) When CEL expression 'proto.getExt(msg, cel.expr.conformance.proto2.Proto2ExtensionScopedMessage.message_scoped_repeated_test_all_types) == [cel.expr.conformance.proto2.TestAllTypes{single_int64: 1}, cel.expr.conformance.proto2.TestAllTypes{single_bool: true}]' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) diff --git a/features/proto3.feature b/features/proto3.feature index 94a7eb2..471a404 100644 --- a/features/proto3.feature +++ b/features/proto3.feature @@ -129,7 +129,7 @@ Scenario: literal_wellknown/struct Given container is 'cel.expr.conformance.proto3' When CEL expression "TestAllTypes{single_struct: {'one': 1, 'two': 2}}" is evaluated - Then value is TestAllTypes(single_struct=celpy.celtypes.MapType({'one': celpy.celtypes.DoubleType(source=1.0), 'two': celpy.celtypes.DoubleType(source=2.0)})) + Then value is TestAllTypes(single_struct=celpy.celtypes.MapType({'two': celpy.celtypes.DoubleType(source=2.0), 'one': celpy.celtypes.DoubleType(source=1.0)})) Scenario: literal_wellknown/value @@ -456,6 +456,76 @@ Scenario: set_null/single_timestamp When CEL expression 'TestAllTypes{single_timestamp: null} == TestAllTypes{}' is evaluated Then value is celpy.celtypes.BoolType(source=True) +@wip +Scenario: set_null/repeated_field_timestamp_null_pruned + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{repeated_timestamp: [timestamp(1), null]}.repeated_timestamp == [timestamp(1)]' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/repeated_field_duration_null_pruned + + Given container is 'cel.expr.conformance.proto3' + When CEL expression "TestAllTypes{repeated_duration: [duration('1s'), null]}.repeated_duration == [duration('1s')]" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/repeated_field_wrapper_null_pruned + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{repeated_int32_wrapper: [1, null]}.repeated_int32_wrapper == [1]' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/repeated_field_anytype_null_retained + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{repeated_any: [1, null]}.repeated_any == [1, null]' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/repeated_field_jsontype_null_retained + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{repeated_value: [google.protobuf.Value{bool_value: true}, null]}.repeated_value == [true, null]' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/map_timestamp_null_pruned + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{map_bool_timestamp: {true: null, false: timestamp(1)}}.map_bool_timestamp == {false: timestamp(1)}' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/map_duration_null_pruned + + Given container is 'cel.expr.conformance.proto3' + When CEL expression "TestAllTypes{map_bool_duration: {true: null, false: duration('1s')}}.map_bool_duration == {false: duration('1s')}" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/map_wrapper_null_pruned + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{map_bool_int32_wrapper: {true: null, false: 1}}.map_bool_int32_wrapper == {false: 1}' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/map_anytype_null_retained + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{map_bool_any: {true: null, false: 1}}.map_bool_any == {true: null, false: 1}' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: set_null/map_jsontype_null_retained + + Given container is 'cel.expr.conformance.proto3' + When CEL expression 'TestAllTypes{map_bool_value: {true: null, false: google.protobuf.Value{bool_value: true}}}.map_bool_value == {true: null, false: true}' is evaluated + Then value is celpy.celtypes.BoolType(source=True) + @wip Scenario: set_null/single_scalar diff --git a/features/proto3.textproto b/features/proto3.textproto index 2a0378d..676c6e3 100644 --- a/features/proto3.textproto +++ b/features/proto3.textproto @@ -695,6 +695,66 @@ section { expr: "TestAllTypes{single_timestamp: null} == TestAllTypes{}" value: { bool_value: true } } + test { + name: "repeated_field_timestamp_null_pruned" + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{repeated_timestamp: [timestamp(1), null]}.repeated_timestamp == [timestamp(1)]" + value: { bool_value: true } + } + test { + name: "repeated_field_duration_null_pruned" + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{repeated_duration: [duration('1s'), null]}.repeated_duration == [duration('1s')]" + value: { bool_value: true } + } + test { + name: "repeated_field_wrapper_null_pruned" + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{repeated_int32_wrapper: [1, null]}.repeated_int32_wrapper == [1]" + value: { bool_value: true } + } + test { + name: "repeated_field_anytype_null_retained" + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{repeated_any: [1, null]}.repeated_any == [1, null]" + value: { bool_value: true } + } + test { + name: "repeated_field_jsontype_null_retained" + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{repeated_value: [google.protobuf.Value{bool_value: true}, null]}.repeated_value == [true, null]" + value: { bool_value: true } + } + test { + name: "map_timestamp_null_pruned" + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{map_bool_timestamp: {true: null, false: timestamp(1)}}.map_bool_timestamp == {false: timestamp(1)}" + value: { bool_value: true } + } + test { + name: "map_duration_null_pruned" + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{map_bool_duration: {true: null, false: duration('1s')}}.map_bool_duration == {false: duration('1s')}" + value: { bool_value: true } + } + test { + name: "map_wrapper_null_pruned" + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{map_bool_int32_wrapper: {true: null, false: 1}}.map_bool_int32_wrapper == {false: 1}" + value: { bool_value: true } + } + test { + name: "map_anytype_null_retained" + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{map_bool_any: {true: null, false: 1}}.map_bool_any == {true: null, false: 1}" + value: { bool_value: true } + } + test { + name: "map_jsontype_null_retained" + container: "cel.expr.conformance.proto3" + expr: "TestAllTypes{map_bool_value: {true: null, false: google.protobuf.Value{bool_value: true}}}.map_bool_value == {true: null, false: true}" + value: { bool_value: true } + } test { name: "single_scalar" container: "cel.expr.conformance.proto3" diff --git a/features/string_ext.feature b/features/string_ext.feature index 4e70825..3d0480b 100644 --- a/features/string_ext.feature +++ b/features/string_ext.feature @@ -21,7 +21,7 @@ Scenario: char_at/end_index Scenario: char_at/multiple When CEL expression "'©αT'.charAt(0) == '©' && '©αT'.charAt(1) == 'α' && '©αT'.charAt(2) == 'T'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # index_of -- @@ -42,154 +42,160 @@ Scenario: index_of/string_index Scenario: index_of/nomatch When CEL expression "'tacocat'.indexOf('none') == -1" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: index_of/empty_index When CEL expression "'tacocat'.indexOf('', 3) == 3" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: index_of/char_index When CEL expression "'tacocat'.indexOf('a', 3) == 5" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: index_of/string_index When CEL expression "'tacocat'.indexOf('at', 3) == 5" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: index_of/unicode_char When CEL expression "'ta©o©αT'.indexOf('©') == 2" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: index_of/unicode_char_index When CEL expression "'ta©o©αT'.indexOf('©', 3) == 4" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: index_of/unicode_string_index When CEL expression "'ta©o©αT'.indexOf('©αT', 3) == 4" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: index_of/unicode_string_nomatch_index When CEL expression "'ta©o©αT'.indexOf('©α', 5) == -1" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: index_of/char_index When CEL expression "'ijk'.indexOf('k') == 2" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: index_of/string_with_space_fullmatch When CEL expression "'hello wello'.indexOf('hello wello') == 0" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: index_of/string_with_space_index When CEL expression "'hello wello'.indexOf('ello', 6) == 7" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: index_of/string_nomatch_index When CEL expression "'hello wello'.indexOf('elbo room!!') == -1" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # last_index_of -- @wip -Scenario: last_index_of/empty +Scenario: last_index_of/empty_string + + When CEL expression "''.lastIndexOf('@@') == -1" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: last_index_of/empty_argument When CEL expression "'tacocat'.lastIndexOf('') == 7" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/string When CEL expression "'tacocat'.lastIndexOf('at') == 5" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/string_nomatch When CEL expression "'tacocat'.lastIndexOf('none') == -1" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/empty_index When CEL expression "'tacocat'.lastIndexOf('', 3) == 3" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/char_index When CEL expression "'tacocat'.lastIndexOf('a', 3) == 1" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/unicode_char When CEL expression "'ta©o©αT'.lastIndexOf('©') == 4" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/unicode_char_index When CEL expression "'ta©o©αT'.lastIndexOf('©', 3) == 2" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/unicode_string_index When CEL expression "'ta©o©αT'.lastIndexOf('©α', 4) == 4" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/string_with_space_string_index When CEL expression "'hello wello'.lastIndexOf('ello', 6) == 1" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/string_with_space_string_nomatch When CEL expression "'hello wello'.lastIndexOf('low') == -1" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/string_with_space_string_with_space_nomatch When CEL expression "'hello wello'.lastIndexOf('elbo room!!') == -1" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/string_with_space_fullmatch When CEL expression "'hello wello'.lastIndexOf('hello wello') == 0" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: last_index_of/repeated_string When CEL expression "'bananananana'.lastIndexOf('nana', 7) == 6" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # ascii_casing -- @@ -198,37 +204,37 @@ Scenario: last_index_of/repeated_string Scenario: ascii_casing/lowerascii When CEL expression "'TacoCat'.lowerAscii() == 'tacocat'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: ascii_casing/lowerascii_unicode When CEL expression "'TacoCÆt'.lowerAscii() == 'tacocÆt'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: ascii_casing/lowerascii_unicode_with_space When CEL expression "'TacoCÆt Xii'.lowerAscii() == 'tacocÆt xii'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: ascii_casing/upperascii When CEL expression "'tacoCat'.upperAscii() == 'TACOCAT'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: ascii_casing/upperascii_unicode When CEL expression "'tacoCαt'.upperAscii() == 'TACOCαT'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: ascii_casing/upperascii_unicode_with_space When CEL expression "'TacoCÆt Xii'.upperAscii() == 'TACOCÆT XII'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # replace -- @@ -237,25 +243,25 @@ Scenario: ascii_casing/upperascii_unicode_with_space Scenario: replace/no_placeholder When CEL expression "'12 days 12 hours'.replace('{0}', '2') == '12 days 12 hours'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: replace/basic When CEL expression "'{0} days {0} hours'.replace('{0}', '2') == '2 days 2 hours'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: replace/chained When CEL expression "'{0} days {0} hours'.replace('{0}', '2', 1).replace('{0}', '23') == '2 days 23 hours'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: replace/unicode When CEL expression "'1 ©αT taco'.replace('αT', 'o©α') == '1 ©o©α taco'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # split -- @@ -264,25 +270,25 @@ Scenario: replace/unicode Scenario: split/empty When CEL expression "'hello world'.split(' ') == ['hello', 'world']" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: split/zero_limit When CEL expression "'hello world events!'.split(' ', 0) == []" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: split/one_limit When CEL expression "'hello world events!'.split(' ', 1) == ['hello world events!']" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: split/unicode_negative_limit When CEL expression "'o©o©o©o'.split('©', -1) == ['o', 'o', 'o', 'o']" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # substring -- @@ -291,37 +297,37 @@ Scenario: split/unicode_negative_limit Scenario: substring/start When CEL expression "'tacocat'.substring(4) == 'cat'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: substring/start_with_max_length When CEL expression "'tacocat'.substring(7) == ''" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: substring/start_and_end When CEL expression "'tacocat'.substring(0, 4) == 'taco'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: substring/start_and_end_equal_value When CEL expression "'tacocat'.substring(4, 4) == ''" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: substring/unicode_start_and_end When CEL expression "'ta©o©αT'.substring(2, 6) == '©o©α'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: substring/unicode_start_and_end_equal_value When CEL expression "'ta©o©αT'.substring(7, 7) == ''" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # trim -- @@ -330,31 +336,31 @@ Scenario: substring/unicode_start_and_end_equal_value Scenario: trim/blank_spaces_escaped_chars When CEL expression "' \\f\\n\\r\\t\\vtext '.trim() == 'text'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: trim/unicode_space_chars_1 When CEL expression "'\\u0085\\u00a0\\u1680text'.trim() == 'text'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: trim/unicode_space_chars_2 When CEL expression "'text\\u2000\\u2001\\u2002\\u2003\\u2004\\u2004\\u2006\\u2007\\u2008\\u2009'.trim() == 'text'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: trim/unicode_space_chars_3 When CEL expression "'\\u200atext\\u2028\\u2029\\u202F\\u205F\\u3000'.trim() == 'text'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: trim/unicode_no_trim When CEL expression "'\\u180etext\\u200b\\u200c\\u200d\\u2060\\ufeff'.trim() == '\\u180etext\\u200b\\u200c\\u200d\\u2060\\ufeff'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # join -- @@ -363,25 +369,25 @@ Scenario: trim/unicode_no_trim Scenario: join/empty_separator When CEL expression "['x', 'y'].join() == 'xy'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: join/dash_separator When CEL expression "['x', 'y'].join('-') == 'x-y'" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: join/empty_string_empty_separator When CEL expression "[].join() == ''" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: join/empty_string_dash_separator When CEL expression "[].join('-') == ''" is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) # quote -- @@ -390,109 +396,109 @@ Scenario: join/empty_string_dash_separator Scenario: quote/multiline When CEL expression 'strings.quote("first\\nsecond") == "\\"first\\\\nsecond\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/escaped When CEL expression 'strings.quote("bell\\a") == "\\"bell\\\\a\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/backspace When CEL expression 'strings.quote("\\bbackspace") == "\\"\\\\bbackspace\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/form_feed When CEL expression 'strings.quote("\\fform feed") == "\\"\\\\fform feed\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/carriage_return When CEL expression 'strings.quote("carriage \\r return") == "\\"carriage \\\\r return\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/horizontal_tab When CEL expression 'strings.quote("horizontal tab\\t") == "\\"horizontal tab\\\\t\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/vertical_tab When CEL expression 'strings.quote("vertical \\v tab") == "\\"vertical \\\\v tab\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/double_slash When CEL expression 'strings.quote("double \\\\\\\\ slash") == "\\"double \\\\\\\\\\\\\\\\ slash\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/two_escape_sequences When CEL expression 'strings.quote("two escape sequences \\\\a\\\\n") == "\\"two escape sequences \\\\\\\\a\\\\\\\\n\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/verbatim When CEL expression 'strings.quote("verbatim") == "\\"verbatim\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/ends_with When CEL expression 'strings.quote("ends with \\\\") == "\\"ends with \\\\\\\\\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/starts_with When CEL expression 'strings.quote("\\\\ starts with") == "\\"\\\\\\\\ starts with\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/printable_unicode When CEL expression 'strings.quote("printable unicode😀") == "\\"printable unicode😀\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/mid_string_quote When CEL expression 'strings.quote("mid string \\" quote") == "\\"mid string \\\\\\" quote\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/single_quote_with_double_quote When CEL expression 'strings.quote(\'single-quote with "double quote"\') == "\\"single-quote with \\\\\\"double quote\\\\\\"\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/size_unicode_char When CEL expression 'strings.quote("size(\'ÿ\')") == "\\"size(\'ÿ\')\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/size_unicode_string When CEL expression 'strings.quote("size(\'πέντε\')") == "\\"size(\'πέντε\')\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/unicode When CEL expression 'strings.quote("завтра") == "\\"завтра\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/unicode_code_points @@ -504,7 +510,7 @@ Scenario: quote/unicode_code_points Scenario: quote/unicode_2 When CEL expression 'strings.quote("ta©o©αT") == "\\"ta©o©αT\\""' is evaluated - Then none is None + Then value is celpy.celtypes.BoolType(source=True) @wip Scenario: quote/empty_quote @@ -653,12 +659,36 @@ Scenario: format/default precision for fixed-point clause When CEL expression '"%f".format([2.71828])' is evaluated Then value is celpy.celtypes.StringType(source='2.718280') +@wip +Scenario: format/default precision for fixed-point clause with int + + When CEL expression '"%f".format([2])' is evaluated + Then value is celpy.celtypes.StringType(source='2.000000') + +@wip +Scenario: format/default precision for fixed-point clause with uint + + When CEL expression '"%f".format([3u])' is evaluated + Then value is celpy.celtypes.StringType(source='3.000000') + @wip Scenario: format/default precision for scientific notation When CEL expression '"%e".format([2.71828])' is evaluated Then value is celpy.celtypes.StringType(source='2.718280e+00') +@wip +Scenario: format/default precision for scientific notation with int + + When CEL expression '"%e".format([2])' is evaluated + Then value is celpy.celtypes.StringType(source='2.000000e+00') + +@wip +Scenario: format/default precision for scientific notation with uint + + When CEL expression '"%e".format([3u])' is evaluated + Then value is celpy.celtypes.StringType(source='3.000000e+00') + @wip Scenario: format/NaN support for scientific notation @@ -919,6 +949,66 @@ Scenario: format/default precision for fixed-point clause in a string variable When CEL expression 'str_var.format([2.71828])' is evaluated Then value is celpy.celtypes.StringType(source='2.718280') +@wip +Scenario: format/format_%f_insignificant_zeroes_removed + + When CEL expression '"%.0f".format([123.000000])' is evaluated + Then value is celpy.celtypes.StringType(source='123') + +@wip +Scenario: format/format_%f_positive_round_to_whole_number + + When CEL expression '"%.0f".format([3.5001])' is evaluated + Then value is celpy.celtypes.StringType(source='4') + +@wip +Scenario: format/format_%f_negative_truncate_to_whole_number + + When CEL expression '"%.0f".format([3.4999])' is evaluated + Then value is celpy.celtypes.StringType(source='3') + +@wip +Scenario: format/format_%f_halfway_round_up_to_nearest_even + + When CEL expression '"%.0f".format([1.5])' is evaluated + Then value is celpy.celtypes.StringType(source='2') + +@wip +Scenario: format/format_%f_halfway_truncate_to_nearest_even + + When CEL expression '"%.0f".format([2.5])' is evaluated + Then value is celpy.celtypes.StringType(source='2') + +@wip +Scenario: format/format_%f_positive_round_up + + When CEL expression '"%.3f".format([123.4999])' is evaluated + Then value is celpy.celtypes.StringType(source='123.500') + +@wip +Scenario: format/format_%f_positive_round_down + + When CEL expression '"%.3f".format([123.4994])' is evaluated + Then value is celpy.celtypes.StringType(source='123.499') + +@wip +Scenario: format/format_%f_negative_round_up + + When CEL expression '"%.3f".format([-123.4999])' is evaluated + Then value is celpy.celtypes.StringType(source='-123.500') + +@wip +Scenario: format/format_%f_negative_round_down + + When CEL expression '"%.3f".format([-123.4994])' is evaluated + Then value is celpy.celtypes.StringType(source='-123.499') + +@wip +Scenario: format/format_%f_zero_padding + + When CEL expression '"%.5f".format([-1.2])' is evaluated + Then value is celpy.celtypes.StringType(source='-1.20000') + # format_errors -- @@ -1253,3 +1343,24 @@ Scenario: type_errors/substring_binary_invalid_argument_2 When CEL expression "'tacocat'.substring(0, false) == ''" is evaluated Then eval_error is 'no such overload' + +# reverse -- Tests for (string).reverse(). Added in version 3. + +@wip +Scenario: reverse/empty + + When CEL expression "''.reverse() == ''" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: reverse/single_character + + When CEL expression "'☺'.reverse() == '☺'" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + +@wip +Scenario: reverse/multiple + + When CEL expression "'Ta©oCαt'.reverse() == 'tαCo©aT'" is evaluated + Then value is celpy.celtypes.BoolType(source=True) + diff --git a/features/string_ext.textproto b/features/string_ext.textproto index d245558..9a92db3 100644 --- a/features/string_ext.textproto +++ b/features/string_ext.textproto @@ -92,7 +92,11 @@ section: { section: { name: "last_index_of" test: { - name: "empty" + name: "empty_string" + expr: "''.lastIndexOf('@@') == -1" + } + test: { + name: "empty_argument" expr: "'tacocat'.lastIndexOf('') == 7" } test: { @@ -534,6 +538,20 @@ section: { string_value: '2.718280', } } + test: { + name: "default precision for fixed-point clause with int" + expr: '"%f".format([2])' + value: { + string_value: '2.000000', + } + } + test: { + name: "default precision for fixed-point clause with uint" + expr: '"%f".format([3u])' + value: { + string_value: '3.000000', + } + } test: { name: "default precision for scientific notation" expr: '"%e".format([2.71828])' @@ -541,6 +559,20 @@ section: { string_value: '2.718280e+00', } } + test: { + name: "default precision for scientific notation with int" + expr: '"%e".format([2])' + value: { + string_value: '2.000000e+00', + } + } + test: { + name: "default precision for scientific notation with uint" + expr: '"%e".format([3u])' + value: { + string_value: '3.000000e+00', + } + } test: { name: "NaN support for scientific notation" expr: '"%e".format([double("NaN")])' @@ -884,6 +916,76 @@ section: { string_value: '2.718280', } } +test: { + name: "format_%f_insignificant_zeroes_removed" + expr: '"%.0f".format([123.000000])' + value: { + string_value: '123', + } + } + test: { + name: "format_%f_positive_round_to_whole_number" + expr: '"%.0f".format([3.5001])' + value: { + string_value: '4', + } + } + test: { + name: "format_%f_negative_truncate_to_whole_number" + expr: '"%.0f".format([3.4999])' + value: { + string_value: '3', + } + } + test: { + name: "format_%f_halfway_round_up_to_nearest_even" + expr: '"%.0f".format([1.5])' + value: { + string_value: '2', + } + } + test: { + name: "format_%f_halfway_truncate_to_nearest_even" + expr: '"%.0f".format([2.5])' + value: { + string_value: '2', + } + } + test: { + name: "format_%f_positive_round_up" + expr: '"%.3f".format([123.4999])' + value: { + string_value: '123.500', + } + } + test: { + name: "format_%f_positive_round_down" + expr: '"%.3f".format([123.4994])' + value: { + string_value: '123.499', + } + } + test: { + name: "format_%f_negative_round_up" + expr: '"%.3f".format([-123.4999])' + value: { + string_value: '-123.500', + } + } + test: { + name: "format_%f_negative_round_down" + expr: '"%.3f".format([-123.4994])' + value: { + string_value: '-123.499', + } + } + test: { + name: "format_%f_zero_padding" + expr: '"%.5f".format([-1.2])' + value: { + string_value: '-1.20000', + } + } } section: { name: "format_errors" @@ -1415,3 +1517,19 @@ section: { } } } +section: { + name: "reverse" + description: "Tests for (string).reverse(). Added in version 3." + test: { + name: "empty" + expr: "''.reverse() == ''" + } + test: { + name: "single_character" + expr: "'☺'.reverse() == '☺'" + } + test: { + name: "multiple" + expr: "'Ta©oCαt'.reverse() == 'tαCo©aT'" + } +} diff --git a/tools/README.rst b/tools/README.rst index a027ef4..b43dafa 100644 --- a/tools/README.rst +++ b/tools/README.rst @@ -5,20 +5,30 @@ Tools to Create the Conformance Test Suite The conformance test files originate from the https://github.com/google/cel-spec repository. They are all Protobuf messages, serialized into ``textproto``, like this: - .. code-block:: protobuf +.. code-block:: protobuf - test { - name: "self_eval_int_zero" - expr: "0" - value: { int64_value: 0 } - } + test { + name: "self_eval_int_zero" + expr: "0" + value: { int64_value: 0 } + } The ``gherkinize.py`` script translates these into Gherkin scenarios. +.. code-block:: gherkin + + Scenario: self_eval_zeroish/self_eval_int_zero + + When CEL expression '0' is evaluated + Then value is celpy.celtypes.IntType(source=0) + +The **behave** tool expands the various Gherkin steps into Python functions +to arrange a fixture, take an action on the fixture, and assert the actual result is the expected value. + Usage ===== -Gherkin generation is controlled by a Makefile in the ``features`` directory which provides +Gherkin generation is controlled by a ``Makefile`` in the ``features`` directory which provides two commands: - ``make all`` checks the cel-spec repository for ``.textproto`` files, copies them to the local @@ -38,6 +48,12 @@ A good way to use this is to do a checkout from https://github.com/google/cel-sp directory. By default, the Makefile looks for ``/../../google/cel-spec`` but the location can be overridden with the ``CEL_SPEC_PATH`` environment variable. +refresh_spec.py +=============== + +This script executes a number of **git** commands to pull the most recent +tag for the google/cel-spec project. + gherkinize.py ============= diff --git a/tools/refresh_spec.py b/tools/refresh_spec.py new file mode 100644 index 0000000..22054d8 --- /dev/null +++ b/tools/refresh_spec.py @@ -0,0 +1,76 @@ +# SPDX-Copyright: Copyright (c) Capital One Services, LLC +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2020 Capital One Services, 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. + +""" +Run the GIT commands to refresh the CEL specification and conformance test suite. + +Synopsis:: + + python tools/refresh_spec.py + +Environment Variables: + +:CEL_SPEC_PATH: + Location of the google/cel-spec repository. + The default path is ``../google/cel-spec``. + +""" + +import os +from pathlib import Path +import re +import shlex +import subprocess +from typing import Optional, Iterator + + +def shell( + command: str, *, cwd: Optional[Path] = None +) -> subprocess.CompletedProcess[str]: + command_parsed = shlex.split(command) + try: + result = subprocess.run( + command_parsed, cwd=cwd, text=True, check=True, capture_output=True + ) + except subprocess.CalledProcessError as ex: + print(f"Returncode {ex.returncode} from {command!r}") + print(ex.stderr) + raise + return result + + +def tag_filter(stdout: str) -> Iterator[tuple[int, int, int, str]]: + for line in stdout.splitlines(): + if match := re.match(r"v(\d+)\.(\d+)\.(\d+)", line): + major, minor, patch = match.groups() + yield (int(major), int(minor), int(patch), line) + + +def main() -> None: + base = Path( + os.environ.get("CEL_SPEC_PATH", Path.cwd().parent / "google" / "cel-spec") + ) + # tests = base / "tests" / "simple" / "testdata" + tags_process = shell("git tag --list", cwd=base) + major, minor, patch, max_tag = max(tag_filter(tags_process.stdout)) + print(f"Fetching tag {max_tag}") + pull_process = shell(f"git pull origin tag {max_tag}", cwd=base) + print(pull_process.stdout) + log_process = shell("git log -n 1 --oneline --tags", cwd=base) + print(f"Last commit: {log_process.stdout}") + + +if __name__ == "__main__": + main() diff --git a/tools/test_survey.ipynb b/tools/survey_feature_files.ipynb similarity index 99% rename from tools/test_survey.ipynb rename to tools/survey_feature_files.ipynb index 924f027..5d510e8 100644 --- a/tools/test_survey.ipynb +++ b/tools/survey_feature_files.ipynb @@ -1,5 +1,28 @@ { "cells": [ + { + "metadata": {}, + "cell_type": "code", + "outputs": [], + "execution_count": null, + "source": [ + "# SPDX-Copyright: Copyright (c) Capital One Services, LLC\n", + "# SPDX-License-Identifier: Apache-2.0\n", + "# Copyright 2020 Capital One Services, LLC\n", + "#\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# http://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and limitations under the License." + ], + "id": "f90f378c14957266" + }, { "cell_type": "markdown", "id": "ca1b43d654b8d852", diff --git a/tools/test_refresh_spec.py b/tools/test_refresh_spec.py new file mode 100644 index 0000000..88cf312 --- /dev/null +++ b/tools/test_refresh_spec.py @@ -0,0 +1,120 @@ +# SPDX-Copyright: Copyright (c) Capital One Services, LLC +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2020 Capital One Services, 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. + +""" +Test the refresh_spec tool. +""" + +import subprocess +from unittest.mock import Mock, call, sentinel, ANY + +from pytest import fixture, MonkeyPatch, CaptureFixture, raises + +import refresh_spec + + +@fixture +def mock_subprocess(monkeypatch: MonkeyPatch) -> Mock: + subprocess_mock = Mock(spec=subprocess, run=Mock(return_value=sentinel.COMPLETED)) + monkeypatch.setattr(refresh_spec, "subprocess", subprocess_mock) + return subprocess_mock + + +def test_shell(mock_subprocess: Mock) -> None: + result = refresh_spec.shell("some command") + assert result == sentinel.COMPLETED + assert mock_subprocess.run.mock_calls == [ + call(["some", "command"], cwd=ANY, text=True, check=True, capture_output=True) + ] + + +@fixture +def mock_subprocess_fail(monkeypatch: MonkeyPatch) -> Mock: + subprocess_mock = Mock( + spec=subprocess, + CalledProcessError=subprocess.CalledProcessError, + run=Mock( + side_effect=subprocess.CalledProcessError( + sentinel.CODE, sentinel.COMMAND, stderr=sentinel.MESSAGE + ) + ), + ) + monkeypatch.setattr(refresh_spec, "subprocess", subprocess_mock) + return subprocess_mock + + +def test_shell_fail(mock_subprocess_fail: Mock, capsys: CaptureFixture[str]) -> None: + with raises(BaseException) as exc_info: + refresh_spec.shell("some command") + assert exc_info.value.args == (sentinel.CODE, sentinel.COMMAND) + out, err = capsys.readouterr() + assert out == "Returncode sentinel.CODE from 'some command'\nsentinel.MESSAGE\n" + assert mock_subprocess_fail.run.mock_calls == [ + call(["some", "command"], cwd=ANY, text=True, check=True, capture_output=True) + ] + + +def test_tag_filter() -> None: + result = max(refresh_spec.tag_filter("words\nv0.1.2\nv0.2.3\nwords\n")) + assert result == (0, 2, 3, "v0.2.3") + + +@fixture +def mock_subprocess_output(monkeypatch: MonkeyPatch) -> Mock: + subprocess_mock = Mock( + spec=subprocess, + run=Mock( + side_effect=[ + Mock(stdout="v1.2.3\n"), + Mock(stdout="pull output"), + Mock(stdout="log output"), + ] + ), + ) + monkeypatch.setattr(refresh_spec, "subprocess", subprocess_mock) + return subprocess_mock + + +def test_main(mock_subprocess_output: Mock, capsys: CaptureFixture[str]) -> None: + refresh_spec.main() + assert mock_subprocess_output.run.mock_calls == [ + call( + ["git", "tag", "--list"], + cwd=ANY, + text=True, + check=True, + capture_output=True, + ), + call( + ["git", "pull", "origin", "tag", "v1.2.3"], + cwd=ANY, + text=True, + check=True, + capture_output=True, + ), + call( + ["git", "log", "-n", "1", "--oneline", "--tags"], + cwd=ANY, + text=True, + check=True, + capture_output=True, + ), + ] + out, err = capsys.readouterr() + assert out.splitlines() == [ + "Fetching tag v1.2.3", + "pull output", + "Last commit: log output", + ] diff --git a/tools/wip.toml b/tools/wip.toml index b24e691..f5018f2 100644 --- a/tools/wip.toml +++ b/tools/wip.toml @@ -1,6 +1,6 @@ # The value for each scenario can be a string tag, an array of tags or a -# dictionary with a `tags` key containing an array of tags. For additional -# documentation, see the Config class in gherkinize.py. +# dictionary with a `tags` key containing an array of tags. +# For additional documentation, see the `Config` class in `gherkinize.py`. [bindings_ext.bind] bind_nested = "@wip" @@ -8,6 +8,9 @@ boolean_literal = "@wip" macro_exists = "@wip" macro_not_exists = "@wip" string_concat = "@wip" +shadowing = "@wip" +shadowing_namespace_resolution = "@wip" +shadowing_namespace_resolution_selector = "@wip" [block_ext.basic] adjacent_macros = "@wip" @@ -670,7 +673,7 @@ ternary_with_int = "@wip" unary = "@wip" [math_ext.round] -nan = "@wip" +"nan" = "@wip" negative_down = "@wip" negative_mid = "@wip" negative_up = "@wip" @@ -688,10 +691,24 @@ zero_int = "@wip" zero_uint = "@wip" [math_ext.trunc] -nan = "@wip" +"nan" = "@wip" negative = "@wip" positive = "@wip" +[namespace.namespace_shadowing] +basic = "@wip" +disambiguation = "@wip" +comprehension_shadowing = "@wip" +comprehension_shadowing_disambiguation = "@wip" +comprehension_shadowing_parse_only = "@wip" +comprehension_shadowing_selector = "@wip" +comprehension_shadowing_selector_parse_only = "@wip" +comprehension_shadowing_namespaced_selector = "@wip" +comprehension_shadowing_namespaced_selector_parse_only = "@wip" +comprehension_shadowing_namespaced_selector_disambiguation = "@wip" +comprehension_shadowing_nesting = "@wip" + + [optionals.optionals] empty_list_optindex_hasValue = "@wip" empty_map_optFlatMap_hasValue = "@wip" @@ -763,6 +780,227 @@ type = "@wip" [parse.comments] new_line_terminated = "@wip" +[parse.bytes_literals] +single_quoted = "@wip" +double_quoted = "@wip" +triple_single_quoted = "@wip" +triple_double_quoted = "@wip" +single_quoted_escaped_punctuation = "@wip" +double_quoted_escaped_punctuation = "@wip" +triple_single_quoted_escaped_punctuation = "@wip" +triple_double_quoted_escaped_punctuation = "@wip" +triple_single_quoted_unescaped_punctuation = "@wip" +triple_double_quoted_unescaped_punctuation = "@wip" +single_quoted_escaped_special_control_characters = "@wip" +double_quoted_escaped_special_control_characters = "@wip" +single_quoted_unescaped_special_control_characters = "@wip" +double_quoted_unescaped_special_control_characters = "@wip" +triple_single_quoted_escaped_special_control_characters = "@wip" +triple_double_quoted_escaped_special_control_characters = "@wip" +triple_single_quoted_unescaped_special_control_characters = "@wip" +triple_double_quoted_unescaped_special_control_characters = "@wip" +single_quoted_escaped_line_feed = "@wip" +double_quoted_escaped_line_feed = "@wip" +triple_single_quoted_escaped_line_feed = "@wip" +triple_double_quoted_escaped_line_feed = "@wip" +triple_single_quoted_unescaped_line_feed = "@wip" +triple_double_quoted_unescaped_line_feed = "@wip" +single_quoted_escaped_carriage_return = "@wip" +double_quoted_escaped_carriage_return = "@wip" +triple_single_quoted_escaped_carriage_return = "@wip" +triple_double_quoted_escaped_carriage_return = "@wip" +single_quoted_escaped_windows_line_end = "@wip" +double_quoted_escaped_windows_line_end = "@wip" +triple_single_quoted_escaped_windows_line_end = "@wip" +triple_double_quoted_escaped_windows_line_end = "@wip" +single_quoted_escaped_all_control_characters = "@wip" +double_quoted_escaped_all_control_characters = "@wip" +single_quoted_unescaped_all_control_characters = "@wip" +double_quoted_unescaped_all_control_characters = "@wip" +triple_single_quoted_escaped_all_control_characters = "@wip" +triple_double_quoted_escaped_all_control_characters = "@wip" +triple_single_quoted_unescaped_all_control_characters = "@wip" +triple_double_quoted_unescaped_all_control_characters = "@wip" +single_quoted_octal_escapes = "@wip" +double_quoted_octal_escapes = "@wip" +triple_single_quoted_octal_escapes = "@wip" +triple_double_quoted_octal_escapes = "@wip" +single_quoted_lower_x_escapes = "@wip" +double_quoted_lower_x_escapes = "@wip" +triple_single_quoted_lower_x_escapes = "@wip" +triple_double_quoted_lower_x_escapes = "@wip" +single_quoted_upper_x_escapes = "@wip" +double_quoted_upper_x_escapes = "@wip" +triple_single_quoted_upper_x_escapes = "@wip" +triple_double_quoted_upper_x_escapes = "@wip" +single_quoted_lower_u_escapes = "@wip" +double_quoted_lower_u_escapes = "@wip" +triple_single_quoted_lower_u_escapes = "@wip" +triple_double_quoted_lower_u_escapes = "@wip" +single_quoted_upper_u_escapes = "@wip" +double_quoted_upper_u_escapes = "@wip" +triple_single_quoted_upper_u_escapes = "@wip" +triple_double_quoted_upper_u_escapes = "@wip" +mixed_case_hex_single_quoted_escapes = "@wip" +mixed_case_hex_double_quoted_escapes = "@wip" +mixed_case_hex_triple_single_quoted_escapes = "@wip" +mixed_case_hex_triple_double_quoted_escapes = "@wip" +unassigned_code_point_single_quoted_escapes = "@wip" +unassigned_code_point_double_quoted_escapes = "@wip" +unassigned_code_point_triple_single_quoted_escapes = "@wip" +unassigned_code_point_triple_double_quoted_escapes = "@wip" +unassigned_code_point_single_quoted_unescaped = "@wip" +unassigned_code_point_double_quoted_unescaped = "@wip" +unassigned_code_point_triple_single_quoted_unescaped = "@wip" +unassigned_code_point_triple_double_quoted_unescaped = "@wip" +raw_single_quoted_escapes = "@wip" +raw_double_quoted_escapes = "@wip" +raw_triple_single_quoted_escapes = "@wip" +raw_triple_double_quoted_escapes = "@wip" +upper_raw_single_quoted_escapes = "@wip" +upper_raw_double_quoted_escapes = "@wip" +upper_raw_triple_single_quoted_escapes = "@wip" +upper_raw_triple_double_quoted_escapes = "@wip" + +[parse.string_literals] +single_quoted = "@wip" +double_quoted = "@wip" +triple_single_quoted = "@wip" +triple_double_quoted = "@wip" +single_quoted_escaped_punctuation = "@wip" +double_quoted_escaped_punctuation = "@wip" +triple_single_quoted_escaped_punctuation = "@wip" +triple_double_quoted_escaped_punctuation = "@wip" +triple_single_quoted_unescaped_punctuation = "@wip" +triple_double_quoted_unescaped_punctuation = "@wip" +single_quoted_escaped_special_control_characters = "@wip" +double_quoted_escaped_special_control_characters = "@wip" +single_quoted_unescaped_special_control_characters = "@wip" +double_quoted_unescaped_special_control_characters = "@wip" +triple_single_quoted_escaped_special_control_characters = "@wip" +triple_double_quoted_escaped_special_control_characters = "@wip" +triple_single_quoted_unescaped_special_control_characters = "@wip" +triple_double_quoted_unescaped_special_control_characters = "@wip" +single_quoted_escaped_line_feed = "@wip" +double_quoted_escaped_line_feed = "@wip" +triple_single_quoted_escaped_line_feed = "@wip" +triple_double_quoted_escaped_line_feed = "@wip" +triple_single_quoted_unescaped_line_feed = "@wip" +triple_double_quoted_unescaped_line_feed = "@wip" +single_quoted_escaped_carriage_return = "@wip" +double_quoted_escaped_carriage_return = "@wip" +triple_single_quoted_escaped_carriage_return = "@wip" +triple_double_quoted_escaped_carriage_return = "@wip" +single_quoted_escaped_windows_line_end = "@wip" +double_quoted_escaped_windows_line_end = "@wip" +triple_single_quoted_escaped_windows_line_end = "@wip" +triple_double_quoted_escaped_windows_line_end = "@wip" +single_quoted_escaped_all_control_characters = "@wip" +double_quoted_escaped_all_control_characters = "@wip" +single_quoted_unescaped_all_control_characters = "@wip" +double_quoted_unescaped_all_control_characters = "@wip" +triple_single_quoted_escaped_all_control_characters = "@wip" +triple_double_quoted_escaped_all_control_characters = "@wip" +triple_single_quoted_unescaped_all_control_characters = "@wip" +triple_double_quoted_unescaped_all_control_characters = "@wip" +single_quoted_octal_escapes = "@wip" +double_quoted_octal_escapes = "@wip" +triple_single_quoted_octal_escapes = "@wip" +triple_double_quoted_octal_escapes = "@wip" +single_quoted_lower_x_escapes = "@wip" +double_quoted_lower_x_escapes = "@wip" +triple_single_quoted_lower_x_escapes = "@wip" +triple_double_quoted_lower_x_escapes = "@wip" +single_quoted_upper_x_escapes = "@wip" +double_quoted_upper_x_escapes = "@wip" +triple_single_quoted_upper_x_escapes = "@wip" +triple_double_quoted_upper_x_escapes = "@wip" +single_quoted_lower_u_escapes = "@wip" +double_quoted_lower_u_escapes = "@wip" +triple_single_quoted_lower_u_escapes = "@wip" +triple_double_quoted_lower_u_escapes = "@wip" +single_quoted_upper_u_escapes = "@wip" +double_quoted_upper_u_escapes = "@wip" +triple_single_quoted_upper_u_escapes = "@wip" +triple_double_quoted_upper_u_escapes = "@wip" +mixed_case_hex_single_quoted_escapes = "@wip" +mixed_case_hex_double_quoted_escapes = "@wip" +mixed_case_hex_triple_single_quoted_escapes = "@wip" +mixed_case_hex_triple_double_quoted_escapes = "@wip" +unassigned_code_point_single_quoted_escapes = "@wip" +unassigned_code_point_double_quoted_escapes = "@wip" +unassigned_code_point_triple_single_quoted_escapes = "@wip" +unassigned_code_point_triple_double_quoted_escapes = "@wip" +unassigned_code_point_single_quoted_unescaped = "@wip" +unassigned_code_point_double_quoted_unescaped = "@wip" +unassigned_code_point_triple_single_quoted_unescaped = "@wip" +unassigned_code_point_triple_double_quoted_unescaped = "@wip" +raw_single_quoted_escapes = "@wip" +raw_double_quoted_escapes = "@wip" +raw_triple_single_quoted_escapes = "@wip" +raw_triple_double_quoted_escapes = "@wip" +upper_raw_single_quoted_escapes = "@wip" +upper_raw_double_quoted_escapes = "@wip" +upper_raw_triple_single_quoted_escapes = "@wip" +upper_raw_triple_double_quoted_escapes = "@wip" + +[parse.selectors] +as = "@wip" +break = "@wip" +const = "@wip" +continue = "@wip" +else = "@wip" +for = "@wip" +function = "@wip" +if = "@wip" +import = "@wip" +let = "@wip" +loop = "@wip" +package = "@wip" +namespace = "@wip" +return = "@wip" +var = "@wip" +void = "@wip" +while = "@wip" + +[parse.receiver_function_names] +as = "@wip" +break = "@wip" +const = "@wip" +continue = "@wip" +else = "@wip" +for = "@wip" +function = "@wip" +if = "@wip" +import = "@wip" +let = "@wip" +loop = "@wip" +package = "@wip" +namespace = "@wip" +return = "@wip" +var = "@wip" +void = "@wip" +while = "@wip" + +[parse.struct_field_names] +as = "@wip" +break = "@wip" +const = "@wip" +continue = "@wip" +else = "@wip" +for = "@wip" +function = "@wip" +if = "@wip" +import = "@wip" +let = "@wip" +loop = "@wip" +package = "@wip" +namespace = "@wip" +return = "@wip" +var = "@wip" +void = "@wip" +while = "@wip" + [parse.whitespace] carriage_returns = "@wip" new_lines = "@wip" @@ -849,6 +1087,16 @@ map = "@wip" repeated = "@wip" single_scalar = "@wip" single_struct = "@wip" +repeated_field_timestamp_null_pruned = "@wip" +repeated_field_duration_null_pruned = "@wip" +repeated_field_wrapper_null_pruned = "@wip" +repeated_field_anytype_null_retained = "@wip" +repeated_field_jsontype_null_retained = "@wip" +map_timestamp_null_pruned = "@wip" +map_duration_null_pruned = "@wip" +map_wrapper_null_pruned = "@wip" +map_anytype_null_retained = "@wip" +map_jsontype_null_retained = "@wip" [proto2_ext.get_ext] message_scoped_int64 = "@wip" @@ -915,6 +1163,16 @@ map = "@wip" repeated = "@wip" single_scalar = "@wip" single_struct = "@wip" +repeated_field_timestamp_null_pruned = "@wip" +repeated_field_duration_null_pruned = "@wip" +repeated_field_wrapper_null_pruned = "@wip" +repeated_field_anytype_null_retained = "@wip" +repeated_field_jsontype_null_retained = "@wip" +map_timestamp_null_pruned = "@wip" +map_duration_null_pruned = "@wip" +map_wrapper_null_pruned = "@wip" +map_anytype_null_retained = "@wip" +map_jsontype_null_retained = "@wip" [string_ext.ascii_casing] lowerascii = "@wip" @@ -942,7 +1200,11 @@ multiple = "@wip" "bytes support for string" = "@wip" "default precision for fixed-point clause" = "@wip" "default precision for fixed-point clause in a string variable" = "@wip" +"default precision for fixed-point clause with int" = "@wip" +"default precision for fixed-point clause with uint" = "@wip" "default precision for scientific notation" = "@wip" +"default precision for scientific notation with int" = "@wip" +"default precision for scientific notation with uint" = "@wip" "duration support for string" = "@wip" "dyntype NaN/infinity support" = "@wip" "dyntype support for duration" = "@wip" @@ -960,6 +1222,18 @@ multiple = "@wip" "dyntype support for unsigned hex formatting clause" = "@wip" "fixed point formatting clause" = "@wip" "fixed point formatting clause in a string variable" = "@wip" + +"format_%f_insignificant_zeroes_removed" = "@wip" +"format_%f_positive_round_to_whole_number" = "@wip" +"format_%f_negative_truncate_to_whole_number" = "@wip" +"format_%f_halfway_round_up_to_nearest_even" = "@wip" +"format_%f_halfway_truncate_to_nearest_even" = "@wip" +"format_%f_positive_round_up" = "@wip" +"format_%f_positive_round_down" = "@wip" +"format_%f_negative_round_up" = "@wip" +"format_%f_negative_round_down" = "@wip" +"format_%f_zero_padding" = "@wip" + "int support for string" = "@wip" "list support for string" = "@wip" "lowercase hexadecimal formatting clause" = "@wip" @@ -1019,6 +1293,8 @@ empty_string_empty_separator = "@wip" char_index = "@wip" empty = "@wip" empty_index = "@wip" +empty_argument = "@wip" +empty_string = "@wip" repeated_string = "@wip" string = "@wip" string_nomatch = "@wip" @@ -1059,6 +1335,11 @@ chained = "@wip" no_placeholder = "@wip" unicode = "@wip" +[string_ext.reverse] +empty = "@wip" +single_character = "@wip" +multiple = "@wip" + [string_ext.split] empty = "@wip" one_limit = "@wip"