diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index 31b2fd442..7c7015872 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -1616,93 +1616,25 @@ or Tags are defined as a comma separated list within the `--%tags` annotation. -When executing a test run with tag filter applied, the framework will find all tests associated with the given tags and execute them. -The framework applies `OR` logic to all specified tags so any test / suite that matches at least one tag will be included in the test run. - -When a suite/context is tagged, all of its children will automatically inherit the tag and get executed along with the parent. Parent suite tests are not executed, but a suitepath hierarchy is kept. - - -Sample test suite package with tags. -```sql linenums="1" -create or replace package ut_sample_test is - - --%suite(Sample Test Suite) - --%tags(api) - - --%test(Compare Ref Cursors) - --%tags(complex,fast) - procedure ut_refcursors1; - - --%test(Run equality test) - --%tags(simple,fast) - procedure ut_test; - -end ut_sample_test; -/ - -create or replace package body ut_sample_test is - - procedure ut_refcursors1 is - v_actual sys_refcursor; - v_expected sys_refcursor; - begin - open v_expected for select 1 as test from dual; - open v_actual for select 2 as test from dual; - - ut.expect(v_actual).to_equal(v_expected); - end; - - procedure ut_test is - begin - ut.expect(1).to_equal(0); - end; - -end ut_sample_test; -/ -``` - -Execution of the test is done by using the parameter `a_tags` - -```sql linenums="1" -select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'api')); -``` -The above call will execute all tests from `ut_sample_test` package as the whole suite is tagged with `api` - -```sql linenums="1" -select * from table(ut.run(a_tags => 'complex')); -``` -The above call will execute only the `ut_sample_test.ut_refcursors1` test, as only the test `ut_refcursors1` is tagged with `complex` - -```sql linenums="1" -select * from table(ut.run(a_tags => 'fast')); -``` -The above call will execute both `ut_sample_test.ut_refcursors1` and `ut_sample_test.ut_test` tests, as both tests are tagged with `fast` +When a suite/context is tagged, all of its children will automatically inherit the tag and get executed along with the parent, unless they are excluded explicitly at runtime with a negated tag expression. +See [running unit tests](running-unit-tests.md) for more information on using tags to filter test suites that are to be executed. #### Tag naming convention Tags must follow the below naming convention: - tag is case sensitive -- tag can contain special characters like `$#/\?-!` etc. -- tag cannot be an empty string +- tag must not contain any of the following reserved characters: + - comma (,) + - left or right parenthesis ((, )) + - ampersand (&) + - vertical bar (|) + - exclamation point (!) +- tag cannot be null or blank - tag cannot start with a dash, e.g. `-some-stuff` is **not** a valid tag - tag cannot contain spaces, e.g. `test of batch`. To create a multi-word tag use underscores or dashes, e.g. `test_of_batch`, `test-of-batch` - leading and trailing spaces are ignored in tag name, e.g. `--%tags( tag1 , tag2 )` becomes `tag1` and `tag2` tag names - - -#### Excluding tests/suites by tags - -It is possible to exclude parts of test suites with tags. -In order to do so, prefix the tag name to exclude with a `-` (dash) sign when invoking the test run. - -Examples (based on above sample test suite) - -```sql linenums="1" -select * from table(ut.run(a_tags => 'api,fast,-complex')); -``` -The above call will execute all suites/contexts/tests that are marked with any of tags `api` or `fast` except those suites/contexts/tests that are marked as `complex`. -Given the above example package `ut_sample_test`, only `ut_sample_test.ut_test` will be executed. - +- tag cannot be one of two reserved words: `none` and `any`. `none` and `any` as a tag will be treated as no tag ### Suitepath diff --git a/docs/userguide/running-unit-tests.md b/docs/userguide/running-unit-tests.md index 978803b9a..2b18a3d3b 100644 --- a/docs/userguide/running-unit-tests.md +++ b/docs/userguide/running-unit-tests.md @@ -319,22 +319,218 @@ select * from table(ut.run('hr.test_apply_bonus', a_random_test_order_seed => 30 In addition to the path, you can filter the tests to be run by specifying tags. Tags are defined in the test / context / suite with the `--%tags`-annotation ([Read more](annotations.md#tags)). Multiple tags are separated by comma. -The framework applies `OR` logic to all specified tags so any test / suite that matches at least one tag will be included in the test run. + + +### Tag Expressions + +Tag expressions are boolean expressions created by combining tags with the `!`, `&`, `|` operators. Tag expressions can be grouped using `(` and `)` braces. Grouping tag expressions affects operator precedence. + +Two reserved keywords, `any` and `none`, can be used when creating a tag expression to run tests. +- `any` keyword represents tests and suites with any tags +- `none` keyword represents tests and suites without tags + +These keywords may be combined with other expressions just like normal tags. + +!!! note + When specifying `none`, be aware that it will exclude any tests/suites/contexts contained within a tagged suite. + +| Operator | Meaning | +| -------- | --------| +| ! | not | +| & | and | +| \| | or | + +If you are tagging your tests across multiple dimensions, tag expressions help you to select which tests to execute. When tagging by test type (e.g., micro, integration, end-to-end) and feature (e.g., product, catalog, shipping), the following tag expressions can be useful. + + +| Tag Expression | Selection | +| -------- | --------| +| product | all tests for product | +| catalog \| shipping | all tests for catalog plus all tests for shipping | +| catalog & shipping | all tests that are tagged with both `catalog` and `shipping` tags | +| product & !end-to-end | all tests tagged `product`, except the tests tagged `end-to-end` | +| (micro \| integration) & (product \| shipping) | all micro or integration tests for product or shipping | + + +Taking the last expression above `(micro | integration) & (product | shipping)` + +| --%tags |included in run | +| -------- | --------| +| micro | no | +| integration | no | +| micro | no | +| product | no | +| shipping | no | +| micro | no | +| micro, integration | no | +| product, shipping | no | +| micro, product | yes | +| micro, shipping | yes | +| integration, product | yes | +| integration, shipping | yes | +| integration, micro, shipping | yes | +| integration, micro, product | yes | +| integration, shipping ,product | yes | +| micro, shipping ,product | yes | +| integration, micro, shipping ,product | yes | + + +### Sample execution of test with tags. + +Execution of the test with tag expressions is done using the parameter `a_tags`. +Given a test package `ut_sample_test` defined below ```sql linenums="1" -begin - ut.run('hr.test_apply_bonus', a_tags => 'test1,test2'); -end; +create or replace package ut_sample_test is + + --%suite(Sample Test Suite) + --%tags(api) + + --%test(Compare Ref Cursors) + --%tags(complex,fast) + procedure ut_refcursors1; + + --%test(Run equality test) + --%tags(simple,fast) + procedure ut_test; + +end ut_sample_test; +/ + +create or replace package body ut_sample_test is + + procedure ut_refcursors1 is + v_actual sys_refcursor; + v_expected sys_refcursor; + begin + open v_expected for select 1 as test from dual; + open v_actual for select 2 as test from dual; + + ut.expect(v_actual).to_equal(v_expected); + end; + + procedure ut_test is + begin + ut.expect(1).to_equal(0); + end; + +end ut_sample_test; +/ ``` + ```sql linenums="1" -select * from table(ut.run('hr.test_apply_bonus', a_tags => 'suite1')) +select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'api')); ``` +The above call will execute all tests from `ut_sample_test` package as the whole suite is tagged with `api` -You can also exclude specific tags by adding a `-` (dash) in front of the tag +```sql linenums="1" +select * from table(ut.run(a_tags => 'fast&complex')); +``` +The above call will execute only the `ut_sample_test.ut_refcursors1` test, as only the test `ut_refcursors1` is tagged with `complex` and `fast` ```sql linenums="1" -select * from table(ut.run('hr.test_apply_bonus', a_tags => '-suite1')) +select * from table(ut.run(a_tags => 'fast')); ``` +The above call will execute both `ut_sample_test.ut_refcursors1` and `ut_sample_test.ut_test` tests, as both tests are tagged with `fast` + +### Excluding tests/suites by tags + +It is possible to exclude parts of test suites with tags. +In order to do so, prefix the tag name to exclude with a `!` (exclamation) sign when invoking the test run which is equivalent of `-` (dash) in legacy notation. +Examples (based on above sample test suite) + +```sql linenums="1" +select * from table(ut.run(a_tags => '(api|fast)&!complex')); +``` + +or + +```sql linenums="1" +select * from table(ut.run(a_tags => '(api|fast)&!complex&!test1')); +``` + +which is equivalent of exclusion on whole expression + +```sql linenums="1" +select * from table(ut.run(a_tags => '(api|fast)&!(complex|test1)')); +``` + +The above calls will execute all suites/contexts/tests that are marked with any of tags `api` or `fast` except those suites/contexts/tests that are marked as `complex` and except those suites/contexts/tests that are marked as `test1`. +Given the above example package `ut_sample_test`, only `ut_sample_test.ut_test` will be executed. + + +### Sample execution with `any` and `none` + +Given a sample test package: + +```sql linenums="1" +create or replace package ut_sample_test is + + --%suite(Sample Test Suite) + + --%test(Compare Ref Cursors) + --%tags(complex,fast) + procedure ut_refcursors1; + + --%test(Run equality test) + --%tags(simple,fast) + procedure ut_test; + + --%test(Run equality test no tag) + procedure ut_test_no_tag; + +end ut_sample_test; +/ + +create or replace package body ut_sample_test is + + procedure ut_refcursors1 is + v_actual sys_refcursor; + v_expected sys_refcursor; + begin + open v_expected for select 1 as test from dual; + open v_actual for select 2 as test from dual; + + ut.expect(v_actual).to_equal(v_expected); + end; + + procedure ut_test is + begin + ut.expect(1).to_equal(0); + end; + + procedure ut_test_no_tag is + begin + ut.expect(1).to_equal(0); + end; + +end ut_sample_test; +/ +``` + +```sql linenums="1" +select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'none')); +``` + +The above call will execute tests `ut_test_no_tag` + +```sql linenums="1" +select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'any')); +``` + +The above call will execute tests `ut_test` and `ut_refcursors1` + +```sql linenums="1" +select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'none|simple')); +``` + +The above call will execute tests `ut_test_no_tag` and `ut_test` + +```sql linenums="1" +select * from table(ut.run(a_tags => 'none|!simple')); +``` + +The above call will execute tests `ut_test_no_tag` and `ut_refcursors1` ## Keeping uncommitted data after test-run diff --git a/source/api/ut_runner.pkb b/source/api/ut_runner.pkb index b69a51a04..3ec2a5393 100644 --- a/source/api/ut_runner.pkb +++ b/source/api/ut_runner.pkb @@ -78,8 +78,7 @@ create or replace package body ut_runner is l_run ut_run; l_coverage_schema_names ut_varchar2_rows; l_paths ut_varchar2_list; - l_random_test_order_seed positive; - l_tags ut_varchar2_rows := ut_varchar2_rows(); + l_random_test_order_seed positive; begin ut_event_manager.initialize(); if a_reporters is not empty then @@ -118,12 +117,6 @@ create or replace package body ut_runner is l_coverage_schema_names := ut_suite_manager.get_schema_names(l_paths); end if; - - if a_tags is not null then - l_tags := l_tags multiset union distinct ut_utils.convert_collection( - ut_utils.trim_list_elements(ut_utils.filter_list(ut_utils.string_to_table(a_tags,','),ut_utils.gc_word_no_space)) - ); - end if; l_run := ut_run( a_run_paths => l_paths, a_coverage_options => ut_coverage_options( @@ -140,10 +133,10 @@ create or replace package body ut_runner is a_test_file_mappings => set(a_test_file_mappings), a_client_character_set => a_client_character_set, a_random_test_order_seed => l_random_test_order_seed, - a_run_tags => l_tags + a_run_tags => a_tags ); - ut_suite_manager.configure_execution_by_path(l_paths, l_run.items, l_random_test_order_seed, l_tags); + ut_suite_manager.configure_execution_by_path(l_paths, l_run.items, l_random_test_order_seed, a_tags); if a_force_manual_rollback then l_run.set_rollback_type( a_rollback_type => ut_utils.gc_rollback_manual, a_force => true ); end if; diff --git a/source/core/types/ut_run.tpb b/source/core/types/ut_run.tpb index cdafb30fc..660c88791 100644 --- a/source/core/types/ut_run.tpb +++ b/source/core/types/ut_run.tpb @@ -24,7 +24,7 @@ create or replace type body ut_run as a_test_file_mappings ut_file_mappings := null, a_client_character_set varchar2 := null, a_random_test_order_seed positive := null, - a_run_tags ut_varchar2_rows := null + a_run_tags varchar2 := null ) return self as result is begin self.run_paths := a_run_paths; diff --git a/source/core/types/ut_run.tps b/source/core/types/ut_run.tps index debf847cd..1878a2d46 100644 --- a/source/core/types/ut_run.tps +++ b/source/core/types/ut_run.tps @@ -21,7 +21,7 @@ create or replace type ut_run under ut_suite_item ( project_name varchar2(4000), items ut_suite_items, run_paths ut_varchar2_list, - run_tags ut_varchar2_rows, + run_tags varchar2(4000), coverage_options ut_coverage_options, test_file_mappings ut_file_mappings, client_character_set varchar2(100), @@ -34,7 +34,7 @@ create or replace type ut_run under ut_suite_item ( a_test_file_mappings ut_file_mappings := null, a_client_character_set varchar2 := null, a_random_test_order_seed positive := null, - a_run_tags ut_varchar2_rows := null + a_run_tags varchar2 := null ) return self as result, overriding member procedure mark_as_skipped(self in out nocopy ut_run,a_skip_reason in varchar2), overriding member function do_execute(self in out nocopy ut_run) return boolean, diff --git a/source/core/types/ut_stack.tpb b/source/core/types/ut_stack.tpb new file mode 100644 index 000000000..b5f4e8747 --- /dev/null +++ b/source/core/types/ut_stack.tpb @@ -0,0 +1,58 @@ +create or replace type body ut_stack as + /* + utPLSQL - Version 3 + Copyright 2016 - 2023 utPLSQL Project + + 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. + */ + + constructor function ut_stack( self in out nocopy ut_stack) return self as result is + begin + self.tokens := ut_varchar2_list(); + self.top := 0; + return; + end ut_stack; + + member function peek(self in out nocopy ut_stack) return varchar2 is + l_token varchar2(32767); + begin + if self.tokens.count =0 or self.tokens is null then + l_token := null; + else + l_token := self.tokens(self.tokens.last); + end if; + return l_token; + end; + + member procedure push(self in out nocopy ut_stack, a_token varchar2) is + begin + self.tokens.extend; + self.tokens(self.tokens.last) := a_token; + self.top := self.tokens.count; + end push; + + member procedure pop(self in out nocopy ut_stack,a_cnt in integer default 1) is + begin + self.tokens.trim(a_cnt); + self.top := self.tokens.count; + end pop; + + member function pop(self in out nocopy ut_stack) return varchar2 is + l_token varchar2(32767) := self.tokens(self.tokens.last); + begin + self.pop(); + return l_token; + end; +end; +/ + diff --git a/source/core/types/ut_stack.tps b/source/core/types/ut_stack.tps new file mode 100644 index 000000000..23112fdde --- /dev/null +++ b/source/core/types/ut_stack.tps @@ -0,0 +1,26 @@ +create or replace type ut_stack as object ( + top integer, + tokens ut_varchar2_list, + /* + utPLSQL - Version 3 + Copyright 2016 - 2023 utPLSQL Project + + 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. + */ + constructor function ut_stack( self in out nocopy ut_stack) return self as result, + member function peek(self in out nocopy ut_stack) return varchar2, + member procedure push(self in out nocopy ut_stack, a_token varchar2), + member procedure pop(self in out nocopy ut_stack,a_cnt in integer default 1), + member function pop(self in out nocopy ut_stack) return varchar2 +) +/ \ No newline at end of file diff --git a/source/core/ut_suite_cache_manager.pkb b/source/core/ut_suite_cache_manager.pkb index d3e832a9b..680624f02 100644 --- a/source/core/ut_suite_cache_manager.pkb +++ b/source/core/ut_suite_cache_manager.pkb @@ -220,55 +220,7 @@ create or replace package body ut_suite_cache_manager is and c.name = nvl(upper(sp.procedure_name),c.name)))) where r_num =1; return l_suite_items; end; - - /* - Having a base set of suites we will do a further filter down if there are - any tags defined. - */ - function get_tags_suites ( - a_suite_items ut_suite_cache_rows, - a_tags ut_varchar2_rows - ) return ut_suite_cache_rows is - l_suite_tags ut_suite_cache_rows := ut_suite_cache_rows(); - l_include_tags ut_varchar2_rows; - l_exclude_tags ut_varchar2_rows; - begin - - select /*+ no_parallel */ column_value - bulk collect into l_include_tags - from table(a_tags) - where column_value not like '-%'; - - select /*+ no_parallel */ ltrim(column_value,'-') - bulk collect into l_exclude_tags - from table(a_tags) - where column_value like '-%'; - - with included_tags as ( - select c.path as path - from table(a_suite_items) c - where c.tags multiset intersect l_include_tags is not empty or l_include_tags is empty - ), - excluded_tags as ( - select c.path as path - from table(a_suite_items) c - where c.tags multiset intersect l_exclude_tags is not empty - ) - select value(c) as obj - bulk collect into l_suite_tags - from table(a_suite_items) c - where exists ( - select 1 from included_tags t - where t.path||'.' like c.path || '.%' /*all ancestors and self*/ - or c.path||'.' like t.path || '.%' /*all descendants and self*/ - ) - and not exists ( - select 1 from excluded_tags t - where c.path||'.' like t.path || '.%' /*all descendants and self*/ - ); - return l_suite_tags; - end; - + /* We will sort a suites in hierarchical structure. Sorting from bottom to top so when we consolidate @@ -321,31 +273,18 @@ create or replace package body ut_suite_cache_manager is end; function get_cached_suite_rows( - a_schema_paths ut_path_items, - a_random_seed positive := null, - a_tags ut_varchar2_rows := null + a_suites_filtered ut_suite_cache_rows ) return ut_suite_cache_rows is l_results ut_suite_cache_rows := ut_suite_cache_rows(); - l_suite_items ut_suite_cache_rows := ut_suite_cache_rows(); - l_schema_paths ut_path_items; - l_tags ut_varchar2_rows := coalesce(a_tags,ut_varchar2_rows()); begin - l_schema_paths := a_schema_paths; - l_suite_items := get_suite_items(a_schema_paths); - if l_tags.count > 0 then - l_suite_items := get_tags_suites(l_suite_items,l_tags); - end if; - - open c_get_bulk_cache_suite(l_suite_items); + open c_get_bulk_cache_suite(a_suites_filtered); fetch c_get_bulk_cache_suite bulk collect into l_results; close c_get_bulk_cache_suite; return l_results; end; - - function get_schema_parse_time(a_schema_name varchar2) return timestamp result_cache is l_cache_parse_time timestamp; begin @@ -492,7 +431,7 @@ create or replace package body ut_suite_cache_manager is a_schema_paths ut_path_items ) return ut_suite_cache_rows is begin - return get_cached_suite_rows( a_schema_paths ); + return get_cached_suite_rows(get_suite_items(a_schema_paths)); end; function get_suite_items_info( diff --git a/source/core/ut_suite_cache_manager.pks b/source/core/ut_suite_cache_manager.pks index 974babca5..81b1e0136 100644 --- a/source/core/ut_suite_cache_manager.pks +++ b/source/core/ut_suite_cache_manager.pks @@ -55,11 +55,9 @@ create or replace package ut_suite_cache_manager authid definer is * Not to be used publicly. Used internally for building suites at runtime. */ function get_cached_suite_rows( - a_schema_paths ut_path_items, - a_random_seed positive := null, - a_tags ut_varchar2_rows := null + a_suites_filtered ut_suite_cache_rows ) return ut_suite_cache_rows; - + function get_schema_paths(a_paths in ut_varchar2_list) return ut_path_items; /* @@ -74,6 +72,10 @@ create or replace package ut_suite_cache_manager authid definer is function get_suite_items_info( a_suite_cache_items ut_suite_cache_rows ) return ut_suite_items_info; + + function get_suite_items ( + a_schema_paths ut_path_items + ) return ut_suite_cache_rows; /* * Retrieves list of cached suite packages. @@ -94,6 +96,6 @@ create or replace package ut_suite_cache_manager authid definer is a_package_name varchar2, a_procedure_name varchar2 ) return boolean; - + end ut_suite_cache_manager; / diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index 86f68c076..c46ba66b8 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -352,20 +352,18 @@ create or replace package body ut_suite_manager is function get_cached_suite_data( a_schema_paths ut_path_items, a_random_seed positive, - a_tags ut_varchar2_rows := null, + a_tags varchar2 := null, a_skip_all_objects boolean := false ) return t_cached_suites_cursor is - l_unfiltered_rows ut_suite_cache_rows; - l_filtered_rows ut_suite_cache_rows; - l_result t_cached_suites_cursor; + l_unfiltered_rows ut_suite_cache_rows; + l_tag_filter_applied ut_suite_cache_rows; + l_filtered_rows ut_suite_cache_rows; + l_result t_cached_suites_cursor; begin - l_unfiltered_rows := ut_suite_cache_manager.get_cached_suite_rows( - a_schema_paths, - a_random_seed, - a_tags - ); + l_unfiltered_rows := ut_suite_cache_manager.get_suite_items(a_schema_paths); - l_filtered_rows := get_filtered_cursor(l_unfiltered_rows,a_skip_all_objects); + l_tag_filter_applied := ut_suite_tag_filter.apply(l_unfiltered_rows,a_tags); + l_filtered_rows := get_filtered_cursor(ut_suite_cache_manager.get_cached_suite_rows(l_tag_filter_applied),a_skip_all_objects); reconcile_paths_and_suites(a_schema_paths,l_filtered_rows); ut_suite_cache_manager.sort_and_randomize_tests(l_filtered_rows,a_random_seed); @@ -451,7 +449,7 @@ create or replace package body ut_suite_manager is a_schema_paths ut_path_items, a_suites in out nocopy ut_suite_items, a_random_seed positive, - a_tags ut_varchar2_rows := null + a_tags varchar2 := null ) is begin reconstruct_from_cache( @@ -528,7 +526,7 @@ create or replace package body ut_suite_manager is a_paths ut_varchar2_list, a_suites out nocopy ut_suite_items, a_random_seed positive := null, - a_tags ut_varchar2_rows := ut_varchar2_rows() + a_tags varchar2 := null ) is l_paths ut_varchar2_list := a_paths; l_schema_names ut_varchar2_rows; diff --git a/source/core/ut_suite_manager.pks b/source/core/ut_suite_manager.pks index 65b3c0654..07539139a 100644 --- a/source/core/ut_suite_manager.pks +++ b/source/core/ut_suite_manager.pks @@ -53,7 +53,7 @@ create or replace package ut_suite_manager authid current_user is a_paths in ut_varchar2_list, a_suites out nocopy ut_suite_items, a_random_seed in positive := null, - a_tags ut_varchar2_rows := ut_varchar2_rows() + a_tags in varchar2 := null ); /** diff --git a/source/core/ut_suite_tag_filter.pkb b/source/core/ut_suite_tag_filter.pkb new file mode 100644 index 000000000..2d9436301 --- /dev/null +++ b/source/core/ut_suite_tag_filter.pkb @@ -0,0 +1,295 @@ +create or replace package body ut_suite_tag_filter is + /* + utPLSQL - Version 3 + Copyright 2016 - 2023 utPLSQL Project + + 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. + */ + + /** + * Constants use in postfix and infix transformations + */ + gc_operators constant ut_varchar2_list := ut_varchar2_list('|','&','!'); + gc_unary_operators constant ut_varchar2_list := ut_varchar2_list('!'); -- right side associative operator + gc_binary_operators constant ut_varchar2_list := ut_varchar2_list('|','&'); -- left side associative operator + gc_reserved_tag_words constant ut_varchar2_list := ut_varchar2_list('none','any'); + gc_tags_column_name constant varchar2(250) := 'tags'; + gc_exception_msg constant varchar2(200) := 'Invalid tag expression'; + + type t_precedence_table is table of number index by varchar2(1); + g_precedence t_precedence_table; + + function tokenize_tags_string(a_tags in varchar2) return ut_varchar2_list is + l_tags_tokens ut_varchar2_list := ut_varchar2_list(); + begin + --Tokenize a string into operators and tags + select regexp_substr(a_tags,'([^!()|&]+)|([!()|&])', 1, level) as string_parts + bulk collect into l_tags_tokens + from dual connect by regexp_substr (a_tags, '([^!()|&]+)|([!()|&])', 1, level) is not null; + + return l_tags_tokens; + end; + + /* + To support a legact tag notation + , = OR + - = NOT + we will perform a replace of that characters into + new notation. + | = OR + & = AND + ! = NOT + */ + function replace_legacy_tag_notation(a_tags varchar2 + ) return varchar2 is + l_tags ut_varchar2_list := ut_utils.string_to_table(a_tags,','); + l_tags_include varchar2(4000); + l_tags_exclude varchar2(4000); + l_return_tag varchar2(4000); + begin + if instr(a_tags,',') > 0 or instr(a_tags,'-') > 0 then + + select '('||listagg( t.column_value,'|') + within group( order by column_value)||')' + into l_tags_include + from table(l_tags) t + where t.column_value not like '-%'; + + select '('||listagg( replace(t.column_value,'-','!'),' & ') + within group( order by column_value)||')' + into l_tags_exclude + from table(l_tags) t + where t.column_value like '-%'; + + + l_return_tag:= + case + when l_tags_include <> '()' and l_tags_exclude <> '()' + then l_tags_include || ' & ' || l_tags_exclude + when l_tags_include <> '()' + then l_tags_include + when l_tags_exclude <> '()' + then l_tags_exclude + end; + else + l_return_tag := a_tags; + end if; + return l_return_tag; + end; + + /* + https://stackoverflow.com/questions/29634992/shunting-yard-validate-expression + */ + function shunt_logical_expression(a_tags in ut_varchar2_list) return ut_varchar2_list is + l_operator_stack ut_stack := ut_stack(); + l_rnp_tokens ut_varchar2_list := ut_varchar2_list(); + l_token varchar2(32767); + l_expect_operand boolean := true; + l_expect_operator boolean := false; + l_idx pls_integer; + begin + l_idx := a_tags.first; + --Exuecute modified shunting algorithm + WHILE (l_idx is not null) loop + l_token := a_tags(l_idx); + if (l_token member of gc_operators and l_token member of gc_binary_operators) then + if not(l_expect_operator) then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + while l_operator_stack.top > 0 and (g_precedence(l_operator_stack.peek) > g_precedence(l_token)) loop + l_rnp_tokens.extend; + l_rnp_tokens(l_rnp_tokens.last) := l_operator_stack.pop; + end loop; + l_operator_stack.push(a_tags(l_idx)); + l_expect_operand := true; + l_expect_operator:= false; + elsif (l_token member of gc_operators and l_token member of gc_unary_operators) then + if not(l_expect_operand) then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + l_operator_stack.push(a_tags(l_idx)); + l_expect_operand := true; + l_expect_operator:= false; + elsif l_token = '(' then + if not(l_expect_operand) then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + l_operator_stack.push(a_tags(l_idx)); + l_expect_operand := true; + l_expect_operator:= false; + elsif l_token = ')' then + if not(l_expect_operator) then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + while l_operator_stack.peek <> '(' loop + l_rnp_tokens.extend; + l_rnp_tokens(l_rnp_tokens.last) := l_operator_stack.pop; + end loop; + l_operator_stack.pop; --Pop the open bracket and discard it + l_expect_operand := false; + l_expect_operator:= true; + else + if not(l_expect_operand) then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + l_rnp_tokens.extend; + l_rnp_tokens(l_rnp_tokens.last) :=l_token; + l_expect_operator := true; + l_expect_operand := false; + end if; + + l_idx := a_tags.next(l_idx); + end loop; + + while l_operator_stack.peek is not null loop + if l_operator_stack.peek in ('(',')') then + raise_application_error(ut_utils.gc_invalid_tag_expression, gc_exception_msg); + end if; + l_rnp_tokens.extend; + l_rnp_tokens(l_rnp_tokens.last):=l_operator_stack.pop; + end loop; + + return l_rnp_tokens; + end shunt_logical_expression; + + function conv_postfix_to_infix_sql(a_postfix_exp in ut_varchar2_list,a_tags_column_name in varchar2) + return varchar2 is + l_infix_stack ut_stack := ut_stack(); + l_right_side varchar2(32767); + l_left_side varchar2(32767); + l_infix_exp varchar2(32767); + l_member_token varchar2(20) := ' member of '||a_tags_column_name; + l_idx pls_integer; + begin + l_idx := a_postfix_exp.first; + while ( l_idx is not null) loop + --If the token we got is a none or any keyword + if a_postfix_exp(l_idx) member of gc_reserved_tag_words then + + l_infix_stack.push( + case + when a_postfix_exp(l_idx) = 'none' then '('||a_tags_column_name||' is empty or '||a_tags_column_name||' is null)' + else a_tags_column_name||' is not empty' + end + ); + --If token is operand but also single tag + elsif regexp_count(a_postfix_exp(l_idx),'[!()|&]') = 0 then + l_infix_stack.push(q'[']'||a_postfix_exp(l_idx)||q'[']'||l_member_token); + --If token is unary operator not + elsif a_postfix_exp(l_idx) member of gc_unary_operators then + l_right_side := l_infix_stack.pop; + l_infix_exp := a_postfix_exp(l_idx)||'('||l_right_side||')'; + l_infix_stack.push(l_infix_exp); + --If token is binary operator + elsif a_postfix_exp(l_idx) member of gc_binary_operators then + l_right_side := l_infix_stack.pop; + l_left_side := l_infix_stack.pop; + l_infix_exp := '('||l_left_side||a_postfix_exp(l_idx)||l_right_side||')'; + l_infix_stack.push(l_infix_exp); + end if; + l_idx := a_postfix_exp.next(l_idx); + end loop; + + return l_infix_stack.pop; + end conv_postfix_to_infix_sql; + + function create_where_filter(a_tags varchar2 + ) return varchar2 is + l_tags varchar2(4000); + begin + l_tags := replace(replace_legacy_tag_notation(a_tags),' '); + l_tags := conv_postfix_to_infix_sql(shunt_logical_expression(tokenize_tags_string(l_tags)),gc_tags_column_name); + l_tags := replace(l_tags, '|',' or '); + l_tags := replace(l_tags ,'&',' and '); + l_tags := replace(l_tags ,'!','not'); + return l_tags; + end; + + + /* + Having a base set of suites we will do a further filter down if there are + any tags defined. + */ + function get_tags_suites ( + a_suite_items ut_suite_cache_rows, + a_tags varchar2 + ) return ut_suite_cache_rows is + l_suite_tags ut_suite_cache_rows := ut_suite_cache_rows(); + l_sql varchar2(32000); + l_tags varchar2(4000):= create_where_filter(a_tags); + begin + l_sql := + q'[ +with + suites_mv as ( + select c.id,value(c) as obj,c.path as path,c.self_type,c.object_owner,c.tags as ]'||gc_tags_column_name||q'[ + from table(:suite_items) c + ), + suites_matching_expr as ( + select c.id,c.path as path,c.self_type,c.object_owner,c.tags + from suites_mv c + where c.self_type in ('UT_SUITE','UT_CONTEXT') + and ]'||l_tags||q'[ + ), + tests_matching_expr as ( + select c.id,c.path as path,c.self_type,c.object_owner,c.tags as ]'||gc_tags_column_name||q'[ + from suites_mv c where c.self_type in ('UT_TEST') + and ]'||l_tags||q'[ + ), + tests_with_tags_inh_from_suite as ( + select c.id,c.self_type,c.path,c.tags multiset union distinct t.tags as ]'||gc_tags_column_name||q'[ ,c.object_owner + from suites_mv c join suites_matching_expr t + on (c.path||'.' like t.path || '.%' /*all descendants and self*/ and c.object_owner = t.object_owner) + ), + tests_with_tags_prom_to_suite as ( + select c.id,c.self_type,c.path,c.tags multiset union distinct t.tags as ]'||gc_tags_column_name||q'[ ,c.object_owner + from suites_mv c join tests_matching_expr t + on (t.path||'.' like c.path || '.%' /*all ancestors and self*/ and c.object_owner = t.object_owner) + ) + select obj from suites_mv c, + (select id,row_number() over (partition by id order by id) r_num from + (select id + from tests_with_tags_prom_to_suite tst + where ]'||l_tags||q'[ + union all + select id from tests_with_tags_inh_from_suite tst + where ]'||l_tags||q'[ + ) + ) t where c.id = t.id and r_num = 1 ]'; + execute immediate l_sql bulk collect into l_suite_tags using a_suite_items; + return l_suite_tags; + end; + + function apply( + a_unfiltered_rows ut_suite_cache_rows, + a_tags varchar2 := null + ) return ut_suite_cache_rows is + l_suite_items ut_suite_cache_rows := a_unfiltered_rows; + begin + if length(a_tags) > 0 then + l_suite_items := get_tags_suites(l_suite_items,a_tags); + end if; + + return l_suite_items; + end; + +begin + --Define operators precedence + g_precedence('!'):=4; + g_precedence('&'):=3; + g_precedence('|'):=2; + g_precedence(')'):=1; + g_precedence('('):=1; + +end ut_suite_tag_filter; +/ diff --git a/source/core/ut_suite_tag_filter.pks b/source/core/ut_suite_tag_filter.pks new file mode 100644 index 000000000..e824ae275 --- /dev/null +++ b/source/core/ut_suite_tag_filter.pks @@ -0,0 +1,55 @@ +create or replace package ut_suite_tag_filter authid definer is + /* + utPLSQL - Version 3 + Copyright 2016 - 2023 utPLSQL Project + + Licensed under the Apache License, Version 2.0 (the "License"): + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + /** + * Package that will filter suites by tags + * + */ + + /* + * Return table of tokens character by character + */ + function tokenize_tags_string(a_tags in varchar2) return ut_varchar2_list; + + /* + * Function that uses Dijkstra algorithm to parse mathematical and logical expression + * and return a list of elements in Reverse Polish Notation ( postfix ) + * As part of execution it will validate expression. + */ + function shunt_logical_expression(a_tags in ut_varchar2_list) return ut_varchar2_list; + + /* + * Function that converts postfix notation into infix and creating a string of sql filter + * that checking a tags collections for tags according to posted logic. + */ + function conv_postfix_to_infix_sql(a_postfix_exp in ut_varchar2_list,a_tags_column_name in varchar2) + return varchar2; + + /* + * Generates a part where clause sql + */ + function create_where_filter(a_tags varchar2) + return varchar2; + + function apply( + a_unfiltered_rows ut_suite_cache_rows, + a_tags varchar2 := null + ) return ut_suite_cache_rows; + +end ut_suite_tag_filter; +/ diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index 5114cecad..6f972e11c 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -24,6 +24,7 @@ create or replace package body ut_utils is gc_full_valid_xml_name constant varchar2(50) := '^([_a-zA-Z])([_a-zA-Z0-9\.-])*$'; gc_owner_hash constant integer(11) := dbms_utility.get_hash_value( ut_owner(), 0, power(2,31)-1); + function surround_with(a_value varchar2, a_quote_char varchar2) return varchar2 is begin return case when a_quote_char is not null then a_quote_char||a_value||a_quote_char else a_value end; @@ -989,7 +990,5 @@ create or replace package body ut_utils is return l_result; end; - - end ut_utils; / diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index e79ce1fa6..b60fc4cdf 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -121,6 +121,10 @@ create or replace package ut_utils authid definer is ex_failed_open_cur exception; gc_failed_open_cur constant pls_integer := -20218; pragma exception_init (ex_failed_open_cur, -20218); + + ex_invalid_tag_expression exception; + gc_invalid_tag_expression constant pls_integer := -20219; + pragma exception_init (ex_invalid_tag_expression, -20219); gc_max_storage_varchar2_len constant integer := 4000; gc_max_output_string_length constant integer := 4000; diff --git a/source/install.sql b/source/install.sql index 0873d6a1e..827213e6c 100644 --- a/source/install.sql +++ b/source/install.sql @@ -50,6 +50,8 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex @@install_component.sql 'core/types/ut_key_value_pairs.tps' @@install_component.sql 'core/types/ut_reporter_info.tps' @@install_component.sql 'core/types/ut_reporters_info.tps' +@@install_component.sql 'core/types/ut_stack.tps' +@@install_component.sql 'core/types/ut_stack.tpb' @@install_component.sql 'core/ut_utils.pks' @@install_component.sql 'core/ut_metadata.pks' @@install_component.sql 'core/ut_savepoint_seq.sql' @@ -153,6 +155,8 @@ create or replace context &&ut3_owner._info using &&ut3_owner..ut_session_contex @@install_component.sql 'core/ut_suite_cache_seq.sql' @@install_component.sql 'core/ut_suite_cache.sql' +@@install_component.sql 'core/ut_suite_tag_filter.pks' +@@install_component.sql 'core/ut_suite_tag_filter.pkb' @@install_component.sql 'core/ut_suite_cache_manager.pks' @@install_component.sql 'core/ut_suite_cache_manager.pkb' @@install_component.sql 'core/ut_suite_builder.pks' diff --git a/source/uninstall_objects.sql b/source/uninstall_objects.sql index 9531736b9..f488b8b8d 100644 --- a/source/uninstall_objects.sql +++ b/source/uninstall_objects.sql @@ -95,6 +95,8 @@ drop package ut_suite_manager; drop package ut_suite_builder; +drop package ut_suite_tag_filter; + drop package ut_suite_cache_manager; drop table ut_suite_cache purge; @@ -267,6 +269,8 @@ end; drop package ut_utils; +drop type ut_stack force; + drop sequence ut_savepoint_seq; drop type ut_documentation_reporter force; diff --git a/test/install_ut3_tester_tests.sql b/test/install_ut3_tester_tests.sql index 8163a39f2..cc96b2b64 100644 --- a/test/install_ut3_tester_tests.sql +++ b/test/install_ut3_tester_tests.sql @@ -17,6 +17,7 @@ alter session set plsql_optimize_level=0; @@ut3_tester/core/annotations/test_annot_disabled_reason.pks @@ut3_tester/core/expectations/test_expectation_processor.pks @@ut3_tester/core/test_ut_utils.pks +@@ut3_tester/core/test_ut_suite_tag_filter.pks @@ut3_tester/core/test_ut_test.pks @@ut3_tester/core/test_ut_suite.pks @@ut3_tester/core/test_ut_executable.pks @@ -35,6 +36,7 @@ alter session set plsql_optimize_level=0; @@ut3_tester/core/annotations/test_annot_disabled_reason.pkb @@ut3_tester/core/expectations/test_expectation_processor.pkb @@ut3_tester/core/test_ut_utils.pkb +@@ut3_tester/core/test_ut_suite_tag_filter.pkb @@ut3_tester/core/test_ut_test.pkb @@ut3_tester/core/test_ut_suite.pkb @@ut3_tester/core/test_ut_executable.pkb diff --git a/test/ut3_tester/core/test_ut_suite_tag_filter.pkb b/test/ut3_tester/core/test_ut_suite_tag_filter.pkb new file mode 100644 index 000000000..edfb27cfc --- /dev/null +++ b/test/ut3_tester/core/test_ut_suite_tag_filter.pkb @@ -0,0 +1,82 @@ +create or replace package body test_ut_suite_tag_filter is + + procedure test_conversion_to_rpn is + l_postfix ut3_develop.ut_varchar2_list; + l_postfix_string varchar2(4000); + l_input_token ut3_develop.ut_varchar2_list; + begin + l_input_token := ut3_develop.ut_suite_tag_filter.tokenize_tags_string('A'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + l_postfix_string := ut3_develop.ut_utils.table_to_clob(l_postfix,''); + ut.expect(l_postfix_string).to_equal('A'); + + l_input_token := ut3_develop.ut_suite_tag_filter.tokenize_tags_string('A|B'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + l_postfix_string := ut3_develop.ut_utils.table_to_clob(l_postfix,''); + ut.expect(l_postfix_string).to_equal('AB|'); + + l_input_token := ut3_develop.ut_suite_tag_filter.tokenize_tags_string('(a|b)|c&d'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + l_postfix_string := ut3_develop.ut_utils.table_to_clob(l_postfix,''); + ut.expect(l_postfix_string).to_equal('ab|cd&|'); + + l_input_token := ut3_develop.ut_suite_tag_filter.tokenize_tags_string('!a|b'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + l_postfix_string := ut3_develop.ut_utils.table_to_clob(l_postfix,''); + ut.expect(l_postfix_string).to_equal('a!b|'); + end; + + procedure test_conversion_opr_by_opr is + l_postfix ut3_develop.ut_varchar2_list; + l_input_token ut3_develop.ut_varchar2_list; + begin + l_input_token := ut3_develop.ut_varchar2_list('A','B'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + ut.fail('Expected exception but nothing was raised'); + end; + + procedure test_conversion_oprd_by_opd is + l_postfix ut3_develop.ut_varchar2_list; + l_input_token ut3_develop.ut_varchar2_list; + begin + l_input_token := ut3_develop.ut_varchar2_list('|','|'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + ut.fail('Expected exception but nothing was raised'); + end; + + procedure test_conversion_lb_by_oper is + l_postfix ut3_develop.ut_varchar2_list; + l_input_token ut3_develop.ut_varchar2_list; + begin + l_input_token := ut3_develop.ut_varchar2_list('(','A','|','B',')','('); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + ut.fail('Expected exception but nothing was raised'); + end; + + procedure test_conversion_rb_by_oprd is + l_postfix ut3_develop.ut_varchar2_list; + l_input_token ut3_develop.ut_varchar2_list; + begin + l_input_token := ut3_develop.ut_varchar2_list(')','A'); + l_postfix := ut3_develop.ut_suite_tag_filter.shunt_logical_expression(l_input_token); + ut.fail('Expected exception but nothing was raised'); + end; + + procedure conv_from_tag_to_sql_filter is + l_sql_filter varchar2(4000); + begin + l_sql_filter := ut3_develop.ut_suite_tag_filter.create_where_filter('test1'); + ut.expect(l_sql_filter).to_equal(q'['test1' member of tags]'); + + l_sql_filter := ut3_develop.ut_suite_tag_filter.create_where_filter('test1|test2'); + ut.expect(l_sql_filter).to_equal(q'[('test1' member of tags or 'test2' member of tags)]'); + + l_sql_filter := ut3_develop.ut_suite_tag_filter.create_where_filter('test1|!test2'); + ut.expect(l_sql_filter).to_equal(q'[('test1' member of tags or not('test2' member of tags))]'); + + l_sql_filter := ut3_develop.ut_suite_tag_filter.create_where_filter('test1&!test2'); + ut.expect(l_sql_filter).to_equal(q'[('test1' member of tags and not('test2' member of tags))]'); + end; + +end test_ut_suite_tag_filter; +/ diff --git a/test/ut3_tester/core/test_ut_suite_tag_filter.pks b/test/ut3_tester/core/test_ut_suite_tag_filter.pks new file mode 100644 index 000000000..0f84b751b --- /dev/null +++ b/test/ut3_tester/core/test_ut_suite_tag_filter.pks @@ -0,0 +1,33 @@ +create or replace package test_ut_suite_tag_filter is + + --%suite(ut_suite_tag_filter) + --%suitepath(utplsql.ut3_tester.core) + + --%context( Conversion to Reverse Polish Notation) + + --%test( Test conversion of expression into Reverse Polish Notation) + procedure test_conversion_to_rpn; + + --%test( Operator is followed by operator) + --%throws(ut3_develop.ut_utils.gc_invalid_tag_expression) + procedure test_conversion_opr_by_opr; + + --%test( Operand is followed by operand) + --%throws(ut3_develop.ut_utils.gc_invalid_tag_expression) + procedure test_conversion_oprd_by_opd; + + --%test( Left Bracket is followed by operator) + --%throws(ut3_develop.ut_utils.gc_invalid_tag_expression) + procedure test_conversion_lb_by_oper; + + --%test( Right Bracket is followed by operand) + --%throws(ut3_develop.ut_utils.gc_invalid_tag_expression) + procedure test_conversion_rb_by_oprd; + + --%endcontext + + --%test( Test conversion of expression from tag into custom where filter for SQL) + procedure conv_from_tag_to_sql_filter; + +end test_ut_suite_tag_filter; +/ diff --git a/test/ut3_tester/core/test_ut_utils.pkb b/test/ut3_tester/core/test_ut_utils.pkb index 4ed718777..ec7e4f403 100644 --- a/test/ut3_tester/core/test_ut_utils.pkb +++ b/test/ut3_tester/core/test_ut_utils.pkb @@ -488,6 +488,6 @@ end; begin ut.expect(l_expected).to_equal(l_actual); end; - + end test_ut_utils; / diff --git a/test/ut3_tester/core/test_ut_utils.pks b/test/ut3_tester/core/test_ut_utils.pks index 4d83b5042..114b35e86 100644 --- a/test/ut3_tester/core/test_ut_utils.pks +++ b/test/ut3_tester/core/test_ut_utils.pks @@ -156,6 +156,6 @@ create or replace package test_ut_utils is --%endcontext - + end test_ut_utils; / diff --git a/test/ut3_tester_helper/run_helper.pkb b/test/ut3_tester_helper/run_helper.pkb index a132f4943..6289b1642 100644 --- a/test/ut3_tester_helper/run_helper.pkb +++ b/test/ut3_tester_helper/run_helper.pkb @@ -343,9 +343,250 @@ create or replace package body run_helper is end; end test_package_3; ]'; + + execute immediate q'[create or replace package test_tag_pkg_1 is + --%suite + --%tags(suite1,release_3_1_13,development,complex,end_to_end) + --%suitepath(suite1) + --%rollback(manual) + + --%test(Test1 from test_tag_pkg_1) + --%tags(test1,development,fast) + procedure test1; + + --%test(Test2 from test_tag_pkg_1) + --%tags(test2,production,slow,patch_3_1_13) + procedure test2; + + end test_tag_pkg_1; + ]'; + + execute immediate q'[create or replace package body test_tag_pkg_1 is + procedure test1 is + begin + dbms_output.put_line('test_tag_pkg_1.test1 executed'); + end; + procedure test2 is + begin + dbms_output.put_line('test_tag_pkg_1.test2 executed'); + end; + end test_tag_pkg_1; + ]'; + + execute immediate q'[create or replace package test_tag_pkg_2 is + --%suite + --%tags(suite2,release_3_1_12,development,simple) + --%suitepath(suite1.suite2) + --%rollback(manual) + + --%test(Test3 from test_tag_pkg_2) + --%tags(test3,development,fast) + procedure test3; + + --%test(Test4 from test_tag_pkg_1) + --%tags(test4,production,slow) + procedure test4; + + end test_tag_pkg_2; + ]'; + + execute immediate q'[create or replace package body test_tag_pkg_2 is + procedure test3 is + begin + dbms_output.put_line('test_tag_pkg_2.test3 executed'); + end; + procedure test4 is + begin + dbms_output.put_line('test_tag_pkg_2.test4 executed'); + end; + end test_tag_pkg_2; + ]'; + + execute immediate q'[create or replace package test_tag_pkg_3 is + --%suite + --%tags(suite3,release_3_1_13,production,simple,end_to_end) + --%suitepath(suite3) + --%rollback(manual) + + --%test(Test5 from test_tag_pkg_3) + --%tags(test5,release_3_1_13,production,patch_3_1_13) + procedure test5; + + --%test(Test6 from test_tag_pkg_3) + --%tags(test6,development,patch_3_1_14) + procedure test6; + + end test_tag_pkg_3; + ]'; + + execute immediate q'[create or replace package body test_tag_pkg_3 is + procedure test5 is + begin + dbms_output.put_line('test_tag_pkg_3.test5 executed'); + end; + procedure test6 is + begin + dbms_output.put_line('test_tag_pkg_3.test6 executed'); + end; + end test_tag_pkg_3; + ]'; + + execute immediate q'[create or replace package suite1_level1_pkg is + + --%suite(suite1_level1) + --%suitepath(any_none) + --%rollback(manual) + + --%test(Test 1 from Suite1 on level 1) + --%tags(suite1,level1,test1,test1_level1) + procedure test1_level1; + + --%test(Test 2 from Suite1 on level 1) + procedure test2_level1; + + end suite1_level1_pkg; + ]'; + + execute immediate q'[create or replace package body suite1_level1_pkg is + procedure test1_level1 is + begin + dbms_output.put_line('suite1_level1_pkg.test1_level1 executed'); + end; + procedure test2_level1 is + begin + dbms_output.put_line('suite1_level1_pkg.test2_level1 executed'); + end; + end suite1_level1_pkg; + ]'; + + execute immediate q'[create or replace package suite1_1_level2_pkg is + + --%suite(suite1_1_level2) + --%suitepath(any_none.suite1_level1) + --%rollback(manual) + + --%test(Test 1 from Suite1_2 on level 2) + --%tags(level2,test1,test1_level2) + procedure suite1_1_test1_level2; + + --%test(Test 2 from Suite1_2 on level 2) + procedure suite1_1_test2_level2; + + end suite1_1_level2_pkg; + ]'; + + execute immediate q'[create or replace package body suite1_1_level2_pkg is + procedure suite1_1_test1_level2 is + begin + dbms_output.put_line('suite1_1_level2_pkg.suite1_1_test1_level2 executed'); + end; + procedure suite1_1_test2_level2 is + begin + dbms_output.put_line('suite1_1_level2_pkg.suite1_1_test2_level2 executed'); + end; + end suite1_1_level2_pkg; + ]'; + + execute immediate q'[create or replace package suite1_2_level2_pkg is + + --%suite(suite1_2_level2) + --%tags(level2,suite1_2,suites) + --%suitepath(any_none.suite1_level1) + --%rollback(manual) + + --%test(Test 1 from Suite1_2 on level 2) + procedure suite1_2_test1_level2; + + --%test(Test 2 from Suite1_2 on level 2) + --%tags(level2,test2,test2_level2) + procedure suite1_2_test2_level1; + + end suite1_2_level2_pkg; + ]'; + + execute immediate q'[create or replace package body suite1_2_level2_pkg is + procedure suite1_2_test1_level2 is + begin + dbms_output.put_line('suite1_2_level2_pkg.suite1_2_test1_level2 executed'); + end; + procedure suite1_2_test2_level1 is + begin + dbms_output.put_line('suite1_2_level2_pkg.suite1_2_test2_level1 executed'); + end; + end suite1_2_level2_pkg; + ]'; + + execute immediate q'[create or replace package suite2_level1_pkg is + + --%suite(suite2_level1) + --%tags(level1,suite2,suites) + --%suitepath(any_none) + --%rollback(manual) + + --%test(Test 1 from Suite1 on level 1) + --%tags(suite2,level1,test1,test1_level1) + procedure test1_level1; + + --%test(Test 2 from Suite1 on level 1) + procedure test2_level1; + + end suite2_level1_pkg; + ]'; + + execute immediate q'[create or replace package body suite2_level1_pkg is + procedure test1_level1 is + begin + dbms_output.put_line('suite2_level1_pkg.test1_level1 executed'); + end; + procedure test2_level1 is + begin + dbms_output.put_line('suite2_level1_pkg.test2_level1 executed'); + end; + end suite2_level1_pkg; + ]'; + + execute immediate q'[create or replace package suite2_2_level2_pkg is + + --%suite(suite2_2_level2) + --%tags(level2,suite2_2,suites) + --%suitepath(any_none.suite2_level1) + --%rollback(manual) + + --%test(Test 1 from Suite2_2 on level 2) + procedure suite2_2_test1_level2; + + --%test(Test 2 from Suite2_2 on level 2) + --%tags(level2,test2,test2_level2) + procedure suite2_2_test2_level2; + + end suite2_2_level2_pkg; + ]'; + + execute immediate q'[create or replace package body suite2_2_level2_pkg is + procedure suite2_2_test1_level2 is + begin + dbms_output.put_line('suite2_2_level2_pkg.suite2_2_test1_level2 executed'); + end; + procedure suite2_2_test2_level2 is + begin + dbms_output.put_line('suite2_2_level2_pkg.suite2_2_test2_level2 executed'); + end; + end suite2_2_level2_pkg; + ]'; + + execute immediate q'[grant execute on test_package_1 to public]'; execute immediate q'[grant execute on test_package_2 to public]'; execute immediate q'[grant execute on test_package_3 to public]'; + execute immediate q'[grant execute on test_tag_pkg_1 to public]'; + execute immediate q'[grant execute on test_tag_pkg_2 to public]'; + execute immediate q'[grant execute on test_tag_pkg_3 to public]'; + + execute immediate q'[grant execute on suite1_level1_pkg to public]'; + execute immediate q'[grant execute on suite1_1_level2_pkg to public]'; + execute immediate q'[grant execute on suite1_2_level2_pkg to public]'; + execute immediate q'[grant execute on suite2_level1_pkg to public]'; + execute immediate q'[grant execute on suite2_2_level2_pkg to public]'; end; procedure drop_ut3_user_tests is @@ -354,6 +595,15 @@ create or replace package body run_helper is execute immediate q'[drop package test_package_1]'; execute immediate q'[drop package test_package_2]'; execute immediate q'[drop package test_package_3]'; + execute immediate q'[drop package test_tag_pkg_1]'; + execute immediate q'[drop package test_tag_pkg_2]'; + execute immediate q'[drop package test_tag_pkg_3]'; + + execute immediate q'[drop package suite2_2_level2_pkg]'; + execute immediate q'[drop package suite2_level1_pkg]'; + execute immediate q'[drop package suite1_2_level2_pkg]'; + execute immediate q'[drop package suite1_level1_pkg]'; + execute immediate q'[drop package suite1_1_level2_pkg]'; end; procedure create_test_suite is diff --git a/test/ut3_user/api/test_ut_run.pkb b/test/ut3_user/api/test_ut_run.pkb index 41b969359..e80235744 100644 --- a/test/ut3_user/api/test_ut_run.pkb +++ b/test/ut3_user/api/test_ut_run.pkb @@ -948,7 +948,7 @@ Failures:% procedure two_test_run_by_two_tags is l_results clob; begin - ut3_tester_helper.run_helper.run(a_tags => 'subtest1,subtest2'); + ut3_tester_helper.run_helper.run(a_tags => 'subtest1|subtest2'); l_results := ut3_tester_helper.main_helper.get_dbms_output_as_clob(); --Assert ut.expect( l_results ).to_be_like( '%test_package_1%' ); @@ -958,7 +958,21 @@ Failures:% ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); end; - + + procedure two_test_run_by_two_tags_leg is + l_results clob; + begin + ut3_tester_helper.run_helper.run(a_tags => 'subtest1,subtest2'); + l_results := ut3_tester_helper.main_helper.get_dbms_output_as_clob(); + --Assert + ut.expect( l_results ).to_be_like( '%test_package_1%' ); + ut.expect( l_results ).to_be_like( '%test_package_2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_1.test2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_2.test2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); + end; + procedure suite_with_children_tag is l_results clob; begin @@ -1078,6 +1092,20 @@ Failures:% procedure tag_run_func_path_list is l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests.test_package_1',':tests'),a_tags => 'suite1test1|suite2test1'); + --Assert + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3%' ); + end; + + procedure tag_run_func_path_list_leg is + l_results ut3_develop.ut_varchar2_list; begin l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests.test_package_1',':tests'),a_tags => 'suite1test1,suite2test1'); --Assert @@ -1092,6 +1120,18 @@ Failures:% procedure tag_inc_exc_run_func_path_list is l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests.test_package_1',':tests'),a_tags => '(suite1test1|suite2test1)&!suite2'); + --Assert + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3%' ); + end; + + procedure tag_inc_exc_run_fun_pth_lst_lg is + l_results ut3_develop.ut_varchar2_list; begin l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests.test_package_1',':tests'),a_tags => 'suite1test1,suite2test1,-suite2'); --Assert @@ -1104,6 +1144,22 @@ Failures:% procedure tag_exclude_run_func_path_list is l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests,:tests2'),a_tags => '!suite1test2&!suite2test1&!test1suite3'); + --Assert + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_3%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_3.test2%executed%' ); + end; + +procedure tag_exclude_run_fun_pth_lst_lg is + l_results ut3_develop.ut_varchar2_list; begin l_results := ut3_tester_helper.run_helper.run(ut3_develop.ut_varchar2_list(':tests,:tests2'),a_tags => '-suite1test2,-suite2test1,-test1suite3'); --Assert @@ -1120,6 +1176,20 @@ Failures:% procedure tag_include_exclude_run_func is l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(a_tags => '(suite1)&(!suite1test2&!suite2test1&!test1suite3)'); + --Assert + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2.test2%executed%' ); + end; + + procedure tag_include_exclude_run_fun_lg is + l_results ut3_develop.ut_varchar2_list; begin l_results := ut3_tester_helper.run_helper.run(a_tags => 'suite1,-suite1test2,-suite2test1,-test1suite3'); --Assert @@ -1132,6 +1202,105 @@ Failures:% ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_2.test2%executed%' ); end; + procedure tag_complex_expressions is + l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(a_tags => 'release_3_1_13&(fast|simple)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test5 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test6 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test3%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test4%executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => 'release_3_1_13&(!patch_3_1_13&!patch_3_1_14)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_1.test1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test3%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test4%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3.test5%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3.test6%executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => 'release_3_1_13&(patch_3_1_13&!slow)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test5 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test3%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test4%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3.test6%executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(simple&end_to_end)|(development&fast)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_1.test1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_2.test3 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test5 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test6 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test4%executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(!development&end_to_end)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_tag_pkg_3.test5 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test1%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_1.test2%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test3%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_2.test4%executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%test_package_3.test6%executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => 'any'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite2_level1_pkg.test1_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite2_level1_pkg.test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_level1_pkg.test1_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite2_2_level2_pkg.suite2_2_test1_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite2_2_level2_pkg.suite2_2_test2_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_2_level2_pkg.suite1_2_test1_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_2_level2_pkg.suite1_2_test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_1_level2_pkg.suite1_1_test1_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_level1_pkg.test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_1_level2_pkg.suite1_1_test2_level2 executed%' ); + + l_results := ut3_tester_helper.run_helper.run(a_tags => 'none'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_level1_pkg.test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%suite1_1_level2_pkg.suite1_1_test2_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite2_level1_pkg.test1_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite2_level1_pkg.test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_level1_pkg.test1_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite2_2_level2_pkg.suite2_2_test1_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite2_2_level2_pkg.suite2_2_test2_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_2_level2_pkg.suite1_2_test1_level2 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_2_level2_pkg.suite1_2_test2_level1 executed%' ); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like( '%suite1_1_level2_pkg.suite1_1_test1_level2 executed%' ); + + + end; + + procedure invalid_tag_expression is + l_results ut3_develop.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(a_tags => '(!development!&end_to_end)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(!development&&end_to_end)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(!development&end_to_end|)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(!development&!!end_to_end)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(&development&end_to_end)'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + l_results := ut3_tester_helper.run_helper.run(a_tags => '(development|end_to_end))'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_match('^\s*invalid_tag_expression \[[,\.0-9]+ sec\]\s*$','m'); + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).not_to_be_like('%(FAILED -%'); + + end; + procedure set_application_info is begin dbms_application_info.set_module( gc_module, gc_action ); diff --git a/test/ut3_user/api/test_ut_run.pks b/test/ut3_user/api/test_ut_run.pks index b3be8700d..c57788bff 100644 --- a/test/ut3_user/api/test_ut_run.pks +++ b/test/ut3_user/api/test_ut_run.pks @@ -202,6 +202,9 @@ create or replace package test_ut_run is --%test(Execute tests by passing two tags) procedure two_test_run_by_two_tags; + --%test(Execute tests by passing two tags - Legacy notation) + procedure two_test_run_by_two_tags_leg; + --%test(Execute suite and all of its children) procedure suite_with_children_tag; @@ -235,15 +238,34 @@ create or replace package test_ut_run is --%test(Runs tests from given paths with paths list and a tag) procedure tag_run_func_path_list; + --%test(Runs tests from given paths with paths list and a tag - Legacy Notation) + procedure tag_run_func_path_list_leg; + --%test(Runs tests from given paths with paths list and include/exclude tags) procedure tag_inc_exc_run_func_path_list; + --%test(Runs tests from given paths with paths list and include/exclude tags - Legacy Notation) + procedure tag_inc_exc_run_fun_pth_lst_lg; + --%test(Runs tests from given path and excludes specific tags) procedure tag_exclude_run_func_path_list; + --%test(Runs tests from given path and excludes specific tags - Legacy Notation) + procedure tag_exclude_run_fun_pth_lst_lg; + --%test(Runs tests from given tags and exclude tags) procedure tag_include_exclude_run_func; + --%test(Runs tests from given tags and exclude tags - Legacy Notation) + procedure tag_include_exclude_run_fun_lg; + + --%test(Runs tests suing complex expressions) + procedure tag_complex_expressions; + + --%test(Testing invalid tag expression) + --%throws(-20219) + procedure invalid_tag_expression; + --%endcontext --%context(ut3_info context) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy