diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index f6242dce0..4277ba9cb 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -38,6 +38,7 @@ We strongly recommend putting package level annotations at the very top of packa | `--%disabled` | Package/procedure | Used to disable a suite or a test. Disabled suites/tests do not get executed, they are however marked and reported as disabled in a test run. | | `--%context()` | Package | Denotes start of a named context (sub-suite) in a suite package | | `--%endcontext` | Package | Denotes end of a nested context (sub-suite) in a suite package | +| `--%tags` | Package/procedure | Used to label a test or a suite for purpose of identification | ### Suite @@ -823,7 +824,7 @@ See [beforeall](#Beforeall) for more examples. Indicates specific setup procedure(s) to be executed for a test. The procedure(s) can be located either: - within current package (package name is optional) - within another package - + The annotation need to be placed alongside `--%test` annotation. The `--%beforetest` procedures are executed after invoking all `--%beforeeach` for a test. @@ -911,7 +912,7 @@ Finished in .015185 seconds Indicates specific cleanup procedure(s) to be executed for a test. The procedure(s) can be located either: - within current package (package name is optional) - within another package - + The annotation need to be placed alongside `--%test` annotation. If a test is marked as disabled the `--%aftertest` procedures are not invoked for that test. @@ -1221,6 +1222,98 @@ Finished in .035261 seconds ``` + +### Tags + +Tag is a label attached to the test or a suite path. It is used for identification and execution a group of tests / suites that share same tag. + +It allows us to group a tests / suites using a various categorization and place a test / suite in multiple buckets. Same tests can be group with other tests based on the functionality , frequency, type of output etc. + +e.q. + +```sql +--%tags(batch,daily,csv) +``` + +or + +```sql +--%tags(api,online,json) +``` + + + +Tags are defined as a coma separated list. When executing a test run with tag filter applied, framework will find all tests associated with given tags and execute them. Framework applies `OR` logic when resolving a tags so any tests / suites that match at least one tag will be included in the test run. + +When a suite gets tagged all of its children will automatically inherit a tag and get executed along the parent. Parent suit tests are not executed. but a suitepath hierarchy is kept. + +Sample tag package. + +```sql +create or replace package ut_sample_test IS + + --%suite(Sample Test Suite) + --%tag(suite1) + + --%test(Compare Ref Cursors) + --%tag(test1,sample) + procedure ut_refcursors1; + + --%test(Run equality test) + --%tag(test2,sample) + 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 a parameter `a_tags` + +```sql +select * from table(ut.run(a_path => 'ut_sample_test',a_tags => 'suite1')); +select * from table(ut.run(a_tags => 'test1,test2')); +select * from table(ut.run(a_tags => 'sample')); + +begin + ut.run(a_path => 'ut_sample_test',a_tags => 'suite1'); +end; +/ + +exec ut.run('ut_sample_test', a_tags => 'sample'); +``` + + + +Tags should adhere to following rules: + +- tags are case sensitive +- tags cannot be an empty string +- tags cannot contain spaces e.g. to create a multi-word `tag` please use underscores,dashes, dots etc. e.g. `test_of_batch` +- tags with empty spaces will be ignored during execution +- tags can contain special characters + + + ### Suitepath It is very likely that the application for which you are going to introduce tests consists of many different packages, procedures and functions. @@ -1346,9 +1439,9 @@ If `--%throws` annotation is specified with arguments and exception raised is no The framework will raise a warning, when `--%throws` annotation has invalid arguments or when no arguments were provided. Annotation `--%throws(7894562, operaqk, -=1, -20496, pow74d, posdfk3)` will be interpreted as `--%throws(-20496)`. - + Please note that `NO_DATA_FOUND` exception is a special case in Oracle. To capture it use `NO_DATA_FOUND` named exception or `-1403` exception No. - +​ Example: ```sql create or replace package exc_pkg is diff --git a/docs/userguide/querying_suites.md b/docs/userguide/querying_suites.md index a12ea5501..a412a7c17 100644 --- a/docs/userguide/querying_suites.md +++ b/docs/userguide/querying_suites.md @@ -22,21 +22,22 @@ Querying the data from function provides the follwing details: - `item_line_no` - line_number where annotation identifying the item exists - `path` - suitepath of the item - `disabled_flag` - (0/1) indicator if item is disabled by --%disabled annotation - +- `tags` - tags associated with suites + To get list of all test suites in current schema ```sql select * from table(ut_runner.get_suites_info()) where item_type = 'UT_SUITE'; -``` +``` To get list of all tests for test suite `TEST_STUFF` in current user schema ```sql select * from table(ut_runner.get_suites_info(USER, 'TEST_STUFF')) where item_type = 'UT_TEST'; -``` +``` To get a full information about suite `TEST_STUFF` including suite description, all contexts and tests in a suite ```sql select * from table(ut_runner.get_suites_info(USER, 'TEST_STUFF')) where item_type = 'UT_TEST'; -``` +``` ## Checking if schema contains tests diff --git a/source/api/ut.pkb b/source/api/ut.pkb index 5e13dcc62..1a95f0725 100644 --- a/source/api/ut.pkb +++ b/source/api/ut.pkb @@ -120,7 +120,8 @@ create or replace package body ut is a_exclude_objects ut_varchar2_list, a_client_character_set varchar2, a_random_test_order integer, - a_random_test_order_seed positive + a_random_test_order_seed positive, + a_tags varchar2 := null ) is pragma autonomous_transaction; begin @@ -138,7 +139,8 @@ create or replace package body ut is a_client_character_set, false, ut_utils.int_to_boolean(a_random_test_order), - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); rollback; end; @@ -154,7 +156,8 @@ create or replace package body ut is a_exclude_objects ut_varchar2_list, a_client_character_set varchar2, a_random_test_order integer, - a_random_test_order_seed positive + a_random_test_order_seed positive, + a_tags varchar2 := null ) is pragma autonomous_transaction; begin @@ -172,7 +175,8 @@ create or replace package body ut is a_client_character_set, false, ut_utils.int_to_boolean(a_random_test_order), - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); rollback; end; @@ -212,7 +216,8 @@ create or replace package body ut is a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -228,7 +233,8 @@ create or replace package body ut is a_exclude_objects, a_client_character_set, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); @@ -249,7 +255,8 @@ create or replace package body ut is a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -265,7 +272,8 @@ create or replace package body ut is a_exclude_objects, a_client_character_set, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); @@ -287,7 +295,8 @@ create or replace package body ut is a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -303,7 +312,8 @@ create or replace package body ut is a_exclude_objects, a_client_character_set, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); @@ -325,7 +335,8 @@ create or replace package body ut is a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -341,7 +352,8 @@ create or replace package body ut is a_exclude_objects, a_client_character_set, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); @@ -363,7 +375,8 @@ create or replace package body ut is a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -379,7 +392,8 @@ create or replace package body ut is a_exclude_objects, a_client_character_set, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); @@ -401,7 +415,8 @@ create or replace package body ut is a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined is l_reporter ut_reporter_base := a_reporter; l_results sys_refcursor; @@ -417,7 +432,8 @@ create or replace package body ut is a_exclude_objects, a_client_character_set, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); if l_reporter is of (ut_output_reporter_base) then l_results := treat(l_reporter as ut_output_reporter_base).get_lines_cursor(); @@ -440,7 +456,8 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) is l_reporter ut_reporter_base := a_reporter; begin @@ -459,7 +476,8 @@ create or replace package body ut is a_client_character_set, a_force_manual_rollback, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); else run_autonomous( @@ -473,7 +491,8 @@ create or replace package body ut is a_exclude_objects, a_client_character_set, ut_utils.boolean_to_int(a_random_test_order), - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); end if; if l_reporter is of (ut_output_reporter_base) then @@ -494,7 +513,8 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) is l_reporter ut_reporter_base := a_reporter; begin @@ -510,7 +530,8 @@ create or replace package body ut is a_client_character_set, a_force_manual_rollback, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); end; @@ -525,7 +546,8 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) is begin ut.run( @@ -540,7 +562,8 @@ create or replace package body ut is a_client_character_set, a_force_manual_rollback, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); end; @@ -555,7 +578,8 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) is begin ut.run( @@ -570,7 +594,8 @@ create or replace package body ut is a_client_character_set, a_force_manual_rollback, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); end; @@ -586,7 +611,8 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) is begin ut.run( @@ -601,7 +627,8 @@ create or replace package body ut is a_client_character_set, a_force_manual_rollback, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); end; @@ -617,7 +644,8 @@ create or replace package body ut is a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) is begin ut.run( @@ -632,10 +660,12 @@ create or replace package body ut is a_client_character_set, a_force_manual_rollback, a_random_test_order, - a_random_test_order_seed + a_random_test_order_seed, + a_tags ); end; + procedure set_nls is begin if g_nls_date_format is null then diff --git a/source/api/ut.pks b/source/api/ut.pks index 7d3b191b3..17b4a845e 100644 --- a/source/api/ut.pks +++ b/source/api/ut.pks @@ -57,7 +57,8 @@ create or replace package ut authid current_user as a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined; function run( @@ -70,7 +71,8 @@ create or replace package ut authid current_user as a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined; function run( @@ -84,7 +86,8 @@ create or replace package ut authid current_user as a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined; function run( @@ -98,7 +101,8 @@ create or replace package ut authid current_user as a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined; function run( @@ -112,7 +116,8 @@ create or replace package ut authid current_user as a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined; function run( @@ -126,7 +131,8 @@ create or replace package ut authid current_user as a_exclude_objects ut_varchar2_list := null, a_client_character_set varchar2 := null, a_random_test_order integer := 0, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) return ut_varchar2_rows pipelined; procedure run( @@ -140,7 +146,8 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ); procedure run( @@ -154,7 +161,8 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ); procedure run( @@ -169,7 +177,8 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ); procedure run( @@ -184,7 +193,8 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ); procedure run( @@ -199,7 +209,8 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ); procedure run( @@ -214,7 +225,8 @@ create or replace package ut authid current_user as a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ); /** diff --git a/source/api/ut_runner.pkb b/source/api/ut_runner.pkb index 28ec1a610..d57b86edd 100644 --- a/source/api/ut_runner.pkb +++ b/source/api/ut_runner.pkb @@ -90,7 +90,8 @@ create or replace package body ut_runner is a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ) is l_run ut_run; l_coverage_schema_names ut_varchar2_rows; @@ -98,6 +99,7 @@ create or replace package body ut_runner is l_include_object_names ut_object_names; l_paths ut_varchar2_list := ut_varchar2_list(); l_random_test_order_seed positive; + l_tags ut_varchar2_rows := ut_varchar2_rows(); begin ut_event_manager.initialize(); if a_reporters is not empty then @@ -129,7 +131,6 @@ create or replace package body ut_runner is ut_utils.save_dbms_output_to_cache(); ut_console_reporter_base.set_color_enabled(a_color_console); - if a_coverage_schemes is not empty then l_coverage_schema_names := ut_utils.convert_collection(a_coverage_schemes); else @@ -139,7 +140,12 @@ create or replace package body ut_runner is if a_exclude_objects is not empty then l_exclude_object_names := to_ut_object_list(a_exclude_objects, l_coverage_schema_names); 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_exclude_object_names := l_exclude_object_names multiset union all ut_suite_manager.get_schema_ut_packages(l_coverage_schema_names); l_include_object_names := to_ut_object_list(a_include_objects, l_coverage_schema_names); @@ -153,10 +159,11 @@ create or replace package body ut_runner is set(a_source_file_mappings), set(a_test_file_mappings), a_client_character_set, - l_random_test_order_seed + l_random_test_order_seed, + l_tags ); - ut_suite_manager.configure_execution_by_path(l_paths, l_run.items, l_random_test_order_seed); + ut_suite_manager.configure_execution_by_path(l_paths, l_run.items, l_random_test_order_seed, l_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/api/ut_runner.pks b/source/api/ut_runner.pks index ab2e1f6bb..0434790f5 100644 --- a/source/api/ut_runner.pks +++ b/source/api/ut_runner.pks @@ -70,7 +70,8 @@ create or replace package ut_runner authid current_user is a_client_character_set varchar2 := null, a_force_manual_rollback boolean := false, a_random_test_order boolean := false, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_tags varchar2 := null ); /** diff --git a/source/api/ut_suite_item_info.tpb b/source/api/ut_suite_item_info.tpb new file mode 100644 index 000000000..06ae8341b --- /dev/null +++ b/source/api/ut_suite_item_info.tpb @@ -0,0 +1,38 @@ +create or replace type body ut_suite_item_info is + /* + utPLSQL - Version 3 + Copyright 2016 - 2018 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_suite_item_info(a_object_owner varchar2, a_object_name varchar2, a_item_name varchar2, + a_item_description varchar2, a_item_type varchar2, a_item_line_no integer, a_path varchar2, a_disabled_flag integer, + a_tags ut_varchar2_rows) return self as result is + begin + self.object_owner := a_object_owner; + self.object_name := a_object_name; + self.item_name := a_item_name; + self.item_description := a_item_description; + self.item_type := a_item_type; + self.item_line_no := a_item_line_no; + self.path := a_path; + self.disabled_flag := a_disabled_flag; + self.tags := case + when a_tags is null then null + when a_tags.count = 0 then null + else ut_utils.to_string(ut_utils.table_to_clob(a_tags,',') ,a_quote_char => null) + end; + return; + end; +end; +/ diff --git a/source/api/ut_suite_item_info.tps b/source/api/ut_suite_item_info.tps index bf6fd6ecf..d7f7f97dc 100644 --- a/source/api/ut_suite_item_info.tps +++ b/source/api/ut_suite_item_info.tps @@ -22,6 +22,10 @@ create or replace type ut_suite_item_info as object ( item_type varchar2( 250 ), -- the type of item (UT_SUITE/UT_SUITE_CONTEXT/UT_TEST) item_line_no integer, -- line_number where annotation identifying the item exists path varchar2( 4000 ),-- suitepath of the item - disabled_flag integer -- 0 (zero) if item is not disabled, 1 if item is disabled by --%disabled annotation + disabled_flag integer, -- 0 (zero) if item is not disabled, 1 if item is disabled by --%disabled annotation + tags varchar2(4000), + constructor function ut_suite_item_info(a_object_owner varchar2, a_object_name varchar2, a_item_name varchar2, + a_item_description varchar2, a_item_type varchar2, a_item_line_no integer, a_path varchar2, a_disabled_flag integer, + a_tags ut_varchar2_rows) return self as result ) / diff --git a/source/core/types/ut_run.tpb b/source/core/types/ut_run.tpb index d5ace34a7..c8f2778ff 100644 --- a/source/core/types/ut_run.tpb +++ b/source/core/types/ut_run.tpb @@ -26,10 +26,12 @@ create or replace type body ut_run as a_project_file_mappings ut_file_mappings := null, a_test_file_mappings ut_file_mappings := null, a_client_character_set varchar2 := null, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_run_tags ut_varchar2_rows := null ) return self as result is begin self.run_paths := a_run_paths; + self.run_tags := a_run_tags; self.self_type := $$plsql_unit; self.items := a_items; self.client_character_set := lower(a_client_character_set); diff --git a/source/core/types/ut_run.tps b/source/core/types/ut_run.tps index 5d5e65367..b5a1d18ac 100644 --- a/source/core/types/ut_run.tps +++ b/source/core/types/ut_run.tps @@ -21,6 +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, coverage_options ut_coverage_options, test_file_mappings ut_file_mappings, client_character_set varchar2(100), @@ -35,7 +36,8 @@ create or replace type ut_run under ut_suite_item ( a_project_file_mappings ut_file_mappings := null, a_test_file_mappings ut_file_mappings := null, a_client_character_set varchar2 := null, - a_random_test_order_seed positive := null + a_random_test_order_seed positive := null, + a_run_tags ut_varchar2_rows := null ) return self as result, overriding member procedure mark_as_skipped(self in out nocopy ut_run), overriding member function do_execute(self in out nocopy ut_run) return boolean, diff --git a/source/core/types/ut_suite.tpb b/source/core/types/ut_suite.tpb index 14a457623..1211d5d5c 100644 --- a/source/core/types/ut_suite.tpb +++ b/source/core/types/ut_suite.tpb @@ -17,7 +17,8 @@ create or replace type body ut_suite as */ constructor function ut_suite ( - self in out nocopy ut_suite, a_object_owner varchar2, a_object_name varchar2, a_line_no integer + self in out nocopy ut_suite, a_object_owner varchar2, a_object_name varchar2, a_line_no integer, + a_tags ut_varchar2_rows := null ) return self as result is begin self.self_type := $$plsql_unit; @@ -25,6 +26,7 @@ create or replace type body ut_suite as self.items := ut_suite_items(); before_all_list := ut_executables(); after_all_list := ut_executables(); + self.tags := coalesce(a_tags,ut_varchar2_rows()); return; end; diff --git a/source/core/types/ut_suite.tps b/source/core/types/ut_suite.tps index 7057e4911..dd44a817e 100644 --- a/source/core/types/ut_suite.tps +++ b/source/core/types/ut_suite.tps @@ -27,7 +27,8 @@ create or replace type ut_suite under ut_logical_suite ( */ after_all_list ut_executables, constructor function ut_suite ( - self in out nocopy ut_suite, a_object_owner varchar2, a_object_name varchar2, a_line_no integer + self in out nocopy ut_suite, a_object_owner varchar2, a_object_name varchar2, a_line_no integer, + a_tags ut_varchar2_rows := null ) return self as result, overriding member function do_execute(self in out nocopy ut_suite) return boolean, overriding member function get_error_stack_traces(self ut_suite) return ut_varchar2_list, diff --git a/source/core/types/ut_suite_item.tps b/source/core/types/ut_suite_item.tps index 6b3b9c686..a5eb10986 100644 --- a/source/core/types/ut_suite_item.tps +++ b/source/core/types/ut_suite_item.tps @@ -60,6 +60,10 @@ create or replace type ut_suite_item force under ut_event_item ( warnings ut_varchar2_rows, results_count ut_results_counter, transaction_invalidators ut_varchar2_list, + /** + * Hold list of tags assign to test + */ + tags ut_varchar2_rows, member procedure init(self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_line_no integer), member function get_disabled_flag return boolean, not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item), diff --git a/source/core/types/ut_test.tpb b/source/core/types/ut_test.tpb index 099704fd5..bb71e1a5b 100644 --- a/source/core/types/ut_test.tpb +++ b/source/core/types/ut_test.tpb @@ -18,7 +18,7 @@ create or replace type body ut_test as constructor function ut_test( self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, - a_line_no integer, a_expected_error_codes ut_integer_list := null + a_line_no integer, a_expected_error_codes ut_integer_list := null, a_tags ut_varchar2_rows := null ) return self as result is begin self.self_type := $$plsql_unit; @@ -31,6 +31,7 @@ create or replace type body ut_test as self.all_expectations := ut_expectation_results(); self.failed_expectations := ut_expectation_results(); self.expected_error_codes := a_expected_error_codes; + self.tags := coalesce(a_tags,ut_varchar2_rows()); return; end; diff --git a/source/core/types/ut_test.tps b/source/core/types/ut_test.tps index 7ca01313a..6751ec522 100644 --- a/source/core/types/ut_test.tps +++ b/source/core/types/ut_test.tps @@ -1,4 +1,4 @@ -create or replace type ut_test under ut_suite_item ( +create or replace type ut_test force under ut_suite_item ( /* utPLSQL - Version 3 Copyright 2016 - 2018 utPLSQL Project @@ -57,7 +57,7 @@ create or replace type ut_test under ut_suite_item ( expected_error_codes ut_integer_list, constructor function ut_test( self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, - a_line_no integer, a_expected_error_codes ut_integer_list := null + a_line_no integer, a_expected_error_codes ut_integer_list := null, a_tags ut_varchar2_rows := null ) return self as result, overriding member procedure mark_as_skipped(self in out nocopy ut_test), overriding member function do_execute(self in out nocopy ut_test) return boolean, diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index b9e5b1b22..295f2d699 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -15,7 +15,7 @@ create or replace package body ut_suite_builder is See the License for the specific language governing permissions and limitations under the License. */ - + subtype t_annotation_text is varchar2(4000); subtype t_annotation_name is varchar2(4000); subtype t_object_name is varchar2(500); @@ -23,6 +23,7 @@ create or replace package body ut_suite_builder is gc_suite constant t_annotation_name := 'suite'; gc_suitepath constant t_annotation_name := 'suitepath'; + gc_tags constant t_annotation_name := 'tags'; gc_test constant t_annotation_name := ut_utils.gc_test_execute; gc_disabled constant t_annotation_name := 'disabled'; gc_displayname constant t_annotation_name := 'displayname'; @@ -43,6 +44,7 @@ create or replace package body ut_suite_builder is := tt_annotations( gc_suite, gc_suitepath, + gc_tags, gc_test, gc_disabled, gc_displayname, @@ -304,7 +306,34 @@ create or replace package body ut_suite_builder is l_annotation_pos := a_throws_ann_text.next(l_annotation_pos); end loop; end; - + + procedure add_tags_to_suite_item( + a_suite in out nocopy ut_suite, + a_tags_ann_text tt_annotation_texts, + a_list in out nocopy ut_varchar2_rows, + a_procedure_name t_object_name := null + ) is + l_annotation_pos binary_integer; + l_tag_list ut_varchar2_list := ut_varchar2_list(); + begin + l_annotation_pos := a_tags_ann_text.first; + while l_annotation_pos is not null loop + if a_tags_ann_text(l_annotation_pos) is null then + a_suite.put_warning( + '"--%tags" annotation requires a tag value populated. Annotation ignored.' + || chr( 10 ) || 'at "' || get_qualified_object_name(a_suite, a_procedure_name) || '", line ' || l_annotation_pos + ); + else + l_tag_list := l_tag_list multiset union distinct ut_utils.trim_list_elements( + ut_utils.string_to_table(a_tags_ann_text(l_annotation_pos),',') + ); + end if; + l_annotation_pos := a_tags_ann_text.next(l_annotation_pos); + end loop; + --remove empty strings from table list e.g. tag1,,tag2 and conver to rows + a_list := ut_utils.convert_collection( ut_utils.filter_list(l_tag_list,ut_utils.gc_word_no_space) ); + end; + procedure set_seq_no( a_list in out nocopy ut_executables ) is @@ -483,13 +512,19 @@ create or replace package body ut_suite_builder is ); set_seq_no(l_test.after_test_list); end if; + + if l_proc_annotations.exists( gc_tags) then + add_tags_to_suite_item(a_suite, l_proc_annotations( gc_tags), l_test.tags, a_procedure_name); + end if; + if l_proc_annotations.exists( gc_throws) then add_to_throws_numbers_list(a_suite, l_test.expected_error_codes, a_procedure_name, l_proc_annotations( gc_throws)); end if; l_test.disabled_flag := ut_utils.boolean_to_int( l_proc_annotations.exists( gc_disabled)); - + a_suite_items.extend; a_suite_items( a_suite_items.last ) := l_test; + end; procedure propagate_before_after_each( @@ -631,7 +666,10 @@ create or replace package body ut_suite_builder is if a_annotations.by_name.exists(gc_aftereach) then l_after_each_list := add_executables( a_suite.object_owner, a_suite.object_name, a_annotations.by_name(gc_aftereach), gc_aftereach ); end if; - + + if a_annotations.by_name.exists(gc_tags) then + add_tags_to_suite_item(a_suite, a_annotations.by_name(gc_tags),a_suite.tags); + end if; a_suite.disabled_flag := ut_utils.boolean_to_int(a_annotations.by_name.exists(gc_disabled)); --process procedure annotations for suite diff --git a/source/core/ut_suite_cache.sql b/source/core/ut_suite_cache.sql index adbdf1734..8687858a3 100644 --- a/source/core/ut_suite_cache.sql +++ b/source/core/ut_suite_cache.sql @@ -36,6 +36,7 @@ create table ut_suite_cache ( after_each_list, after_test_list, expected_error_codes, + tags, item ) nested table warnings store as ut_suite_cache_warnings @@ -45,7 +46,8 @@ create table ut_suite_cache ( nested table after_each_list store as ut_suite_cache_after_each nested table before_test_list store as ut_suite_cache_before_test nested table after_test_list store as ut_suite_cache_after_test - nested table expected_error_codes store as ut_suite_cache_trhows + nested table expected_error_codes store as ut_suite_cache_throws + nested table tags store as ut_suite_cache_tags return as locator as select cast(null as number(22)) id, @@ -67,6 +69,7 @@ create table ut_suite_cache ( t.after_each_list, t.after_test_list, t.expected_error_codes, + t.tags, t.item from table(ut_suite_contexts(ut_suite_context(user,'package_name','ctx_name',1))) c cross join table(ut_tests(ut_test(user,'package_name','test_name',1))) t diff --git a/source/core/ut_suite_cache_manager.pkb b/source/core/ut_suite_cache_manager.pkb index ce7f018e0..b805919fc 100644 --- a/source/core/ut_suite_cache_manager.pkb +++ b/source/core/ut_suite_cache_manager.pkb @@ -90,7 +90,8 @@ create or replace package body ut_suite_cache_manager is before_all_list, after_all_list, before_each_list, after_each_list, before_test_list, after_test_list, - expected_error_codes, item + expected_error_codes, tags, + item ) with suites as ( select treat(value(x) as ut_suite) i @@ -103,7 +104,8 @@ create or replace package body ut_suite_cache_manager is s.i.before_all_list as before_all_list, s.i.after_all_list as after_all_list, null before_each_list, null after_each_list, null before_test_list, null after_test_list, - null expected_error_codes, null item + null expected_error_codes, s.i.tags tags, + null item from suites s; insert into ut_suite_cache t @@ -114,7 +116,8 @@ create or replace package body ut_suite_cache_manager is before_all_list, after_all_list, before_each_list, after_each_list, before_test_list, after_test_list, - expected_error_codes, item + expected_error_codes, tags, + item ) with tests as ( select treat(value(x) as ut_test) t @@ -127,9 +130,10 @@ create or replace package body ut_suite_cache_manager is null before_all_list, null after_all_list, s.t.before_each_list as before_each_list, s.t.after_each_list as after_each_list, s.t.before_test_list as before_test_list, s.t.after_test_list as after_test_list, - s.t.expected_error_codes as expected_error_codes, s.t.item as item + s.t.expected_error_codes as expected_error_codes, s.t.tags as test_tags, + s.t.item as item from tests s; - + end if; end if; commit; diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index 8d2d0b781..3ec25019e 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -18,6 +18,70 @@ create or replace package body ut_suite_manager is gc_suitpath_error_message constant varchar2(100) := 'Suitepath exceeds 1000 CHAR on: '; + gc_get_cache_suite_sql constant varchar2(32767) := + q'[with + suite_items as ( + select /*+ cardinality(c 100) */ c.* + from {:owner:}.ut_suite_cache c + where 1 = 1 {:object_list:} + and c.object_owner = '{:object_owner:}' + and ( {:path:} + and {:object_name:} + and {:procedure_name:} + ) + ) + ), + {:tags:}, + suitepaths as ( + select distinct substr(path,1,instr(path,'.',-1)-1) as suitepath, + path, + object_owner + from {:suite_item_name:} + where self_type = 'UT_SUITE' + ), + gen as ( + select rownum as pos + from xmltable('1 to 20') + ), + suitepath_part AS ( + select distinct + substr(b.suitepath, 1, instr(b.suitepath || '.', '.', 1, g.pos) -1) as path, + object_owner + from suitepaths b + join gen g + on g.pos <= regexp_count(b.suitepath, '\w+') + ), + logical_suite_data as ( + select 'UT_LOGICAL_SUITE' as self_type, p.path, p.object_owner, + upper( substr(p.path, instr( p.path, '.', -1 ) + 1 ) ) as object_name, + cast(null as {:owner:}.ut_executables) as x, + cast(null as {:owner:}.ut_integer_list) as y, + cast(null as {:owner:}.ut_executable_test) as z + from suitepath_part p + where p.path + not in (select s.path from suitepaths s) + ), + logical_suites as ( + select to_number(null) as id, s.self_type, s.path, s.object_owner, s.object_name, + s.object_name as name, null as line_no, null as parse_time, + null as description, null as rollback_type, 0 as disabled_flag, + {:owner:}.ut_varchar2_rows() as warnings, + s.x as before_all_list, s.x as after_all_list, + s.x as before_each_list, s.x as before_test_list, + s.x as after_each_list, s.x as after_test_list, + s.y as expected_error_codes, null as test_tags, + s.z as item + from logical_suite_data s + ), + items as ( + select * from {:suite_item_name:} + union all + select * from logical_suites + ) + select c.* + from items c + order by c.object_owner,{:random_seed:}]'; + type t_path_item is record ( object_name varchar2(250), procedure_name varchar2(250), @@ -100,6 +164,7 @@ create or replace package body ut_suite_manager is l_schema_names.extend; l_schema_names(l_schema_names.last) := l_schema; end loop; + return l_schema_names; end; @@ -192,7 +257,7 @@ create or replace package body ut_suite_manager is results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), items => a_items_at_level(a_prev_level), before_all_list => sort_by_seq_no( a_rows( a_idx ).before_all_list), after_all_list => sort_by_seq_no( - a_rows( a_idx ).after_all_list) + a_rows( a_idx ).after_all_list), tags => a_rows(a_idx).tags ) else ut_suite( @@ -205,7 +270,7 @@ create or replace package body ut_suite_manager is results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), items => ut_suite_items(), before_all_list => sort_by_seq_no( a_rows( a_idx ).before_all_list), after_all_list => sort_by_seq_no( - a_rows( a_idx ).after_all_list) + a_rows( a_idx ).after_all_list), tags => a_rows(a_idx).tags ) end; when 'UT_SUITE_CONTEXT' then @@ -221,7 +286,7 @@ create or replace package body ut_suite_manager is results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), items => a_items_at_level(a_prev_level), before_all_list => sort_by_seq_no( a_rows( a_idx ).before_all_list), after_all_list => sort_by_seq_no( - a_rows( a_idx ).after_all_list) + a_rows( a_idx ).after_all_list), tags => a_rows(a_idx).tags ) else ut_suite_context( @@ -234,7 +299,7 @@ create or replace package body ut_suite_manager is results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), items => ut_suite_items(), before_all_list => sort_by_seq_no( a_rows( a_idx ).before_all_list), after_all_list => sort_by_seq_no( - a_rows( a_idx ).after_all_list) + a_rows( a_idx ).after_all_list), tags => a_rows(a_idx).tags ) end; when 'UT_LOGICAL_SUITE' then @@ -248,7 +313,7 @@ create or replace package body ut_suite_manager is line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time, start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings, results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), - items => a_items_at_level(a_prev_level) + items => a_items_at_level(a_prev_level), tags => null ) else ut_logical_suite( @@ -259,7 +324,7 @@ create or replace package body ut_suite_manager is line_no => a_rows( a_idx ).line_no, parse_time => a_rows( a_idx ).parse_time, start_time => null, end_time => null, result => null, warnings => a_rows( a_idx ).warnings, results_count => ut_results_counter(), transaction_invalidators => ut_varchar2_list(), - items => ut_suite_items() + items => ut_suite_items(), tags => null ) end; when 'UT_TEST' then @@ -276,7 +341,8 @@ create or replace package body ut_suite_manager is item => a_rows(a_idx).item, after_test_list => sort_by_seq_no(a_rows(a_idx).after_test_list), after_each_list => sort_by_seq_no(a_rows(a_idx).after_each_list), all_expectations => ut_expectation_results(), failed_expectations => ut_expectation_results(), - parent_error_stack_trace => null, expected_error_codes => a_rows(a_idx).expected_error_codes + parent_error_stack_trace => null, expected_error_codes => a_rows(a_idx).expected_error_codes, + tags => a_rows(a_idx).tags ); end case; l_result.results_count.warnings_count := l_result.warnings.count; @@ -356,121 +422,65 @@ create or replace package body ut_suite_manager is return l_result; end; - function get_cached_suite_data( - a_object_owner varchar2, - a_path varchar2 := null, - a_object_name varchar2 := null, - a_procedure_name varchar2 := null, - a_skip_all_objects boolean := false, - a_random_seed positive - ) return t_cached_suites_cursor is - l_path varchar2(4000); - l_result sys_refcursor; - l_ut_owner varchar2(250) := ut_utils.ut_owner; - l_object_owner varchar2(250); - l_object_name varchar2(250); - l_procedure_name varchar2(250); + function get_object_names_sql(a_skip_all_objects boolean ) return varchar2 is begin - if a_object_owner is not null then - l_object_owner := sys.dbms_assert.qualified_sql_name(a_object_owner); - end if; - if a_object_name is not null then - l_object_name := sys.dbms_assert.qualified_sql_name(a_object_name); - end if; - if a_procedure_name is not null then - l_procedure_name := sys.dbms_assert.qualified_sql_name(a_procedure_name); - end if; - if a_path is null and a_object_name is not null then - execute immediate 'select min(path) - from '||l_ut_owner||q'[.ut_suite_cache - where object_owner = :a_object_owner - and object_name = :a_object_name - and name = nvl(:a_procedure_name, name)]' - into l_path using upper(l_object_owner), upper(l_object_name), upper(l_procedure_name); - else - if a_path is not null then - l_path := lower(sys.dbms_assert.qualified_sql_name(a_path)); - end if; - end if; - - open l_result for - q'[with - suite_items as ( - select /*+ cardinality(c 100) */ c.* - from ]'||l_ut_owner||q'[.ut_suite_cache c - where 1 = 1 ]'||case when not a_skip_all_objects then q'[ + return case when not a_skip_all_objects then q'[ and exists ( select 1 from all_objects a where a.object_name = c.object_name - and a.owner = ']'||upper(a_object_owner)||q'[' + and a.owner = '{:object_owner:}' and a.owner = c.object_owner and a.object_type = 'PACKAGE' - )]' end ||q'[ - and c.object_owner = ']'||upper(a_object_owner)||q'[' - and ( ]' || case when l_path is not null then q'[ + )]' else null end; + end; + + function get_path_sql(a_path in varchar2) return varchar2 is + begin + return case when a_path is not null then q'[ :l_path||'.' like c.path || '.%' /*all children and self*/ or ( c.path||'.' like :l_path || '.%' --all parents ]' - else ' :l_path is null and ( :l_path is null ' end - || case when a_object_name is not null - then 'and c.object_name = :a_object_name ' - else 'and :a_object_name is null' end ||' - '|| case when a_procedure_name is not null - then 'and c.name = :a_procedure_name' - else 'and :a_procedure_name is null' end ||q'[ - ) - ) - ), - suitepaths as ( - select distinct substr(path,1,instr(path,'.',-1)-1) as suitepath, - path, - object_owner - from suite_items - where self_type = 'UT_SUITE' - ), - gen as ( - select rownum as pos - from xmltable('1 to 20') - ), - suitepath_part AS ( - select distinct - substr(b.suitepath, 1, instr(b.suitepath || '.', '.', 1, g.pos) -1) as path, - object_owner - from suitepaths b - join gen g - on g.pos <= regexp_count(b.suitepath, '\w+') - ), - logical_suite_data as ( - select 'UT_LOGICAL_SUITE' as self_type, p.path, p.object_owner, - upper( substr(p.path, instr( p.path, '.', -1 ) + 1 ) ) as object_name, - cast(null as ]'||l_ut_owner||q'[.ut_executables) as x, - cast(null as ]'||l_ut_owner||q'[.ut_integer_list) as y, - cast(null as ]'||l_ut_owner||q'[.ut_executable_test) as z - from suitepath_part p - where p.path - not in (select s.path from suitepaths s) - ), - logical_suites as ( - select to_number(null) as id, s.self_type, s.path, s.object_owner, s.object_name, - s.object_name as name, null as line_no, null as parse_time, - null as description, null as rollback_type, 0 as disabled_flag, - ]'||l_ut_owner||q'[.ut_varchar2_rows() as warnings, - s.x as before_all_list, s.x as after_all_list, - s.x as before_each_list, s.x as before_test_list, - s.x as after_each_list, s.x as after_test_list, - s.y as expected_error_codes, s.z as item - from logical_suite_data s + else ' :l_path is null and ( :l_path is null ' end; + end; + + function get_object_name_sql(a_object_name in varchar2) return varchar2 is + begin + return case when a_object_name is not null + then ' c.object_name = :a_object_name ' + else ' :a_object_name is null' end; + end; + + function get_procedure_name_sql(a_procedure_name in varchar2) return varchar2 is + begin + return case when a_procedure_name is not null + then ' c.name = :a_procedure_name' + else ' :a_procedure_name is null' end; + end; + + function get_tags_sql(a_tags_count in integer) return varchar2 is + begin + return case when a_tags_count > 0 then + q'[filter_tags as ( + select c.* + from suite_items c + where c.tags multiset intersect :a_tag_list is not empty ), - items as ( - select * from suite_items - union all - select * from logical_suites - ) - select c.* - from items c - order by c.object_owner,]'|| - case + suite_items_tags as ( + select c.* from suite_items c + where exists (select 1 from filter_tags t where + t.path||'.' like c.path || '.%' /*all children and self*/ + or c.path||'.' like t.path || '.%' --all parents + ) + )]' + else + q'[dummy as (select 'x' from dual where :a_tag_list is null )]' + end; + end; + + function get_random_seed_sql(a_random_seed positive) return varchar2 is + begin + return case when a_random_seed is null then q'[ replace( case @@ -483,11 +493,68 @@ create or replace package body ut_suite_manager is c.line_no, :a_random_seed]' else - l_ut_owner||'.ut_annotation_manager.hash_suite_path( + ' {:owner:}.ut_annotation_manager.hash_suite_path( c.path, :a_random_seed ) desc nulls last' - end - using l_path, l_path, upper(a_object_name), upper(a_procedure_name), a_random_seed; + end; + end; + + function get_cached_suite_data( + a_object_owner varchar2, + a_path varchar2 := null, + a_object_name varchar2 := null, + a_procedure_name varchar2 := null, + a_skip_all_objects boolean := false, + a_random_seed positive, + a_tags ut_varchar2_rows := null + ) return t_cached_suites_cursor is + l_path varchar2(4000); + l_result sys_refcursor; + l_ut_owner varchar2(250) := ut_utils.ut_owner; + l_sql varchar2(32767); + l_suite_item_name varchar2(20); + l_tags ut_varchar2_rows := coalesce(a_tags,ut_varchar2_rows()); + l_object_owner varchar2(250); + l_object_name varchar2(250); + l_procedure_name varchar2(250); + begin + if a_object_owner is not null then + l_object_owner := sys.dbms_assert.qualified_sql_name(a_object_owner); + end if; + if a_object_name is not null then + l_object_name := sys.dbms_assert.qualified_sql_name(a_object_name); + end if; + if a_procedure_name is not null then + l_procedure_name := sys.dbms_assert.qualified_sql_name(a_procedure_name); + end if; + if a_path is null and a_object_name is not null then + execute immediate 'select min(path) + from '||l_ut_owner||q'[.ut_suite_cache + where object_owner = :a_object_owner + and object_name = :a_object_name + and name = nvl(:a_procedure_name, name)]' + into l_path using upper(l_object_owner), upper(l_object_name), upper(a_procedure_name); + else + if a_path is not null then + l_path := lower(sys.dbms_assert.qualified_sql_name(a_path)); + end if; + end if; + l_suite_item_name := case when l_tags.count > 0 then 'suite_items_tags' else 'suite_items' end; + + l_sql := gc_get_cache_suite_sql; + l_sql := replace(l_sql,'{:suite_item_name:}',l_suite_item_name); + l_sql := replace(l_sql,'{:object_list:}',get_object_names_sql(a_skip_all_objects)); + l_sql := replace(l_sql,'{:object_owner:}',upper(l_object_owner)); + l_sql := replace(l_sql,'{:path:}',get_path_sql(l_path)); + l_sql := replace(l_sql,'{:object_name:}',get_object_name_sql(l_object_name)); + l_sql := replace(l_sql,'{:procedure_name:}',get_procedure_name_sql(l_procedure_name)); + l_sql := replace(l_sql,'{:tags:}',get_tags_sql(l_tags.count)); + l_sql := replace(l_sql,'{:random_seed:}',get_random_seed_sql(a_random_seed)); + l_sql := replace(l_sql,'{:owner:}',l_ut_owner); + + ut_event_manager.trigger_event(ut_event_manager.gc_debug, ut_key_anyvalues().put('l_sql',l_sql) ); + + open l_result for l_sql using l_path, l_path, upper(a_object_name), upper(a_procedure_name), l_tags, a_random_seed; return l_result; end; @@ -574,7 +641,8 @@ create or replace package body ut_suite_manager is a_object_name varchar2 := null, a_procedure_name varchar2 := null, a_suites in out nocopy ut_suite_items, - a_random_seed positive + a_random_seed positive, + a_tags ut_varchar2_rows := null ) is begin refresh_cache(a_owner_name); @@ -587,7 +655,8 @@ create or replace package body ut_suite_manager is a_object_name, a_procedure_name, can_skip_all_objects_scan(a_owner_name), - a_random_seed + a_random_seed, + a_tags ) ); @@ -617,6 +686,7 @@ create or replace package body ut_suite_manager is a_object_name, a_procedure_name, a_skip_all_objects, + null, null ) ); @@ -676,9 +746,10 @@ create or replace package body ut_suite_manager is end; procedure configure_execution_by_path( - a_paths in ut_varchar2_list, - a_suites out nocopy ut_suite_items, - a_random_seed in positive := null + 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() ) is l_paths ut_varchar2_list := a_paths; l_path_items t_path_items; @@ -705,7 +776,8 @@ create or replace package body ut_suite_manager is l_path_item.object_name, l_path_item.procedure_name, a_suites, - a_random_seed + a_random_seed, + a_tags ); if a_suites.count = l_suites_count then if l_path_item.suite_path is not null then @@ -799,17 +871,17 @@ create or replace package body ut_suite_manager is items as ( select object_owner, object_name, name as item_name, description as item_description, self_type as item_type, line_no as item_line_no, - path, disabled_flag + path, disabled_flag,tags from suite_items union all select object_owner, object_name, object_name as item_name, null as item_description, item_type, null as item_line_no, - s.path, 0 as disabled_flag + s.path, 0 as disabled_flag, ]'||l_ut_owner||q'[.ut_varchar2_rows() as tags from logical_suites s ) select ]'||l_ut_owner||q'[.ut_suite_item_info( object_owner, object_name, item_name, item_description, - item_type, item_line_no, path, disabled_flag + item_type, item_line_no, path, disabled_flag, tags ) from items c]' using upper(l_package_name); @@ -837,7 +909,7 @@ create or replace package body ut_suite_manager is if a_procedure_name is not null then l_procedure_name := sys.dbms_assert.qualified_sql_name(a_procedure_name); end if; - + refresh_cache(l_owner_name); execute immediate q'[ diff --git a/source/core/ut_suite_manager.pks b/source/core/ut_suite_manager.pks index debfd4ac1..bf8819693 100644 --- a/source/core/ut_suite_manager.pks +++ b/source/core/ut_suite_manager.pks @@ -49,7 +49,8 @@ create or replace package ut_suite_manager authid current_user is procedure configure_execution_by_path( a_paths in ut_varchar2_list, a_suites out nocopy ut_suite_items, - a_random_seed in positive := null + a_random_seed in positive := null, + a_tags ut_varchar2_rows := ut_varchar2_rows() ); /** diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index 8ee9a25e1..8e0af7b8d 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -285,6 +285,20 @@ create or replace package body ut_utils is return l_result; end; + function table_to_clob(a_text_table ut_varchar2_rows, a_delimiter varchar2:= chr(10)) return clob is + l_result clob; + l_table_rows integer := coalesce(cardinality(a_text_table),0); + begin + for i in 1 .. l_table_rows loop + if i < l_table_rows then + append_to_clob(l_result, a_text_table(i)||a_delimiter); + else + append_to_clob(l_result, a_text_table(i)); + end if; + end loop; + return l_result; + end; + function table_to_clob(a_integer_table ut_integer_list, a_delimiter varchar2:= chr(10)) return clob is l_result clob; l_table_rows integer := coalesce(cardinality(a_integer_table),0); @@ -603,7 +617,6 @@ create or replace package body ut_utils is if a_list is not null then l_filtered_list := ut_varchar2_list(); l_index := a_list.first; - while (l_index is not null) loop if regexp_like(a_list(l_index), a_regexp_filter) then l_filtered_list.extend; diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index de32c11b0..28a80be01 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -136,6 +136,11 @@ create or replace package ut_utils authid definer is gc_bc_fetch_limit constant integer := 1000; gc_diff_max_rows constant integer := 20; + /** + * Regexp to validate tag + */ + gc_word_no_space constant varchar2(50) := '^(\w|\S)+$'; + type t_version is record( major natural, minor natural, @@ -239,7 +244,9 @@ create or replace package ut_utils authid definer is function clob_to_table(a_clob clob, a_max_amount integer := 8191, a_delimiter varchar2:= chr(10)) return ut_varchar2_list; function table_to_clob(a_text_table ut_varchar2_list, a_delimiter varchar2:= chr(10)) return clob; - + + function table_to_clob(a_text_table ut_varchar2_rows, a_delimiter varchar2:= chr(10)) return clob; + function table_to_clob(a_integer_table ut_integer_list, a_delimiter varchar2:= chr(10)) return clob; /** diff --git a/source/install.sql b/source/install.sql index 728f4349f..465beaa48 100644 --- a/source/install.sql +++ b/source/install.sql @@ -279,6 +279,7 @@ prompt Installing DBMSPLSQL Tables objects into &&ut3_owner schema --plugin interface API for running utPLSQL @@install_component.sql 'api/ut_suite_item_info.tps' +@@install_component.sql 'api/ut_suite_item_info.tpb' @@install_component.sql 'api/ut_suite_items_info.tps' @@install_component.sql 'api/ut_runner.pks' @@install_component.sql 'api/ut_runner.pkb' diff --git a/test/ut3_tester/core/test_suite_builder.pkb b/test/ut3_tester/core/test_suite_builder.pkb index e6d21d969..523d31a3d 100644 --- a/test/ut3_tester/core/test_suite_builder.pkb +++ b/test/ut3_tester/core/test_suite_builder.pkb @@ -7,6 +7,8 @@ create or replace package body test_suite_builder is l_suites ut3.ut_suite_items; l_suite ut3.ut_logical_suite; l_cursor sys_refcursor; + l_type_cursor sys_refcursor; + l_ctx dbms_xmlgen.ctxhandle; l_xml xmltype; begin open l_cursor for select value(x) from table( @@ -22,9 +24,14 @@ create or replace package body test_suite_builder is a_skip_all_objects => true ); l_suite := treat( l_suites(l_suites.first) as ut3.ut_logical_suite); - + + open l_type_cursor for select l_suite as "UT_LOGICAL_SUITE" from dual; + l_ctx := dbms_xmlgen.newcontext(l_type_cursor); + dbms_xmlgen.setNullHandling(l_ctx, dbms_xmlgen.empty_tag); + l_xml := dbms_xmlgen.getxmltype(l_ctx); + select deletexml( - xmltype(l_suite), + l_xml, '//RESULTS_COUNT|//START_TIME|//END_TIME|//RESULT|//ASSOCIATED_EVENT_NAME' || '|//TRANSACTION_INVALIDATORS|//ERROR_BACKTRACE|//ERROR_STACK|//SERVEROUTPUT' ) @@ -638,7 +645,8 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%' || + ''|| + '' || '%' || '%' || '' || @@ -661,7 +669,8 @@ create or replace package body test_suite_builder is '%some_packagesuite_level_beforeall' || '%' || '' || - '' + ''|| + '' ); end; @@ -685,6 +694,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( + ''|| '' || '%' || '%' || @@ -705,7 +715,8 @@ create or replace package body test_suite_builder is '%%suite_level_test%' || '%' || '%' || - '%' + '%'|| + '' ); ut.expect(l_actual).not_to_be_like('%%%%%%'); ut.expect(l_actual).not_to_be_like('%%%%%%'); @@ -733,6 +744,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( + ''|| '' || '%' || '%' || @@ -755,7 +767,8 @@ create or replace package body test_suite_builder is '%' || '%%suite_level_beforeall%' || '%%suite_level_afterall%' || - '%' + '%'|| + '' ); ut.expect(l_actual).not_to_be_like('%%%%%%'); ut.expect(l_actual).not_to_be_like('%%%%%%'); @@ -782,6 +795,7 @@ create or replace package body test_suite_builder is ,'\' ); ut.expect(l_actual).to_be_like( + ''|| '' || '%' || '' || @@ -796,7 +810,8 @@ create or replace package body test_suite_builder is '%some_packagecontext_setup' || '%' || '' || - '' + ''|| + '' ); end; @@ -824,6 +839,7 @@ create or replace package body test_suite_builder is ,'\' ); ut.expect(l_actual).to_be_like( + ''|| '' || '%' || '' || @@ -846,7 +862,8 @@ create or replace package body test_suite_builder is '%some_packagesuite_level_beforeall' || '%' || '' || - '' + ''|| + '' ); end; @@ -879,6 +896,7 @@ create or replace package body test_suite_builder is ,'\' ); ut.expect(l_actual).to_be_like( + ''|| '' || '%' || '' || @@ -901,7 +919,8 @@ create or replace package body test_suite_builder is '%some_packagesuite_level_beforeall' || '%' || '' || - '' + ''|| + '' ); end; @@ -1114,5 +1133,305 @@ create or replace package body test_suite_builder is ut.expect(l_actual).to_match('(.*)(Unsupported annotation "--%bad_package_annotation"\. Annotation ignored\.)(.*)( line 17)(.*)', 'n'); end; + procedure test_tag_annotation is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(8, 'test','Some test', 'test_procedure'), + ut3.ut_annotation(9, 'tags','testtag', 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%test_procedureSome testsome_package.test_procedure' || + '%testtag%'|| + '%%' + ); + + end; + + procedure suite_tag_annotation is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(3, 'tags','suitetag', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%some_packagetestsuitesome_package' || + '%suitetag%'|| + '%%' + ); + + end; + + procedure test_tags_annotation is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(8, 'test','Some test', 'test_procedure'), + ut3.ut_annotation(9, 'tags','testtag,testtag2,testtag3', 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%test_procedureSome testsome_package.test_procedure' || + '%testtagtesttag2testtag3%'|| + '%%' + ); + + end; + + procedure suite_tags_annotation is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(3, 'tags','suitetag,suitetag1,suitetag2', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%some_packagetestsuitesome_package' || + '%suitetagsuitetag1suitetag2%'|| + '%%' + ); + + end; + + procedure test_2line_tags_annotation is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(8, 'test','Some test', 'test_procedure'), + ut3.ut_annotation(9, 'tags','testtag', 'test_procedure'), + ut3.ut_annotation(10, 'tags','testtag2', 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%test_procedureSome testsome_package.test_procedure' || + '%testtagtesttag2%'|| + '%%' + ); + + end; + + procedure suite_2line_tags_annotation is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(3, 'tags','suitetag', null), + ut3.ut_annotation(4, 'tags','suitetag1', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%some_packagetestsuitesome_package' || + '%suitetagsuitetag1%'|| + '%%' + ); + + end; + + procedure test_empty_tag is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(8, 'test','Some test', 'test_procedure'), + ut3.ut_annotation(9, 'tags',null, 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%tags" annotation requires a tag value populated. Annotation ignored.%%'|| + '%%' + ); + + end; + + procedure suite_empty_tag is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(3, 'tags',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%"--%tags" annotation requires a tag value populated. Annotation ignored.%%'|| + '%%' + ); + + end; + + procedure test_duplicate_tag is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(8, 'test','Some test', 'test_procedure'), + ut3.ut_annotation(9, 'tags','testtag,testtag1,testtag', 'test_procedure'), + ut3.ut_annotation(10, 'tags',' testtag,testtag1,testtag2', 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%test_procedureSome testsome_package.test_procedure' || + '%testtagtesttag1testtag2%'|| + '%%' + ); + + end; + + procedure suite_duplicate_tag is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(3, 'tags','suitetag,suitetag1,suitetag', null), + ut3.ut_annotation(4, 'tags',' suitetag1,suitetag2', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%some_packagetestsuitesome_package' || + '%suitetagsuitetag1suitetag2%'|| + '%%' + ); + + end; + + procedure test_empty_tag_between is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(8, 'test','Some test', 'test_procedure'), + ut3.ut_annotation(9, 'tags','testtag,, ,testtag1', 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%test_procedureSome testsome_package.test_procedure' || + '%testtagtesttag1%'|| + '%%' + ); + + end; + + procedure suite_empty_tag_between is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(3, 'tags','suitetag,, ,suitetag1', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%some_packagetestsuitesome_package' || + '%suitetagsuitetag1%'|| + '%%' + ); + + end; + + procedure test_special_char_tag is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(8, 'test','Some test', 'test_procedure'), + ut3.ut_annotation(9, 'tags','#?$%^&*!|\/@][', 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%test_procedureSome testsome_package.test_procedure' || + '%#?$%^&*!|\/@][%'|| + '%%' + ); + + end; + + procedure suite_special_char_tag is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','testsuite', null), + ut3.ut_annotation(3, 'tags','#?$%^&*!|\/@][', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%some_packagetestsuitesome_package' || + '%#?$%^&*!|\/@][%'|| + '%%' + ); + + end; + end test_suite_builder; / diff --git a/test/ut3_tester/core/test_suite_builder.pks b/test/ut3_tester/core/test_suite_builder.pks index 5715cfd85..721db153f 100644 --- a/test/ut3_tester/core/test_suite_builder.pks +++ b/test/ut3_tester/core/test_suite_builder.pks @@ -172,5 +172,52 @@ create or replace package test_suite_builder is --%endcontext + --%context(tags_annotation) + --%displayname(--%tag_annotation) + + --%test(Build suite test with tag) + procedure test_tag_annotation; + + --%test(Build suite with tag) + procedure suite_tag_annotation; + + --%test(Build suite test with three tags) + procedure test_tags_annotation; + + --%test(Build suite with three tags) + procedure suite_tags_annotation; + + --%test(Build suite test with two line tag annotation) + procedure test_2line_tags_annotation; + + --%test(Build suite with two line tag annotation) + procedure suite_2line_tags_annotation; + + --%test(Build suite test with empty line tag annotation) + procedure test_empty_tag; + + --%test(Build suite with empty line tag annotation) + procedure suite_empty_tag; + + --%test(Build suite test with duplicate tag annotation) + procedure test_duplicate_tag; + + --%test(Build suite with duplicate tag annotation) + procedure suite_duplicate_tag; + + --%test(Build suite test with empty between tag annotation) + procedure test_empty_tag_between; + + --%test(Build suite with empty between tag annotation) + procedure suite_empty_tag_between; + + --%test(Build suite test with special char tag annotation) + procedure test_special_char_tag; + + --%test(Build suite with special char tag annotation) + procedure suite_special_char_tag; + + --%endcontext + end test_suite_builder; / diff --git a/test/ut3_tester_helper/run_helper.pkb b/test/ut3_tester_helper/run_helper.pkb index 0fad905fd..9bc2b7f79 100644 --- a/test/ut3_tester_helper/run_helper.pkb +++ b/test/ut3_tester_helper/run_helper.pkb @@ -27,6 +27,64 @@ create or replace package body run_helper is execute immediate q'[grant execute on ut3_tester_helper.dummy_test_procedure to public]'; end; + procedure setup_cache_objectstag is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package ut3$user#.dummy_test_package as + --%suite(dummy_test_suite) + --%tags(dummy) + --%rollback(manual) + + --%test(dummy_test) + --%tags(testtag) + --%beforetest(some_procedure) + procedure some_dummy_test_procedure; + end;]'; + execute immediate q'[create or replace procedure ut3$user#.dummy_test_procedure as + --%some_annotation(some_text) + --%rollback(manual) + begin + null; + end;]'; + execute immediate q'[create or replace procedure ut3_tester_helper.dummy_test_procedure as + --%some_annotation(some_text) + --%rollback(manual) + begin + null; + end;]'; + + execute immediate q'[grant execute on ut3_tester_helper.dummy_test_procedure to public]'; + end; + + procedure setup_cache_twotags is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package ut3$user#.dummy_test_package as + --%suite(dummy_test_suite) + --%tags(suitetag1,suitetag2) + --%rollback(manual) + + --%test(dummy_test) + --%tags(testtag1,testtag2) + --%beforetest(some_procedure) + procedure some_dummy_test_procedure; + end;]'; + execute immediate q'[create or replace procedure ut3$user#.dummy_test_procedure as + --%some_annotation(some_text) + --%rollback(manual) + begin + null; + end;]'; + execute immediate q'[create or replace procedure ut3_tester_helper.dummy_test_procedure as + --%some_annotation(some_text) + --%rollback(manual) + begin + null; + end;]'; + + execute immediate q'[grant execute on ut3_tester_helper.dummy_test_procedure to public]'; + end; + procedure create_trans_control is pragma autonomous_transaction; begin @@ -197,13 +255,16 @@ create or replace package body run_helper is begin execute immediate q'[create or replace package test_package_1 is --%suite + --%tags(suite1,helper) --%suitepath(tests) --%rollback(manual) --%test(Test1 from test package 1) + --%tags(test1,suite1test1,subtest1) procedure test1; --%test(Test2 from test package 1) + --%tags(test1,suite1test2) procedure test2; end test_package_1; @@ -223,12 +284,15 @@ create or replace package body run_helper is execute immediate q'[create or replace package test_package_2 is --%suite + --%tags(suite2,helper) --%suitepath(tests.test_package_1) --%test + --%tags(test2,suite2test1,subtest2) procedure test1; --%test + --%tags(suite2test2) procedure test2; end test_package_2; @@ -247,12 +311,15 @@ create or replace package body run_helper is execute immediate q'[create or replace package test_package_3 is --%suite + --%tags(suite3,helper) --%suitepath(tests2) --%test + --%tags(test1suite3) procedure test1; --%test + --%tags(test2suite3) procedure test2; end test_package_3; @@ -431,6 +498,44 @@ create or replace package body run_helper is )); return l_results; end; + + procedure run(a_reporter ut3.ut_reporter_base := null,a_tags varchar2) is + begin + ut3.ut.run(a_reporter,a_tags => a_tags); + end; + + procedure run(a_path varchar2, a_reporter ut3.ut_reporter_base := null,a_tags varchar2) is + begin + ut3.ut.run(a_path, a_reporter,a_tags => a_tags); + end; + + procedure run(a_paths ut3.ut_varchar2_list, a_reporter ut3.ut_reporter_base := null, a_tags varchar2) is + begin + ut3.ut.run(a_paths, a_reporter,a_tags => a_tags); + end; + + function run(a_reporter ut3.ut_reporter_base := null,a_tags varchar2) return ut3.ut_varchar2_list is + l_results ut3.ut_varchar2_list; + begin + select * bulk collect into l_results from table (ut3.ut.run(a_reporter, a_tags => a_tags)); + return l_results; + end; + + function run(a_path varchar2, a_reporter ut3.ut_reporter_base := null, a_tags varchar2) + return ut3.ut_varchar2_list is + l_results ut3.ut_varchar2_list; + begin + select * bulk collect into l_results from table (ut3.ut.run(a_path, a_reporter,a_tags => a_tags)); + return l_results; + end; + + function run(a_paths ut3.ut_varchar2_list, a_reporter ut3.ut_reporter_base := null, a_tags varchar2) + return ut3.ut_varchar2_list is + l_results ut3.ut_varchar2_list; + begin + select * bulk collect into l_results from table (ut3.ut.run(a_paths, a_reporter, a_tags => a_tags)); + return l_results; + end; procedure test_rollback_type(a_procedure_name varchar2, a_rollback_type integer, a_expectation ut3_latest_release.ut_matcher) is l_suite ut3.ut_suite; diff --git a/test/ut3_tester_helper/run_helper.pks b/test/ut3_tester_helper/run_helper.pks index 7d285d7af..e06401eae 100644 --- a/test/ut3_tester_helper/run_helper.pks +++ b/test/ut3_tester_helper/run_helper.pks @@ -6,6 +6,8 @@ create or replace package run_helper is type prof_runs_tab is table of ut3.plsql_profiler_runs%rowtype; procedure setup_cache_objects; + procedure setup_cache_objectstag; + procedure setup_cache_twotags; procedure setup_cache; procedure cleanup_cache; procedure create_db_link; @@ -41,6 +43,15 @@ create or replace package run_helper is return ut3.ut_varchar2_list; function run(a_test_files ut3.ut_varchar2_list, a_reporter ut3.ut_reporter_base) return ut3.ut_varchar2_list; + + procedure run(a_reporter ut3.ut_reporter_base := null,a_tags varchar2); + procedure run(a_path varchar2, a_reporter ut3.ut_reporter_base := null,a_tags varchar2); + procedure run(a_paths ut3.ut_varchar2_list, a_reporter ut3.ut_reporter_base := null, a_tags varchar2); + function run(a_reporter ut3.ut_reporter_base := null,a_tags varchar2) return ut3.ut_varchar2_list; + function run(a_path varchar2, a_reporter ut3.ut_reporter_base := null, a_tags varchar2) + return ut3.ut_varchar2_list; + function run(a_paths ut3.ut_varchar2_list, a_reporter ut3.ut_reporter_base := null, a_tags varchar2) + return ut3.ut_varchar2_list; procedure test_rollback_type(a_procedure_name varchar2, a_rollback_type integer, a_expectation ut3_latest_release.ut_matcher); diff --git a/test/ut3_user/api/test_ut_run.pkb b/test/ut3_user/api/test_ut_run.pkb index 620c87977..e59571723 100644 --- a/test/ut3_user/api/test_ut_run.pkb +++ b/test/ut3_user/api/test_ut_run.pkb @@ -787,5 +787,198 @@ Failures:% ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; + procedure test_run_by_one_tag is + l_results clob; + begin + ut3_tester_helper.run_helper.run(a_tags => 'suite1test1'); + 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 ).not_to_be_like( '%test_package_2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); + end; + + procedure suite_run_by_one_tag is + l_results clob; + begin + ut3_tester_helper.run_helper.run(a_tags => 'suite2'); + l_results := ut3_tester_helper.main_helper.get_dbms_output_as_clob(); + --Assert + ut.expect( l_results ).not_to_be_like( '%test_package_1.%executed%' ); + ut.expect( l_results ).to_be_like( '%test_package_1%' ); + ut.expect( l_results ).to_be_like( '%test_package_2%' ); + ut.expect( l_results ).to_be_like( '%test_package_2.%executed%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_3.%executed%' ); + end; + + procedure two_test_run_by_one_tag is + l_results clob; + begin + ut3_tester_helper.run_helper.run(a_tags => 'test2'); + l_results := ut3_tester_helper.main_helper.get_dbms_output_as_clob(); + --Assert + ut.expect( l_results ).not_to_be_like( '%test_package_1.%executed%' ); + ut.expect( l_results ).to_be_like( '%test_package_1%' ); + ut.expect( l_results ).to_be_like( '%test_package_2%' ); + ut.expect( l_results ).to_be_like( '%test_package_2.%executed%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_3.%executed%' ); + end; + + procedure all_suites_run_by_one_tag is + l_results clob; + begin + ut3_tester_helper.run_helper.run(a_tags => 'helper'); + 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 ).to_be_like( '%test_package_3%' ); + end; + + procedure two_test_run_by_two_tags 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 + ut3_tester_helper.run_helper.run(a_tags => 'suite1'); + 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_3%' ); + end; + + procedure suite_with_tag_parent is + l_results clob; + begin + ut3_tester_helper.run_helper.run(a_tags => 'suite2'); + 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_3%' ); + end; + + procedure test_nonexists_tag is + l_results clob; + l_exp_message varchar2(4000); + begin + ut3_tester_helper.run_helper.run(a_tags => 'nonexisting'); + l_results := ut3_tester_helper.main_helper.get_dbms_output_as_clob(); + ut.expect( l_results ).not_to_be_like( '%test_package_1%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); + end; + + procedure test_duplicate_tag is + l_results clob; + begin + ut3_tester_helper.run_helper.run(a_tags => 'suite1test1,suite1test1'); + 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 ).not_to_be_like( '%test_package_2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); + end; + + procedure suite_duplicate_tag is + l_results clob; + begin + ut3_tester_helper.run_helper.run(a_tags => 'suite1,suite1'); + 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_3%' ); + end; + + procedure run_proc_pkg_name_tag is + l_results clob; + begin + ut3.ut.run('ut3_tester_helper.test_package_1',a_tags => 'suite1test1'); + 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_1.test1%executed%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_1.test2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); + end; + + procedure run_pkg_name_file_list_tag is + l_results clob; + begin + ut3.ut.run('ut3_tester_helper.test_package_1',a_tags => 'suite1test1'); + 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_1.test1%executed%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_1.test2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_2%' ); + ut.expect( l_results ).not_to_be_like( '%test_package_3%' ); + end; + + procedure run_proc_path_list_tag is + l_results clob; + begin + ut3.ut.run( + 'ut3_tester_helper.test_package_1', + ut3.ut_sonar_test_reporter(), a_source_files => ut3.ut_varchar2_list(),a_tags => 'suite1', + a_test_files => ut3.ut_varchar2_list('tests/ut3_tester_helper.test_package_1.pkb', + 'tests/ut3_tester_helper.test_package_2.pkb', + 'tests/ut3_tester_helper.test_package_3.pkb') + ); + l_results := ut3_tester_helper.main_helper.get_dbms_output_as_clob(); + --Assert + ut.expect( l_results ).to_be_like( '%tests/ut3_tester_helper.test_package_1.pkb%' ); + ut.expect( l_results ).not_to_be_like( '%tests/ut3_tester_helper.test_package_2.pkb%' ); + ut.expect( l_results ).not_to_be_like( '%tests/ut3_tester_helper.test_package_3.pkb%' ); + end; + + procedure tag_run_func_no_params is + l_results ut3.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(a_tags => 'helper'); + --Assert + ut.expect( ut3_tester_helper.main_helper.table_to_clob(l_results) ).to_be_like( '%test_package_1%test_package_2%test_package_3%' ); + end; + + procedure tag_run_func_pkg_name is + l_results ut3.ut_varchar2_list; + begin + select * bulk collect into l_results from table (ut3.ut.run('ut3_tester_helper.test_package_1', a_tags => 'suite1test1')); + --Assert + 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%' ); + 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 is + l_results ut3.ut_varchar2_list; + begin + l_results := ut3_tester_helper.run_helper.run(ut3.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; + end; / diff --git a/test/ut3_user/api/test_ut_run.pks b/test/ut3_user/api/test_ut_run.pks index 82b7eee95..4616763d0 100644 --- a/test/ut3_user/api/test_ut_run.pks +++ b/test/ut3_user/api/test_ut_run.pks @@ -163,6 +163,61 @@ create or replace package test_ut_run is --%endcontext + --%context(run with tags) + --%displayname(Call ut.run with #tags) + --%beforeall(create_ut3$user#_tests) + --%afterall(drop_ut3$user#_tests) + + --%test(Execute test by tag ut_run) + procedure test_run_by_one_tag; + + --%test( Execute suite by one tag) + procedure suite_run_by_one_tag; + + --%test(Execute two tests by one tag) + procedure two_test_run_by_one_tag; + + --%test(Execute all suites tests with tag) + procedure all_suites_run_by_one_tag; + + --%test(Execute tests by passing two tags) + procedure two_test_run_by_two_tags; + + --%test(Execute suite and all of its children) + procedure suite_with_children_tag; + + --%test(Execute suite and parents) + procedure suite_with_tag_parent; + + --%test(Execute test for non existing tag) + procedure test_nonexists_tag; + + --%test(Execute test for duplicate list tags) + procedure test_duplicate_tag; + + --%test(Execute suite test for duplicate list tags) + procedure suite_duplicate_tag; + + --%test(Runs given package only with package name given as path and filter by tag) + procedure run_proc_pkg_name_tag; + + --%test(Runs all from given package with package name given as path and coverage file list with tag) + procedure run_pkg_name_file_list_tag; + + --%test(Runs tests from given paths with paths list and tag) + procedure run_proc_path_list_tag; + + --%test(Runs all tests in current schema with default reporter when only tag is given) + procedure tag_run_func_no_params; + + --%test(Runs given package only with package name given as path and filter by tag) + procedure tag_run_func_pkg_name; + + --%test(Runs tests from given paths with paths list and a tag) + procedure tag_run_func_path_list; + + --%endcontext + end; / diff --git a/test/ut3_user/api/test_ut_runner.pkb b/test/ut3_user/api/test_ut_runner.pkb index 298c15c2a..3ff0a4850 100644 --- a/test/ut3_user/api/test_ut_runner.pkb +++ b/test/ut3_user/api/test_ut_runner.pkb @@ -5,6 +5,16 @@ create or replace package body test_ut_runner is ut3_tester_helper.run_helper.setup_cache_objects(); end; + procedure setup_cache_objectstag is + begin + ut3_tester_helper.run_helper.setup_cache_objectstag(); + end; + + procedure setup_cache_twotags is + begin + ut3_tester_helper.run_helper.setup_cache_twotags(); + end; + procedure setup_cache is begin ut3_tester_helper.run_helper.setup_cache(); @@ -238,7 +248,7 @@ end;'; ut.expect(l_actual).to_equal(0); end; - procedure test_get_suites_info is + procedure test_get_suites_info_notag is l_expected sys_refcursor; l_actual sys_refcursor; begin @@ -247,12 +257,56 @@ end;'; select 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'DUMMY_TEST_PACKAGE' item_name, 'dummy_test_suite' item_description, 'UT_SUITE' item_type, 2 item_line_no, - 'dummy_test_package' path, 0 disabled_flag + 'dummy_test_package' path, 0 disabled_flag,null tags from dual union all select 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'SOME_DUMMY_TEST_PROCEDURE' item_name, 'dummy_test' item_description, 'UT_TEST' item_type, 5 item_line_no, - 'dummy_test_package.some_dummy_test_procedure' path, 0 disabled_flag + 'dummy_test_package.some_dummy_test_procedure' path, 0 disabled_flag,null tags + from dual; + --Act + open l_actual for select * from table(ut3.ut_runner.get_suites_info('UT3$USER#','DUMMY_TEST_PACKAGE')); + --Assert + ut.expect(l_actual).to_equal(l_expected); + end; + + procedure test_get_suites_info_tag is + l_expected sys_refcursor; + l_actual sys_refcursor; + begin + --Arrange + open l_expected for + select + 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'DUMMY_TEST_PACKAGE' item_name, + 'dummy_test_suite' item_description, 'UT_SUITE' item_type, 2 item_line_no, + 'dummy_test_package' path, 0 disabled_flag,'dummy' tags + from dual union all + select + 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'SOME_DUMMY_TEST_PROCEDURE' item_name, + 'dummy_test' item_description, 'UT_TEST' item_type, 6 item_line_no, + 'dummy_test_package.some_dummy_test_procedure' path, 0 disabled_flag,'testtag' tags + from dual; + --Act + open l_actual for select * from table(ut3.ut_runner.get_suites_info('UT3$USER#','DUMMY_TEST_PACKAGE')); + --Assert + ut.expect(l_actual).to_equal(l_expected); + end; + + procedure test_get_suites_info_twotag is + l_expected sys_refcursor; + l_actual sys_refcursor; + begin + --Arrange + open l_expected for + select + 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'DUMMY_TEST_PACKAGE' item_name, + 'dummy_test_suite' item_description, 'UT_SUITE' item_type, 2 item_line_no, + 'dummy_test_package' path, 0 disabled_flag,'suitetag1,suitetag2' tags + from dual union all + select + 'UT3$USER#' object_owner, 'DUMMY_TEST_PACKAGE' object_name, 'SOME_DUMMY_TEST_PROCEDURE' item_name, + 'dummy_test' item_description, 'UT_TEST' item_type, 6 item_line_no, + 'dummy_test_package.some_dummy_test_procedure' path, 0 disabled_flag,'testtag1,testtag2' tags from dual; --Act open l_actual for select * from table(ut3.ut_runner.get_suites_info('UT3$USER#','DUMMY_TEST_PACKAGE')); diff --git a/test/ut3_user/api/test_ut_runner.pks b/test/ut3_user/api/test_ut_runner.pks index 70eed747f..967009c61 100644 --- a/test/ut3_user/api/test_ut_runner.pks +++ b/test/ut3_user/api/test_ut_runner.pks @@ -41,6 +41,8 @@ create or replace package test_ut_runner is procedure test_purge_cache_schema_type; procedure setup_cache_objects; + procedure setup_cache_objectstag; + procedure setup_cache_twotags; --%test(Rebuilds cache for a given schema and object type) --%beforetest(setup_cache_objects) @@ -50,7 +52,17 @@ create or replace package test_ut_runner is --%test(get_suites_info returns a cursor containing records for a newly created test) --%beforetest(setup_cache_objects) --%aftertest(cleanup_cache) - procedure test_get_suites_info; + procedure test_get_suites_info_notag; + + --%test(get_suites_info returns a cursor containing records for a newly created test with tag) + --%beforetest(setup_cache_objectstag) + --%aftertest(cleanup_cache) + procedure test_get_suites_info_tag; + + --%test(get_suites_info returns a cursor containing records for a newly created test with two tags) + --%beforetest(setup_cache_twotags) + --%aftertest(cleanup_cache) + procedure test_get_suites_info_twotag; --%test(get_reporters_list returns a cursor containing all built-in reporters and information about output-reporter) --%beforetest(setup_cache_objects)