From 36a061c687c0a00038e274365888b063e1cbe3d1 Mon Sep 17 00:00:00 2001 From: Jacek Date: Fri, 6 Oct 2017 12:46:46 +0100 Subject: [PATCH 01/19] Removed parsing of annotation text (params) from ut_annotations as annotation text can be different depending on use case and it's not parsers responsibility to support all use-cases for annotation text. Added standalone/separated function `parse_annotation_params` that takes care of annotation text parsing. --- source/core/ut_annotations.pkb | 191 ++++++------ source/core/ut_annotations.pks | 10 +- test/ut_annotations/test_annotations.pkb | 365 +++++++++-------------- 3 files changed, 240 insertions(+), 326 deletions(-) diff --git a/source/core/ut_annotations.pkb b/source/core/ut_annotations.pkb index 7ee7906a6..f87e9fc87 100644 --- a/source/core/ut_annotations.pkb +++ b/source/core/ut_annotations.pkb @@ -24,7 +24,6 @@ create or replace package body ut_annotations as gc_annotation_qualifier constant varchar2(1) := '%'; c_multiline_comment_pattern constant varchar2(50) := '/\*.*?\*/'; c_annot_comment_pattern constant varchar2(30) := '^( |'||chr(09)||')*-- *('||gc_annotation_qualifier||'.*?)$'; -- chr(09) is a tab character - --c_nonannotat_comment_pattern constant varchar2(30) := '^( |'||chr(09)||')*--+ *[^'||gc_annotation_qualifier||']*?$'; c_comment_replacer_patter constant varchar2(50) := '{COMMENT#%N%}'; c_comment_replacer_regex_ptrn constant varchar2(25) := '{COMMENT#(\d+)}'; c_regexp_identifier constant varchar2(50) := '[a-z][a-z0-9#_$]*'; @@ -45,9 +44,8 @@ create or replace package body ut_annotations as l_comment_index pls_integer; l_comment varchar2(32767); l_annotation_str varchar2(32767); - l_annotation_params_str varchar2(32767); + l_annotation_text varchar2(32767); l_annotation_name varchar2(1000); - l_annotation_params tt_annotation_params; l_annotation t_annotation; l_annotations_list tt_annotations; begin @@ -71,40 +69,14 @@ create or replace package body ut_annotations as l_annotation_str := regexp_substr(l_comment, c_annotation_pattern, 1, 1, modifier => 'i'); if l_annotation_str is not null then - l_annotation_params.delete; - -- get the annotation name and it's parameters if present - l_annotation_name := lower(regexp_substr(l_annotation_str - ,'%(' || c_regexp_identifier || ')' - ,modifier => 'i' - ,subexpression => 1)); - l_annotation_params_str := trim(regexp_substr(l_annotation_str, '\((.*?)\)\s*$', subexpression => 1)); - - if l_annotation_params_str is not null then - - -- parse the annotation parameters and store them as key-value pair array - for param_ind in 1 .. regexp_count(l_annotation_params_str, '(.+?)(,|$)') loop - declare - l_param_str varchar2(32767); - l_param_item typ_annotation_param; - begin - l_param_str := regexp_substr(srcstr => l_annotation_params_str - ,pattern => '(.+?)(,|$)' - ,occurrence => param_ind - ,subexpression => 1); - - l_param_item.key := regexp_substr(srcstr => l_param_str - ,pattern => '(' || c_regexp_identifier || ')\s*=' - ,modifier => 'i' - ,subexpression => 1); - l_param_item.val := trim(regexp_substr(l_param_str, '(.+?=)?(.*$)', subexpression => 2)); - - l_annotation_params(l_annotation_params.count + 1) := l_param_item; - end; - end loop; - end if; - l_annotation.text := l_annotation_params_str; - l_annotation.params := l_annotation_params; + l_annotation_name := lower(regexp_substr(l_annotation_str + ,'%(' || c_regexp_identifier || ')' + ,modifier => 'i' + ,subexpression => 1)); + l_annotation_text := trim(regexp_substr(l_annotation_str, '\((.*?)\)\s*$', subexpression => 1)); + + l_annotation.text := l_annotation_text; l_annotations_list(l_annotation_name) := l_annotation; end if; l_loop_index := l_loop_index + 1; @@ -185,6 +157,7 @@ create or replace package body ut_annotations as l_comments tt_comment_list; l_comment_pos pls_integer; l_comment_replacer varchar2(50); + l_source clob := a_source; begin l_comment_pos := 1; loop @@ -209,72 +182,74 @@ create or replace package body ut_annotations as l_comment_replacer := replace(c_comment_replacer_patter, '%N%', l_comments.count); - a_source := regexp_replace(srcstr => a_source + l_source := regexp_replace(srcstr => a_source ,pattern => c_annot_comment_pattern ,replacestr => l_comment_replacer ,position => l_comment_pos ,occurrence => 1 ,modifier => 'm'); + dbms_lob.freetemporary(a_source); + a_source := l_source; + dbms_lob.freetemporary(l_source); l_comment_pos := l_comment_pos + length(l_comment_replacer); end loop; ut_utils.debug_log(a_source); - return l_comments; end extract_and_replace_comments; - $if $$ut_trace $then - procedure print_parse_results(a_annotated_pkg typ_annotated_package) is - l_name t_annotation_name := a_annotated_pkg.package_annotations.first; - l_proc_name t_annotation_name; - begin - dbms_output.put_line('Annotations count: ' || a_annotated_pkg.package_annotations.count); - - while l_name is not null loop - dbms_output.put_line(' @' || l_name); - if a_annotated_pkg.package_annotations(l_name).count > 0 then - dbms_output.put_line(' Parameters:'); - - for j in 1 .. a_annotated_pkg.package_annotations(l_name).count loop - dbms_output.put_line(' ' || nvl(a_annotated_pkg.package_annotations(l_name)(j).key, '') || ' = ' || - nvl(a_annotated_pkg.package_annotations(l_name)(j).val, 'NULL')); - end loop; - else - dbms_output.put_line(' No parameters.'); - end if; - - l_name := a_annotated_pkg.package_annotations.next(l_name); - - end loop; - - dbms_output.put_line('Procedures count: ' || a_annotated_pkg.procedure_annotations.count); - - for i in 1 .. a_annotated_pkg.procedure_annotations.count loop - l_proc_name := a_annotated_pkg.procedure_annotations(i).name; - dbms_output.put_line(rpad('-', 80, '-')); - dbms_output.put_line(' Procedure: ' || l_proc_name); - dbms_output.put_line(' Annotations count: ' || a_annotated_pkg.procedure_annotations(i).annotations.count); - l_name := a_annotated_pkg.procedure_annotations(i).annotations.first; - while l_name is not null loop - dbms_output.put_line(' @' || l_name); - if a_annotated_pkg.procedure_annotations(i).annotations(l_name).count > 0 then - dbms_output.put_line(' Parameters:'); - for j in 1 .. a_annotated_pkg.procedure_annotations(i).annotations(l_name).count loop - dbms_output.put_line(' ' || - nvl(a_annotated_pkg.procedure_annotations(i).annotations(l_name)(j).key, '') || - ' = ' || nvl(a_annotated_pkg.procedure_annotations(i).annotations(l_name)(j).val, 'NULL')); - end loop; - else - dbms_output.put_line(' No parameters.'); - end if; - - l_name := a_annotated_pkg.procedure_annotations(i).annotations.next(l_name); - end loop; - end loop; - - end print_parse_results; - $end +-- $if $$ut_trace $then +-- procedure print_parse_results(a_annotated_pkg typ_annotated_package) is +-- l_name t_annotation_name := a_annotated_pkg.package_annotations.first; +-- l_proc_name t_annotation_name; +-- begin +-- dbms_output.put_line('Annotations count: ' || a_annotated_pkg.package_annotations.count); +-- +-- while l_name is not null loop +-- dbms_output.put_line(' @' || l_name); +-- if a_annotated_pkg.package_annotations(l_name).count > 0 then +-- dbms_output.put_line(' Parameters:'); +-- +-- for j in 1 .. a_annotated_pkg.package_annotations(l_name).count loop +-- dbms_output.put_line(' ' || nvl(a_annotated_pkg.package_annotations(l_name)(j).key, '') || ' = ' || +-- nvl(a_annotated_pkg.package_annotations(l_name)(j).val, 'NULL')); +-- end loop; +-- else +-- dbms_output.put_line(' No parameters.'); +-- end if; +-- +-- l_name := a_annotated_pkg.package_annotations.next(l_name); +-- +-- end loop; +-- +-- dbms_output.put_line('Procedures count: ' || a_annotated_pkg.procedure_annotations.count); +-- +-- for i in 1 .. a_annotated_pkg.procedure_annotations.count loop +-- l_proc_name := a_annotated_pkg.procedure_annotations(i).name; +-- dbms_output.put_line(rpad('-', 80, '-')); +-- dbms_output.put_line(' Procedure: ' || l_proc_name); +-- dbms_output.put_line(' Annotations count: ' || a_annotated_pkg.procedure_annotations(i).annotations.count); +-- l_name := a_annotated_pkg.procedure_annotations(i).annotations.first; +-- while l_name is not null loop +-- dbms_output.put_line(' @' || l_name); +-- if a_annotated_pkg.procedure_annotations(i).annotations(l_name).count > 0 then +-- dbms_output.put_line(' Parameters:'); +-- for j in 1 .. a_annotated_pkg.procedure_annotations(i).annotations(l_name).count loop +-- dbms_output.put_line(' ' || +-- nvl(a_annotated_pkg.procedure_annotations(i).annotations(l_name)(j).key, '') || +-- ' = ' || nvl(a_annotated_pkg.procedure_annotations(i).annotations(l_name)(j).val, 'NULL')); +-- end loop; +-- else +-- dbms_output.put_line(' No parameters.'); +-- end if; +-- +-- l_name := a_annotated_pkg.procedure_annotations(i).annotations.next(l_name); +-- end loop; +-- end loop; +-- +-- end print_parse_results; +-- $end function parse_package_annotations(a_source clob) return typ_annotated_package is l_source clob := a_source; @@ -285,18 +260,19 @@ create or replace package body ut_annotations as l_source := delete_multiline_comments(l_source); -- replace all single line comments with {COMMENT#12} element and store it's content for easier processing - -- this call modifies a_source + -- this call modifies l_source l_comments := extract_and_replace_comments(l_source); l_annotated_pkg.package_annotations := get_package_annotations(l_source, l_comments); l_annotated_pkg.procedure_annotations := get_procedure_list(l_source, l_comments); - -- printing out parsed structure for debugging - $if $$ut_trace $then - print_parse_results(l_annotated_pkg); - $end +-- -- printing out parsed structure for debugging +-- $if $$ut_trace $then +-- print_parse_results(l_annotated_pkg); +-- $end + dbms_lob.freetemporary(l_source); return l_annotated_pkg; end parse_package_annotations; @@ -335,5 +311,32 @@ create or replace package body ut_annotations as -- return l_result; -- end get_annotation_param; + -- parse the annotation parameters and return as key-value pair array + function parse_annotation_params(a_annotation_text varchar2) return tt_annotation_params is + l_annotation_params tt_annotation_params; + l_param_str varchar2(32767); + l_param_item typ_annotation_param; + l_param_item_empty typ_annotation_param; + c_annot_param_pattern constant varchar2(50) := '(.+?)(,|$)'; + begin + if a_annotation_text is not null then + for param_ind in 1 .. regexp_count(a_annotation_text, c_annot_param_pattern) loop + l_param_str := regexp_substr(srcstr => a_annotation_text + ,pattern => c_annot_param_pattern + ,occurrence => param_ind + ,subexpression => 1); + l_param_item := l_param_item_empty; + l_param_item.key := regexp_substr(srcstr => l_param_str + ,pattern => '(' || c_regexp_identifier || ')\s*=' + ,modifier => 'i' + ,subexpression => 1); + l_param_item.val := trim(regexp_substr(l_param_str, '(.+?=)?(.*$)', subexpression => 2)); + + l_annotation_params(l_annotation_params.count + 1) := l_param_item; + end loop; + end if; + return l_annotation_params; + end; + end ut_annotations; / diff --git a/source/core/ut_annotations.pks b/source/core/ut_annotations.pks index 02f0ea646..b64f9bea2 100644 --- a/source/core/ut_annotations.pks +++ b/source/core/ut_annotations.pks @@ -47,8 +47,7 @@ create or replace package ut_annotations authid current_user as type tt_annotation_params is table of typ_annotation_param index by pls_integer; type t_annotation is record( - text varchar2(4000), - params tt_annotation_params + text varchar2(4000) ); /* @@ -94,5 +93,12 @@ create or replace package ut_annotations authid current_user as */ -- function get_annotation_param(a_param_list tt_annotation_params, a_def_index pls_integer) return varchar2; + /* + function: parse_annotation_params + + parses annotation parameters from annotation text string + */ + function parse_annotation_params(a_annotation_text varchar2) return tt_annotation_params; + end ut_annotations; / diff --git a/test/ut_annotations/test_annotations.pkb b/test/ut_annotations/test_annotations.pkb index fc5db3254..9fa6d2da7 100644 --- a/test/ut_annotations/test_annotations.pkb +++ b/test/ut_annotations/test_annotations.pkb @@ -1,52 +1,32 @@ create or replace package body test_annotations is procedure check_annotation_parsing(a_expected ut3.ut_annotations.typ_annotated_package, a_parsing_result ut3.ut_annotations.typ_annotated_package) is - - procedure check_annotation_params(a_msg varchar2, a_expected ut3.ut_annotations.tt_annotation_params, a_actual ut3.ut_annotations.tt_annotation_params) is - begin - ut.expect(a_actual.count, '[' || a_msg || ']Check number of annotation params').to_equal(a_expected.count); - - if a_expected.count = a_actual.count and a_expected.count > 0 then - for i in 1 .. a_expected.count loop - if a_expected(i).key is not null then - ut.expect(a_actual(i).key, '[' || a_msg || '(' || i || ')]Check annotation param key').to_equal(a_expected(i).key); - else - ut.expect(a_actual(i).key, '[' || a_msg || '(' || i || ')]Check annotation param key').to_be_null; - end if; - - if a_expected(i).val is not null then - ut.expect(a_actual(i).val, '[' || a_msg || '(' || i || ')]Check annotation param value').to_equal(a_expected(i).val); - else - ut.expect(a_actual(i).val, '[' || a_msg || '(' || i || ')]Check annotation param value').to_be_null; - end if; - end loop; - end if; - end; - + procedure check_annotations(a_msg varchar2, a_expected ut3.ut_annotations.tt_annotations, a_actual ut3.ut_annotations.tt_annotations) is l_ind varchar2(500); begin ut.expect(a_actual.count, '[' || a_msg || ']Check number of annotations parsed').to_equal(a_expected.count); - + if a_expected.count = a_actual.count and a_expected.count > 0 then l_ind := a_expected.first; while l_ind is not null loop - + ut.expect(a_actual.exists(l_ind), ('[' || a_msg || ']Check annotation exists')).to_be_true; if a_actual.exists(l_ind) then - check_annotation_params(a_msg || '.' || l_ind, a_expected(l_ind).params, a_actual(l_ind).params); + ut.expect(a_actual(l_ind).text, ('[' || a_msg || ']Check annotation text')).to_equal(a_expected(l_ind).text); +-- check_annotation_params(a_msg || '.' || l_ind, a_expected(l_ind).params, a_actual(l_ind).params); end if; l_ind := a_expected.next(l_ind); end loop; end if; end; - + procedure check_procedures(a_msg varchar2, a_expected ut3.ut_annotations.tt_procedure_list, a_actual ut3.ut_annotations.tt_procedure_list) is l_found boolean := false; l_index pls_integer; begin ut.expect(a_actual.count, '[' || a_msg || ']Check number of procedures parsed').to_equal(a_expected.count); - + if a_expected.count = a_actual.count and a_expected.count > 0 then for i in 1 .. a_expected.count loop l_found := false; @@ -58,7 +38,7 @@ create or replace package body test_annotations is exit; end if; end loop; - + ut.expect(l_found, '[' || a_msg || ']Check procedure exists').to_be_true; if l_found then check_annotations(a_msg || '.' || a_expected(i).name @@ -78,42 +58,33 @@ create or replace package body test_annotations is l_parsing_result ut3.ut_annotations.typ_annotated_package; l_expected ut3.ut_annotations.typ_annotated_package; l_ann_param ut3.ut_annotations.typ_annotation_param; - + begin l_source := 'PACKAGE test_tt AS -- %suite -- %displayname(Name of suite) -- %suitepath(all.globaltests) - + -- %ann1(Name of suite) -- wrong line - -- %ann2(some_value) + -- %ann2(some_value) procedure foo; END;'; - + --Act l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); - + --Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast(null as ut3.ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - l_expected.package_annotations('displayname').text := l_ann_param.val; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - l_expected.package_annotations('suitepath').text := l_ann_param.val; - - l_ann_param := null; - l_ann_param.val := 'some_value'; + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('displayname').text := 'Name of suite'; + + l_expected.package_annotations('suitepath').text := 'all.globaltests'; + + l_expected.procedure_annotations(1).annotations('ann2').text := 'some_value'; l_expected.procedure_annotations(1).name := 'foo'; - l_expected.procedure_annotations(1).annotations('ann2').params(1) := l_ann_param; - l_expected.procedure_annotations(1).annotations('ann2').text := l_ann_param.val; - + check_annotation_parsing(l_expected, l_parsing_result); - + end; procedure test2 is @@ -121,34 +92,30 @@ create or replace package body test_annotations is l_parsing_result ut3.ut_annotations.typ_annotated_package; l_expected ut3.ut_annotations.typ_annotated_package; l_ann_param ut3.ut_annotations.typ_annotation_param; - + begin l_source := 'PACKAGE test_tt AS -- %suite -- %displayname(Name of suite) -- %suitepath(all.globaltests) - + -- %ann1(Name of suite) - -- %ann2(all.globaltests) - + -- %ann2(all.globaltests) + procedure foo; END;'; - + --Act l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); - + --Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast(null as ut3.ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('displayname').text := 'Name of suite'; + + l_expected.package_annotations('suitepath').text := 'all.globaltests'; + check_annotation_parsing(l_expected, l_parsing_result); - + end; procedure test3 is @@ -156,63 +123,54 @@ create or replace package body test_annotations is l_parsing_result ut3.ut_annotations.typ_annotated_package; l_expected ut3.ut_annotations.typ_annotated_package; l_ann_param ut3.ut_annotations.typ_annotation_param; - + begin l_source := 'PACKAGE test_tt AS -- %suite -- %displayname(Name of suite) -- %suitepath(all.globaltests) - + --%test procedure foo; - - + + --%beforeeach procedure foo2; - + --test comment -- wrong comment - - + + /* describtion of the procedure */ --%beforeeach(key=testval) PROCEDURE foo3(a_value number default null); - + function foo4(a_val number default null , a_par varchar2 default := ''asdf''); END;'; - + --Act l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); - + --Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast(null as ut3.ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('displayname').text := 'Name of suite'; + + l_expected.package_annotations('suitepath').text := 'all.globaltests'; + l_expected.procedure_annotations(1).name := 'foo'; - l_expected.procedure_annotations(1).annotations('test').params := cast(null as ut3.ut_annotations.tt_annotation_params); - + l_expected.procedure_annotations(1).annotations('test').text := null; + l_expected.procedure_annotations(2).name := 'foo2'; - l_expected.procedure_annotations(2).annotations('beforeeach').params := cast(null as - ut3.ut_annotations.tt_annotation_params); - - l_ann_param := null; - l_ann_param.key := 'key'; - l_ann_param.val := 'testval'; - + l_expected.procedure_annotations(2).annotations('beforeeach').text := null; + l_expected.procedure_annotations(3).name := 'foo3'; - l_expected.procedure_annotations(3).annotations('beforeeach').params(1) := l_ann_param; - + l_expected.procedure_annotations(3).annotations('beforeeach').text := 'key=testval'; + check_annotation_parsing(l_expected, l_parsing_result); - + end; procedure test4 is @@ -220,35 +178,30 @@ create or replace package body test_annotations is l_parsing_result ut3.ut_annotations.typ_annotated_package; l_expected ut3.ut_annotations.typ_annotated_package; l_ann_param ut3.ut_annotations.typ_annotation_param; - + begin l_source := 'PACKAGE test_tt AS -- %suite -- %displayname(Name of suite) -- %suitepath(all.globaltests) - + --%test procedure foo; END;'; - + --Act l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); - + --Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast(null as ut3.ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('displayname').text := 'Name of suite'; + l_expected.package_annotations('suitepath').text := 'all.globaltests'; + l_expected.procedure_annotations(1).name := 'foo'; - l_expected.procedure_annotations(1).annotations('test').params := cast(null as ut3.ut_annotations.tt_annotation_params); - + l_expected.procedure_annotations(1).annotations('test').text := null; + check_annotation_parsing(l_expected, l_parsing_result); - + end; procedure test5 is @@ -256,31 +209,26 @@ create or replace package body test_annotations is l_parsing_result ut3.ut_annotations.typ_annotated_package; l_expected ut3.ut_annotations.typ_annotated_package; l_ann_param ut3.ut_annotations.typ_annotation_param; - + begin l_source := 'PACKAGE test_tt AS -- %suite -- %displayname(Name of suite) -- %suitepath(all.globaltests) - + procedure foo; END;'; - + --Act l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); - + --Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast(null as ut3.ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('displayname').text := 'Name of suite'; + l_expected.package_annotations('suitepath').text := 'all.globaltests'; + check_annotation_parsing(l_expected, l_parsing_result); - + end; procedure test6 is @@ -288,31 +236,26 @@ create or replace package body test_annotations is l_parsing_result ut3.ut_annotations.typ_annotated_package; l_expected ut3.ut_annotations.typ_annotated_package; l_ann_param ut3.ut_annotations.typ_annotation_param; - + begin l_source := 'PACKAGE test_tt accessible by (foo) AS -- %suite -- %displayname(Name of suite) -- %suitepath(all.globaltests) - + procedure foo; END;'; - + --Act l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); - + --Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast(null as ut3.ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('displayname').text := 'Name of suite'; + l_expected.package_annotations('suitepath').text := 'all.globaltests'; + check_annotation_parsing(l_expected, l_parsing_result); - + end; procedure test7 is @@ -320,34 +263,29 @@ create or replace package body test_annotations is l_parsing_result ut3.ut_annotations.typ_annotated_package; l_expected ut3.ut_annotations.typ_annotated_package; l_ann_param ut3.ut_annotations.typ_annotation_param; - + begin - l_source := 'PACKAGE test_tt + l_source := 'PACKAGE test_tt ACCESSIBLE BY (calling_proc) - authid current_user + authid current_user AS -- %suite -- %displayname(Name of suite) -- %suitepath(all.globaltests) - + procedure foo; END;'; - + --Act l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); - + --Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast(null as ut3.ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('displayname').text := 'Name of suite'; + l_expected.package_annotations('suitepath').text := 'all.globaltests'; + check_annotation_parsing(l_expected, l_parsing_result); - + end; procedure test8 is @@ -355,38 +293,26 @@ create or replace package body test_annotations is l_parsing_result ut3.ut_annotations.typ_annotated_package; l_expected ut3.ut_annotations.typ_annotated_package; l_ann_param ut3.ut_annotations.typ_annotation_param; - + begin l_source := 'PACKAGE test_tt AS -- %suite --%displayname(name = Name of suite) -- %suitepath(key=all.globaltests,key2=foo) - + procedure foo; END;'; - + --Act l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); - + --Assert - l_ann_param := null; - l_ann_param.key := 'name'; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast(null as ut3.ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.key := 'key'; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.key := 'key2'; - l_ann_param.val := 'foo'; - l_expected.package_annotations('suitepath').params(2) := l_ann_param; - + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('displayname').text := 'name = Name of suite'; + l_expected.package_annotations('suitepath').text := 'key=all.globaltests,key2=foo'; + check_annotation_parsing(l_expected, l_parsing_result); - + end; procedure test9 is @@ -394,7 +320,7 @@ create or replace package body test_annotations is l_parsing_result ut3.ut_annotations.typ_annotated_package; l_expected ut3.ut_annotations.typ_annotated_package; l_ann_param ut3.ut_annotations.typ_annotation_param; - + begin l_source := 'PACKAGE test_tt AS /* @@ -404,36 +330,31 @@ create or replace package body test_annotations is -- %suite --%displayname(Name of suite) -- %suitepath(all.globaltests) - + procedure foo; END;'; - + --Act l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); - + --Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast(null as ut3.ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('displayname').text := 'Name of suite'; + l_expected.package_annotations('suitepath').text := 'all.globaltests'; + check_annotation_parsing(l_expected, l_parsing_result); - + end; procedure ignore_wrapped_package is l_pck_annotation ut3.ut_annotations.typ_annotated_package; pragma autonomous_transaction; begin - + l_pck_annotation := ut3.ut_annotations.get_package_annotations(user, 'TST_WRAPPED_PCK'); ut.expect(l_pck_annotation.procedure_annotations.count).to_equal(0); ut.expect(l_pck_annotation.package_annotations.count).to_equal(0); - + end; procedure cre_wrapped_pck is @@ -458,7 +379,7 @@ END; end; procedure brackets_in_desc is - + l_source clob; l_parsing_result ut3.ut_annotations.typ_annotated_package; l_expected ut3.ut_annotations.typ_annotated_package; @@ -468,17 +389,16 @@ END; l_source := 'PACKAGE test_tt AS -- %suite(Name of suite (including some brackets) and some more text) END;'; - + --Act l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); - + --Assert - l_ann_param.val := 'Name of suite (including some brackets) and some more text'; - l_expected.package_annotations('suite').params(1) := l_ann_param; - + l_expected.package_annotations('suite').text := 'Name of suite (including some brackets) and some more text'; + check_annotation_parsing(l_expected, l_parsing_result); end; - + procedure test_space_Before_Annot_Params is l_source clob; l_parsing_result ut3.ut_annotations.typ_annotated_package; @@ -501,11 +421,8 @@ END;'; l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').params := cast( null as ut3.ut_annotations.tt_annotation_params); - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('suitepath').text := 'all.globaltests'; check_annotation_parsing(l_expected, l_parsing_result); end; @@ -519,7 +436,7 @@ END;'; begin l_source := 'PACKAGE test_tt AS -- %suite - -- %displayname(Name of suite)' || chr(13) || chr(10) + -- %displayname(Name of suite)' || chr(13) || chr(10) || ' -- %suitepath(all.globaltests) END;'; @@ -527,15 +444,9 @@ END;'; l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); --Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut3.ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('displayname').text := 'Name of suite'; + l_expected.package_annotations('suitepath').text := 'all.globaltests'; check_annotation_parsing(l_expected, l_parsing_result); end; @@ -550,27 +461,21 @@ END;'; -- %suite -- %displayname(Name of suite) -- %suitepath(all.globaltests) - + --%test procedure very_long_procedure_name_valid_for_oracle_12_so_utplsql_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_ditit; END;'; - + --Act l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); - + --Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut3.ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - + l_expected.package_annotations('suite').text := null; + l_expected.package_annotations('displayname').text := 'Name of suite'; + l_expected.package_annotations('suitepath').text := 'all.globaltests'; l_expected.procedure_annotations(1).name := 'very_long_procedure_name_valid_for_oracle_12_so_utplsql_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_ditit'; - l_expected.procedure_annotations(1).annotations('test').params := cast( null as ut3.ut_annotations.tt_annotation_params); - + l_expected.procedure_annotations(1).annotations('test').text := null; + check_annotation_parsing(l_expected, l_parsing_result); end; From a29cabeaca53a0d2c9b023dfb5e50e48fb0b2c1e Mon Sep 17 00:00:00 2001 From: Jacek Date: Fri, 6 Oct 2017 13:59:42 +0100 Subject: [PATCH 02/19] Removed duplicate `old_tests` for ut_annotations - the functionality is covered with new tests. --- old_tests/RunAll.sql | 13 ---- ...WrappedPackageAndDoesNotRaiseException.sql | 22 ------ ...nnotationMixedWithWrongBeforeProcedure.sql | 47 ------------- ...ions.ParseAnnotationNotBeforeProcedure.sql | 42 ----------- ...ions.ParseAnnotationParamsWithBrackets.sql | 34 --------- ...ions.ParseAnnotationWithWindowsNewline.sql | 37 ---------- ...ackage_annotations.ParseComplexPackage.sql | 70 ------------------- ...rsePackageAndProcedureLevelAnnotations.sql | 43 ------------ ...nnotations.ParsePackageLevelAnnotation.sql | 39 ----------- ...arsePackageLevelAnnotationAccessibleBy.sql | 39 ----------- ...PackageLevelAnnotationMultilineDeclare.sql | 42 ----------- ...arsePackageLevelAnnotationWithKeyValue.sql | 46 ------------ ...ageLevelAnnotationWithMultilineComment.sql | 43 ------------ ...rseProcedureAnnotationWithVeryLongName.sql | 43 ------------ ...nnotations.spaceBeforeAnnotationParams.sql | 37 ---------- 15 files changed, 597 deletions(-) delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.IgnoreWrappedPackageAndDoesNotRaiseException.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationMixedWithWrongBeforeProcedure.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationNotBeforeProcedure.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationParamsWithBrackets.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationWithWindowsNewline.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseComplexPackage.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageAndProcedureLevelAnnotations.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotation.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationAccessibleBy.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationMultilineDeclare.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationWithKeyValue.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationWithMultilineComment.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseProcedureAnnotationWithVeryLongName.sql delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.spaceBeforeAnnotationParams.sql diff --git a/old_tests/RunAll.sql b/old_tests/RunAll.sql index 9c76c642a..d1c50ca81 100644 --- a/old_tests/RunAll.sql +++ b/old_tests/RunAll.sql @@ -38,19 +38,6 @@ create table ut$test_table (val varchar2(1)); --Regular coverage excludes the framework exec ut_coverage.coverage_start_develop(); -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.IgnoreWrappedPackageAndDoesNotRaiseException.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationMixedWithWrongBeforeProcedure.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationNotBeforeProcedure.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationParamsWithBrackets.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.ParsePackageAndProcedureLevelAnnotations.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotation.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationAccessibleBy.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationMultilineDeclare.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationWithKeyValue.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationWithMultilineComment.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.spaceBeforeAnnotationParams.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationWithWindowsNewline.sql -@@lib/RunTest.sql ut_annotations/ut_annotations.parse_package_annotations.ParseProcedureAnnotationWithVeryLongName.sql @@lib/RunTest.sql ut_expectation_processor/who_called_expectation.parseStackTrace.sql @@lib/RunTest.sql ut_expectation_processor/who_called_expectation.parseStackTraceWith0x.sql @@ut_expectations/ut.expect.not_to_be_null.sql diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.IgnoreWrappedPackageAndDoesNotRaiseException.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.IgnoreWrappedPackageAndDoesNotRaiseException.sql deleted file mode 100644 index e5f8778ef..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.IgnoreWrappedPackageAndDoesNotRaiseException.sql +++ /dev/null @@ -1,22 +0,0 @@ -begin - DBMS_DDL.CREATE_WRAPPED( -q'[ -CREATE OR REPLACE PACKAGE tst_wrapped_pck IS - PROCEDURE dummy; -END; -]' - ); -end; -/ - -declare - l_pck_annotation ut_annotations.typ_annotated_package; -begin - l_pck_annotation := ut_annotations.get_package_annotations(user, 'TST_WRAPPED_PCK'); - if l_pck_annotation.procedure_annotations.count = 0 and l_pck_annotation.package_annotations.count = 0 then - :test_result := ut_utils.tr_success; - end if; -end; -/ - -drop package tst_wrapped_pck; diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationMixedWithWrongBeforeProcedure.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationMixedWithWrongBeforeProcedure.sql deleted file mode 100644 index 26dd340f9..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationMixedWithWrongBeforeProcedure.sql +++ /dev/null @@ -1,47 +0,0 @@ -PROMPT Parse procedure level annotations with annotations mixed with comments - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - -- %ann1(Name of suite) - -- wrong line - -- %ann2(some_value) - procedure foo; -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'some_value'; - l_expected.procedure_annotations(1).name :='foo'; - l_expected.procedure_annotations(1).annotations('ann2').params(1) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationNotBeforeProcedure.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationNotBeforeProcedure.sql deleted file mode 100644 index b7315726a..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationNotBeforeProcedure.sql +++ /dev/null @@ -1,42 +0,0 @@ -PROMPT Parse package level annotations with annotations "in the air" - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - -- %ann1(Name of suite) - -- %ann2(all.globaltests) - - procedure foo; -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationParamsWithBrackets.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationParamsWithBrackets.sql deleted file mode 100644 index 4a8f5cbef..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationParamsWithBrackets.sql +++ /dev/null @@ -1,34 +0,0 @@ -PROMPT Parse package level annotations with annotation params containing brackets - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param := null; - l_results ut_expectation_results; -begin - l_source := 'PACKAGE test_tt AS - -- %suite(Name of suite (including some brackets) and some more text) -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param.val := 'Name of suite (including some brackets) and some more text'; - l_expected.package_annotations('suite').params(1) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - else - l_results := ut_expectation_processor.get_expectations_results(); - for i in 1 .. l_results.count loop - dbms_output.put_line(l_results(i).message); - end loop; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationWithWindowsNewline.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationWithWindowsNewline.sql deleted file mode 100644 index b530b5d81..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationWithWindowsNewline.sql +++ /dev/null @@ -1,37 +0,0 @@ -PROMPT Parse package level annotations with windows newline - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite)' || chr(13) || chr(10) -|| ' -- %suitepath(all.globaltests) -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseComplexPackage.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseComplexPackage.sql deleted file mode 100644 index 2854e5337..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseComplexPackage.sql +++ /dev/null @@ -1,70 +0,0 @@ -PROMPT Parse complex package - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - --%test - procedure foo; - - - --%beforeeach - procedure foo2; - - --test comment - -- wrong comment - - - /* - describtion of the procedure - */ - --%beforeeach(key=testval) - PROCEDURE foo3(a_value number default null); - - function foo4(a_val number default null - , a_par varchar2 default := ''asdf''); -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - l_expected.procedure_annotations(1).name := 'foo'; - l_expected.procedure_annotations(1).annotations('test').params := cast( null as ut_annotations.tt_annotation_params); - - l_expected.procedure_annotations(2).name := 'foo2'; - l_expected.procedure_annotations(2).annotations('beforeeach').params := cast( null as ut_annotations.tt_annotation_params); - - l_ann_param := null; - l_ann_param.key := 'key'; - l_ann_param.val := 'testval'; - - l_expected.procedure_annotations(3).name := 'foo3'; - l_expected.procedure_annotations(3).annotations('beforeeach')(1) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageAndProcedureLevelAnnotations.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageAndProcedureLevelAnnotations.sql deleted file mode 100644 index a418629e1..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageAndProcedureLevelAnnotations.sql +++ /dev/null @@ -1,43 +0,0 @@ -PROMPT Parse package level annotations - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - --%test - procedure foo; -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - l_expected.procedure_annotations(1).name := 'foo'; - l_expected.procedure_annotations(1).annotations('test').params := cast( null as ut_annotations.tt_annotation_params); - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotation.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotation.sql deleted file mode 100644 index 49d1c6e68..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotation.sql +++ /dev/null @@ -1,39 +0,0 @@ -PROMPT Parse package level annotations - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - procedure foo; -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationAccessibleBy.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationAccessibleBy.sql deleted file mode 100644 index 282a7d313..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationAccessibleBy.sql +++ /dev/null @@ -1,39 +0,0 @@ -PROMPT Parse package level annotations Accessible by - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt accessible by (foo) AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - procedure foo; -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationMultilineDeclare.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationMultilineDeclare.sql deleted file mode 100644 index 6b735a618..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationMultilineDeclare.sql +++ /dev/null @@ -1,42 +0,0 @@ -PROMPT Parse package level annotations with multiline declaration - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt - ACCESSIBLE BY (calling_proc) - authid current_user - AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - procedure foo; -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationWithKeyValue.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationWithKeyValue.sql deleted file mode 100644 index d87a504d7..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationWithKeyValue.sql +++ /dev/null @@ -1,46 +0,0 @@ -PROMPT Parse package level annotations - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt AS - -- %suite - --%displayname(name = Name of suite) - -- %suitepath(key=all.globaltests,key2=foo) - - procedure foo; -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param := null; - l_ann_param.key := 'name'; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.key := 'key'; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.key := 'key2'; - l_ann_param.val := 'foo'; - l_expected.package_annotations('suitepath').params(2) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationWithMultilineComment.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationWithMultilineComment.sql deleted file mode 100644 index 7173cac3f..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParsePackageLevelAnnotationWithMultilineComment.sql +++ /dev/null @@ -1,43 +0,0 @@ -PROMPT Parse package level annotations with multiline comment - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt AS - /* - Some comment - -- inlined - */ - -- %suite - --%displayname(Name of suite) - -- %suitepath(all.globaltests) - - procedure foo; -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseProcedureAnnotationWithVeryLongName.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseProcedureAnnotationWithVeryLongName.sql deleted file mode 100644 index dbbed5a29..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseProcedureAnnotationWithVeryLongName.sql +++ /dev/null @@ -1,43 +0,0 @@ -PROMPT Parse PROCEDURE Annotation with very long name - ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - --%test - procedure very_long_procedure_name_valid_for_oracle_12_so_utplsql_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_ditit; -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_ann_param := null; - l_ann_param.val := 'Name of suite'; - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - l_expected.package_annotations('displayname').params(1) := l_ann_param; - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - l_expected.procedure_annotations(1).name := 'very_long_procedure_name_valid_for_oracle_12_so_utplsql_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_ditit'; - l_expected.procedure_annotations(1).annotations('test').params := cast( null as ut_annotations.tt_annotation_params); - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.spaceBeforeAnnotationParams.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.spaceBeforeAnnotationParams.sql deleted file mode 100644 index 0b2c423bf..000000000 --- a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.spaceBeforeAnnotationParams.sql +++ /dev/null @@ -1,37 +0,0 @@ ---Arrange -declare - l_source clob; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - -begin - l_source := 'PACKAGE test_tt AS - /* - Some comment - -- inlined - */ - -- %suite - -- %suitepath (all.globaltests) - - procedure foo; -END;'; - ---Act - l_parsing_result := ut_annotations.parse_package_annotations(l_source); - ---Assert - l_expected.package_annotations('suite').params := cast( null as ut_annotations.tt_annotation_params); - - l_ann_param := null; - l_ann_param.val := 'all.globaltests'; - l_expected.package_annotations('suitepath').params(1) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - end if; - -end; -/ From 58c62935a70a202d5bad6265b700dc8bd875f289 Mon Sep 17 00:00:00 2001 From: Jacek Date: Sat, 7 Oct 2017 15:59:55 +0100 Subject: [PATCH 03/19] Renamed `ut_annotations` to `ut_annotation_parser` and moved to annotations directory. --- old_tests/RunAll.sql | 4 +- .../ut_annotation_parser.pkb} | 4 +- .../ut_annotation_parser.pks} | 6 +- source/core/ut_suite_manager.pkb | 10 +- source/install.sql | 4 +- source/uninstall.sql | 1 + test/install_tests.sql | 4 +- .../test_annotation_parser.pkb} | 118 +++++++++--------- .../test_annotation_parser.pks} | 6 +- 9 files changed, 79 insertions(+), 78 deletions(-) rename source/core/{ut_annotations.pkb => annotations/ut_annotation_parser.pkb} (99%) rename source/core/{ut_annotations.pks => annotations/ut_annotation_parser.pks} (95%) rename test/{ut_annotations/test_annotations.pkb => ut_annotation_parser/test_annotation_parser.pkb} (72%) rename test/{ut_annotations/test_annotations.pks => ut_annotation_parser/test_annotation_parser.pks} (94%) diff --git a/old_tests/RunAll.sql b/old_tests/RunAll.sql index d1c50ca81..45ee93793 100644 --- a/old_tests/RunAll.sql +++ b/old_tests/RunAll.sql @@ -328,8 +328,8 @@ begin 'source/api/ut_runner.pks', 'source/core/coverage', 'source/core/types', - 'source/core/ut_annotations.pkb', - 'source/core/ut_annotations.pks', + 'source/core/ut_annotation_parser.pkb', + 'source/core/ut_annotation_parser.pks', 'source/core/ut_expectation_processor.pkb', 'source/core/ut_expectation_processor.pks', 'source/core/ut_file_mapper.pkb', diff --git a/source/core/ut_annotations.pkb b/source/core/annotations/ut_annotation_parser.pkb similarity index 99% rename from source/core/ut_annotations.pkb rename to source/core/annotations/ut_annotation_parser.pkb index f87e9fc87..0f2aa7a4f 100644 --- a/source/core/ut_annotations.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -1,4 +1,4 @@ -create or replace package body ut_annotations as +create or replace package body ut_annotation_parser as /* utPLSQL - Version X.X.X.X Copyright 2016 - 2017 utPLSQL Project @@ -338,5 +338,5 @@ create or replace package body ut_annotations as return l_annotation_params; end; -end ut_annotations; +end ut_annotation_parser; / diff --git a/source/core/ut_annotations.pks b/source/core/annotations/ut_annotation_parser.pks similarity index 95% rename from source/core/ut_annotations.pks rename to source/core/annotations/ut_annotation_parser.pks index b64f9bea2..1cf04ac2a 100644 --- a/source/core/ut_annotations.pks +++ b/source/core/annotations/ut_annotation_parser.pks @@ -1,4 +1,4 @@ -create or replace package ut_annotations authid current_user as +create or replace package ut_annotation_parser authid current_user as /* utPLSQL - Version X.X.X.X Copyright 2016 - 2017 utPLSQL Project @@ -17,7 +17,7 @@ create or replace package ut_annotations authid current_user as */ /* - package: ut_annotations + package: ut_annotation_parser Responsible for parsing and accessing utplsql annotations. @@ -100,5 +100,5 @@ create or replace package ut_annotations authid current_user as */ function parse_annotation_params(a_annotation_text varchar2) return tt_annotation_params; -end ut_annotations; +end ut_annotation_parser; / diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index 605be5d61..20c56843b 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -46,10 +46,10 @@ create or replace package body ut_suite_manager is end; function config_package(a_owner_name varchar2, a_object_name varchar2) return ut_logical_suite is - l_annotation_data ut_annotations.typ_annotated_package; - l_suite_name ut_annotations.t_annotation_name; + l_annotation_data ut_annotation_parser.typ_annotated_package; + l_suite_name ut_annotation_parser.t_annotation_name; l_test ut_test; - l_proc_annotations ut_annotations.tt_annotations; + l_proc_annotations ut_annotation_parser.tt_annotations; l_default_setup_proc varchar2(250 char); l_default_teardown_proc varchar2(250 char); @@ -57,7 +57,7 @@ create or replace package body ut_suite_manager is l_suite_teardown_proc varchar2(250 char); l_suite_path varchar2(4000 char); - l_proc_name ut_annotations.t_procedure_name; + l_proc_name ut_annotation_parser.t_procedure_name; l_owner_name varchar2(250 char); l_object_name varchar2(250 char); @@ -76,7 +76,7 @@ create or replace package body ut_suite_manager is when e_insufficient_priv then return null; end; - l_annotation_data := ut_annotations.get_package_annotations(a_owner_name => l_owner_name, a_name => l_object_name); + l_annotation_data := ut_annotation_parser.get_package_annotations(a_owner_name => l_owner_name, a_name => l_object_name); if l_annotation_data.package_annotations.exists('suite') then diff --git a/source/install.sql b/source/install.sql index 71bc70eeb..563f4293f 100644 --- a/source/install.sql +++ b/source/install.sql @@ -75,8 +75,8 @@ alter session set plsql_warnings = 'ENABLE:ALL', 'DISABLE:(5004,5018,6000,6001,6 @@install_component.sql 'core/ut_output_buffer.pkb' --annoations -@@install_component.sql 'core/ut_annotations.pks' -@@install_component.sql 'core/ut_annotations.pkb' +@@install_component.sql 'core/annotations/ut_annotation_parser.pks' +@@install_component.sql 'core/annotations/ut_annotation_parser.pkb' --suite manager @@install_component.sql 'core/ut_suite_manager.pks' diff --git a/source/uninstall.sql b/source/uninstall.sql index 48aba7d04..4b38c5951 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -173,6 +173,7 @@ drop type ut_data_value force; drop table ut_cursor_data; drop package ut_annotations; +drop package ut_annotation_parser; drop package ut_file_mapper; diff --git a/test/install_tests.sql b/test/install_tests.sql index 6e03a6f61..4b5d4aa8a 100644 --- a/test/install_tests.sql +++ b/test/install_tests.sql @@ -3,7 +3,7 @@ whenever oserror exit failure rollback @core.pks @ut_utils/test_ut_utils.pks -@ut_annotations/test_annotations.pks +@ut_annotation_parser/test_annotation_parser.pks @ut_matchers/test_matchers.pks @ut_output_buffer/test_output_buffer.pks @ut_suite_manager/test_suite_manager.pks @@ -15,7 +15,7 @@ whenever oserror exit failure rollback @core.pkb @ut_utils/test_ut_utils.pkb -@ut_annotations/test_annotations.pkb +@ut_annotation_parser/test_annotation_parser.pkb @ut_matchers/test_matchers.pkb @ut_output_buffer/test_output_buffer.pkb @ut_suite_manager/test_suite_manager.pkb diff --git a/test/ut_annotations/test_annotations.pkb b/test/ut_annotation_parser/test_annotation_parser.pkb similarity index 72% rename from test/ut_annotations/test_annotations.pkb rename to test/ut_annotation_parser/test_annotation_parser.pkb index 9fa6d2da7..0a71ebccb 100644 --- a/test/ut_annotations/test_annotations.pkb +++ b/test/ut_annotation_parser/test_annotation_parser.pkb @@ -1,8 +1,8 @@ -create or replace package body test_annotations is +create or replace package body test_annotation_parser is - procedure check_annotation_parsing(a_expected ut3.ut_annotations.typ_annotated_package, a_parsing_result ut3.ut_annotations.typ_annotated_package) is + procedure check_annotation_parsing(a_expected ut3.ut_annotation_parser.typ_annotated_package, a_parsing_result ut3.ut_annotation_parser.typ_annotated_package) is - procedure check_annotations(a_msg varchar2, a_expected ut3.ut_annotations.tt_annotations, a_actual ut3.ut_annotations.tt_annotations) is + procedure check_annotations(a_msg varchar2, a_expected ut3.ut_annotation_parser.tt_annotations, a_actual ut3.ut_annotation_parser.tt_annotations) is l_ind varchar2(500); begin ut.expect(a_actual.count, '[' || a_msg || ']Check number of annotations parsed').to_equal(a_expected.count); @@ -21,7 +21,7 @@ create or replace package body test_annotations is end if; end; - procedure check_procedures(a_msg varchar2, a_expected ut3.ut_annotations.tt_procedure_list, a_actual ut3.ut_annotations.tt_procedure_list) is + procedure check_procedures(a_msg varchar2, a_expected ut3.ut_annotation_parser.tt_procedure_list, a_actual ut3.ut_annotation_parser.tt_procedure_list) is l_found boolean := false; l_index pls_integer; begin @@ -55,9 +55,9 @@ create or replace package body test_annotations is procedure test1 is l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS @@ -72,7 +72,7 @@ create or replace package body test_annotations is END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -89,9 +89,9 @@ create or replace package body test_annotations is procedure test2 is l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS @@ -106,7 +106,7 @@ create or replace package body test_annotations is END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -120,9 +120,9 @@ create or replace package body test_annotations is procedure test3 is l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS @@ -152,7 +152,7 @@ create or replace package body test_annotations is END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -175,9 +175,9 @@ create or replace package body test_annotations is procedure test4 is l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS @@ -190,7 +190,7 @@ create or replace package body test_annotations is END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -206,9 +206,9 @@ create or replace package body test_annotations is procedure test5 is l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS @@ -220,7 +220,7 @@ create or replace package body test_annotations is END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -233,9 +233,9 @@ create or replace package body test_annotations is procedure test6 is l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt accessible by (foo) AS @@ -247,7 +247,7 @@ create or replace package body test_annotations is END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -260,9 +260,9 @@ create or replace package body test_annotations is procedure test7 is l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt @@ -277,7 +277,7 @@ create or replace package body test_annotations is END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -290,9 +290,9 @@ create or replace package body test_annotations is procedure test8 is l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS @@ -304,7 +304,7 @@ create or replace package body test_annotations is END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -317,9 +317,9 @@ create or replace package body test_annotations is procedure test9 is l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS @@ -335,7 +335,7 @@ create or replace package body test_annotations is END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -347,11 +347,11 @@ create or replace package body test_annotations is end; procedure ignore_wrapped_package is - l_pck_annotation ut3.ut_annotations.typ_annotated_package; + l_pck_annotation ut3.ut_annotation_parser.typ_annotated_package; pragma autonomous_transaction; begin - l_pck_annotation := ut3.ut_annotations.get_package_annotations(user, 'TST_WRAPPED_PCK'); + l_pck_annotation := ut3.ut_annotation_parser.get_package_annotations(user, 'TST_WRAPPED_PCK'); ut.expect(l_pck_annotation.procedure_annotations.count).to_equal(0); ut.expect(l_pck_annotation.package_annotations.count).to_equal(0); @@ -381,9 +381,9 @@ END; procedure brackets_in_desc is l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param := null; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param := null; --l_results ut_expectation_results; begin l_source := 'PACKAGE test_tt AS @@ -391,7 +391,7 @@ END; END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := 'Name of suite (including some brackets) and some more text'; @@ -401,9 +401,9 @@ END;'; procedure test_space_Before_Annot_Params is l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS @@ -418,7 +418,7 @@ END;'; END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -430,9 +430,9 @@ END;'; procedure test_windows_newline as l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS -- %suite @@ -441,7 +441,7 @@ END;'; END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -453,9 +453,9 @@ END;'; procedure test_annot_very_long_name as l_source clob; - l_parsing_result ut3.ut_annotations.typ_annotated_package; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.ut_annotations.typ_annotation_param; + l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; + l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS -- %suite @@ -467,7 +467,7 @@ END;'; END;'; --Act - l_parsing_result := ut3.ut_annotations.parse_package_annotations(l_source); + l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert l_expected.package_annotations('suite').text := null; @@ -480,5 +480,5 @@ END;'; end; -end test_annotations; +end test_annotation_parser; / diff --git a/test/ut_annotations/test_annotations.pks b/test/ut_annotation_parser/test_annotation_parser.pks similarity index 94% rename from test/ut_annotations/test_annotations.pks rename to test/ut_annotation_parser/test_annotation_parser.pks index acfd442ac..95182fbc2 100644 --- a/test/ut_annotations/test_annotations.pks +++ b/test/ut_annotation_parser/test_annotation_parser.pks @@ -1,4 +1,4 @@ -create or replace package test_annotations is +create or replace package test_annotation_parser is --%suite(annotations) --%suitepath(utplsql.core) @@ -32,7 +32,7 @@ create or replace package test_annotations is --%test(Parse package level annotations with annotation params containing brackets) procedure brackets_in_desc; - + --%test(Test space before annotation params) procedure test_space_Before_Annot_Params; @@ -42,5 +42,5 @@ create or replace package test_annotations is -- %test(Test annotation function with very long name) procedure test_annot_very_long_name; -end test_annotations; +end test_annotation_parser; / From 375a7ffdf7c6f8b3d944e3a4dfa01d80b40d9626 Mon Sep 17 00:00:00 2001 From: Jacek Date: Sat, 7 Oct 2017 16:01:02 +0100 Subject: [PATCH 04/19] Removed ``ut_annotations` from uninstall script. --- source/core/annotations/ut_annotation.tps | 0 source/uninstall.sql | 1 - 2 files changed, 1 deletion(-) create mode 100644 source/core/annotations/ut_annotation.tps diff --git a/source/core/annotations/ut_annotation.tps b/source/core/annotations/ut_annotation.tps new file mode 100644 index 000000000..e69de29bb diff --git a/source/uninstall.sql b/source/uninstall.sql index 4b38c5951..b3d4c7048 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -172,7 +172,6 @@ drop type ut_data_value force; drop table ut_cursor_data; -drop package ut_annotations; drop package ut_annotation_parser; drop package ut_file_mapper; From 13703faa48456362cf890133e604f42e099441c6 Mon Sep 17 00:00:00 2001 From: Jacek Date: Sat, 7 Oct 2017 21:22:31 +0100 Subject: [PATCH 05/19] Fixed failing old tests after refactoring. --- old_tests/RunAll.sql | 6 +- .../helpers/check_annotation_parsing.prc | 70 ------------------- .../ut_test/ut_test.DisabledFlagSkipTest.sql | 4 -- .../ut_test/ut_test.Rollback_type.Auto.sql | 4 -- .../ut_test.Rollback_type.AutoOnFailure.sql | 4 -- .../ut_test/ut_test.Rollback_type.Manual.sql | 4 -- .../ut_test.Rollback_type.ManualOnFailure.sql | 4 -- .../ut_test_suite.Rollback_type.Auto.sql | 4 -- ...test_suite.Rollback_type.AutoOnFailure.sql | 4 -- .../ut_test_suite.Rollback_type.Manual.sql | 4 -- ...st_suite.Rollback_type.ManualOnFailure.sql | 4 -- 11 files changed, 2 insertions(+), 110 deletions(-) delete mode 100644 old_tests/helpers/check_annotation_parsing.prc diff --git a/old_tests/RunAll.sql b/old_tests/RunAll.sql index 45ee93793..22b806b0e 100644 --- a/old_tests/RunAll.sql +++ b/old_tests/RunAll.sql @@ -18,7 +18,6 @@ spool RunAll.log --Global setup @@helpers/ut_example_tests.pks @@helpers/ut_example_tests.pkb -@@helpers/check_annotation_parsing.prc --@@helpers/cre_tab_ut_test_table.sql create table ut$test_table (val varchar2(1)); @@helpers/ut_transaction_control.pck @@ -271,7 +270,6 @@ spool off --Global cleanup --removing objects that should not be part of coverage report drop package ut_example_tests; -drop procedure check_annotation_parsing; drop package ut_transaction_control; drop table ut$test_table; drop type department$; @@ -328,8 +326,8 @@ begin 'source/api/ut_runner.pks', 'source/core/coverage', 'source/core/types', - 'source/core/ut_annotation_parser.pkb', - 'source/core/ut_annotation_parser.pks', + 'source/core/annotations/ut_annotation_parser.pkb', + 'source/core/annotations/ut_annotation_parser.pks', 'source/core/ut_expectation_processor.pkb', 'source/core/ut_expectation_processor.pks', 'source/core/ut_file_mapper.pkb', diff --git a/old_tests/helpers/check_annotation_parsing.prc b/old_tests/helpers/check_annotation_parsing.prc deleted file mode 100644 index d47e02f67..000000000 --- a/old_tests/helpers/check_annotation_parsing.prc +++ /dev/null @@ -1,70 +0,0 @@ -create or replace procedure check_annotation_parsing(a_expected ut_annotations.typ_annotated_package, a_parsing_result ut_annotations.typ_annotated_package) is - procedure check_annotation_params(a_msg varchar2, a_expected ut_annotations.tt_annotation_params, a_actual ut_annotations.tt_annotation_params) is - begin - ut.expect(a_actual.count,'['||a_msg||']Check number of annotation params').to_equal(a_expected.count); - - if a_expected.count = a_actual.count and a_expected.count > 0 then - for i in 1..a_expected.count loop - if a_expected(i).key is not null then - ut.expect(a_actual(i).key,'['||a_msg||'('||i||')]Check annotation param key').to_equal(a_expected(i).key); - else - ut.expect(a_actual(i).key,'['||a_msg||'('||i||')]Check annotation param key').to_be_null; - end if; - - if a_expected(i).val is not null then - ut.expect(a_actual(i).val,'['||a_msg||'('||i||')]Check annotation param value').to_equal(a_expected(i).val); - else - ut.expect(a_actual(i).val,'['||a_msg||'('||i||')]Check annotation param value').to_be_null; - end if; - end loop; - end if; - end; - - procedure check_annotations(a_msg varchar2, a_expected ut_annotations.tt_annotations, a_actual ut_annotations.tt_annotations) is - l_ind varchar2(500); - begin - ut.expect(a_actual.count,'['||a_msg||']Check number of annotations parsed').to_equal(a_expected.count); - - if a_expected.count = a_actual.count and a_expected.count > 0 then - l_ind := a_expected.first; - while l_ind is not null loop - - ut.expect(a_actual.exists(l_ind),('['||a_msg||']Check annotation exists')).to_be_true; - if a_actual.exists(l_ind) then - check_annotation_params(a_msg||'.'||l_ind,a_expected(l_ind).params,a_actual(l_ind).params); - end if; - l_ind := a_expected.next(l_ind); - end loop; - end if; - end; - - procedure check_procedures(a_msg varchar2, a_expected ut_annotations.tt_procedure_list, a_actual ut_annotations.tt_procedure_list) is - l_found boolean := false; - l_index pls_integer; - begin - ut.expect(a_actual.count,'['||a_msg||']Check number of procedures parsed').to_equal(a_expected.count); - - if a_expected.count = a_actual.count and a_expected.count > 0 then - for i in 1..a_expected.count loop - l_found := false; - l_index := null; - for j in 1..a_actual.count loop - if a_expected(i).name = a_actual(j).name then - l_found:=true; - l_index := j; - exit; - end if; - end loop; - - ut.expect(l_found,'['||a_msg||']Check procedure exists').to_be_true; - if l_found then - check_annotations(a_msg||'.'||a_expected(i).name,a_expected(i).annotations,a_actual(l_index).annotations); - end if; - end loop; - end if; - end; -begin - check_annotations('PACKAGE',a_expected.package_annotations,a_parsing_result.package_annotations); - check_procedures('PROCEDURES',a_expected.procedure_annotations,a_parsing_result.procedure_annotations); -end check_annotation_parsing; -/ diff --git a/old_tests/ut_test/ut_test.DisabledFlagSkipTest.sql b/old_tests/ut_test/ut_test.DisabledFlagSkipTest.sql index e629fc416..fa982f59f 100644 --- a/old_tests/ut_test/ut_test.DisabledFlagSkipTest.sql +++ b/old_tests/ut_test/ut_test.DisabledFlagSkipTest.sql @@ -4,10 +4,6 @@ PROMPT Disable test by disabled flag declare l_suite ut_logical_suite; l_test ut_test; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - l_cnt number; l_listener ut_event_listener := ut_event_listener(ut_reporters()); begin diff --git a/old_tests/ut_test/ut_test.Rollback_type.Auto.sql b/old_tests/ut_test/ut_test.Rollback_type.Auto.sql index aeea82bd2..f2a9da321 100644 --- a/old_tests/ut_test/ut_test.Rollback_type.Auto.sql +++ b/old_tests/ut_test/ut_test.Rollback_type.Auto.sql @@ -4,10 +4,6 @@ PROMPT Test auto transaction control declare l_suite ut_logical_suite; l_test ut_test; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - l_cnt number; l_listener ut_event_listener := ut_event_listener(ut_reporters()); begin diff --git a/old_tests/ut_test/ut_test.Rollback_type.AutoOnFailure.sql b/old_tests/ut_test/ut_test.Rollback_type.AutoOnFailure.sql index 2bd597314..b17d3bdbf 100644 --- a/old_tests/ut_test/ut_test.Rollback_type.AutoOnFailure.sql +++ b/old_tests/ut_test/ut_test.Rollback_type.AutoOnFailure.sql @@ -4,10 +4,6 @@ PROMPT Test auto transaction control declare l_suite ut_logical_suite; l_test ut_test; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - l_cnt number; l_listener ut_event_listener := ut_event_listener(ut_reporters()); begin diff --git a/old_tests/ut_test/ut_test.Rollback_type.Manual.sql b/old_tests/ut_test/ut_test.Rollback_type.Manual.sql index a65e1d386..8c15e92ab 100644 --- a/old_tests/ut_test/ut_test.Rollback_type.Manual.sql +++ b/old_tests/ut_test/ut_test.Rollback_type.Manual.sql @@ -4,10 +4,6 @@ PROMPT Test manual transaction control declare l_suite ut_logical_suite; l_test ut_test; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - l_cnt number; l_listener ut_event_listener := ut_event_listener(ut_reporters()); begin diff --git a/old_tests/ut_test/ut_test.Rollback_type.ManualOnFailure.sql b/old_tests/ut_test/ut_test.Rollback_type.ManualOnFailure.sql index 53ca1eb17..f000b3b4b 100644 --- a/old_tests/ut_test/ut_test.Rollback_type.ManualOnFailure.sql +++ b/old_tests/ut_test/ut_test.Rollback_type.ManualOnFailure.sql @@ -4,10 +4,6 @@ PROMPT Test manual transaction control declare l_suite ut_logical_suite; l_test ut_test; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - l_cnt number; l_listener ut_event_listener := ut_event_listener(ut_reporters()); begin diff --git a/old_tests/ut_test_suite/ut_test_suite.Rollback_type.Auto.sql b/old_tests/ut_test_suite/ut_test_suite.Rollback_type.Auto.sql index 3e9991bff..ceda9b83c 100644 --- a/old_tests/ut_test_suite/ut_test_suite.Rollback_type.Auto.sql +++ b/old_tests/ut_test_suite/ut_test_suite.Rollback_type.Auto.sql @@ -4,10 +4,6 @@ PROMPT Test suite auto transaction control declare l_suite ut_logical_suite; l_test ut_test; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - l_cnt number; l_listener ut_event_listener := ut_event_listener(ut_reporters()); begin diff --git a/old_tests/ut_test_suite/ut_test_suite.Rollback_type.AutoOnFailure.sql b/old_tests/ut_test_suite/ut_test_suite.Rollback_type.AutoOnFailure.sql index 66c12f450..3a01feed7 100644 --- a/old_tests/ut_test_suite/ut_test_suite.Rollback_type.AutoOnFailure.sql +++ b/old_tests/ut_test_suite/ut_test_suite.Rollback_type.AutoOnFailure.sql @@ -4,10 +4,6 @@ PROMPT Test suite auto transaction control on failure declare l_suite ut_logical_suite; l_test ut_test; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - l_cnt number; l_listener ut_event_listener := ut_event_listener(ut_reporters()); begin diff --git a/old_tests/ut_test_suite/ut_test_suite.Rollback_type.Manual.sql b/old_tests/ut_test_suite/ut_test_suite.Rollback_type.Manual.sql index ba991a84f..49631838b 100644 --- a/old_tests/ut_test_suite/ut_test_suite.Rollback_type.Manual.sql +++ b/old_tests/ut_test_suite/ut_test_suite.Rollback_type.Manual.sql @@ -4,10 +4,6 @@ PROMPT Test suite manual transaction control declare l_suite ut_logical_suite; l_test ut_test; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - l_cnt number; l_listener ut_event_listener := ut_event_listener(ut_reporters()); begin diff --git a/old_tests/ut_test_suite/ut_test_suite.Rollback_type.ManualOnFailure.sql b/old_tests/ut_test_suite/ut_test_suite.Rollback_type.ManualOnFailure.sql index 3c5a54da7..7d77ba1f1 100644 --- a/old_tests/ut_test_suite/ut_test_suite.Rollback_type.ManualOnFailure.sql +++ b/old_tests/ut_test_suite/ut_test_suite.Rollback_type.ManualOnFailure.sql @@ -4,10 +4,6 @@ PROMPT Test suite manual transaction control on failure declare l_suite ut_logical_suite; l_test ut_test; - l_parsing_result ut_annotations.typ_annotated_package; - l_expected ut_annotations.typ_annotated_package; - l_ann_param ut_annotations.typ_annotation_param; - l_cnt number; l_listener ut_event_listener := ut_event_listener(ut_reporters()); begin From ac23f152e17ef68706dee81bf3c0abae8de2db0e Mon Sep 17 00:00:00 2001 From: Jacek Date: Sat, 7 Oct 2017 21:23:06 +0100 Subject: [PATCH 06/19] Refactored ut_annotation_parser API to use `ut_annotations` collection as output results. Reworked `ut_suite_manager` to operate on sorted collection for building ut_suite and it's tests. Adjusted `test_annotation_parser` to cover the refactoring. --- source/core/annotations/ut_annotation.tps | 8 + .../core/annotations/ut_annotation_parser.pkb | 173 +-- .../core/annotations/ut_annotation_parser.pks | 38 +- source/core/annotations/ut_annotations.tps | 2 + source/core/types/ut_test.tpb | 8 + source/core/types/ut_test.tps | 2 + source/core/ut_suite_manager.pkb | 1000 +++++++++-------- source/install.sql | 2 + source/uninstall.sql | 4 + .../test_annotation_parser.pkb | 312 +++-- 10 files changed, 716 insertions(+), 833 deletions(-) create mode 100644 source/core/annotations/ut_annotations.tps diff --git a/source/core/annotations/ut_annotation.tps b/source/core/annotations/ut_annotation.tps index e69de29bb..27a2cad0f 100644 --- a/source/core/annotations/ut_annotation.tps +++ b/source/core/annotations/ut_annotation.tps @@ -0,0 +1,8 @@ +create type ut_annotation as object( + position number(5,0), + name varchar2(1000), + text varchar2(4000), + subobject_name varchar2(250) +) +/ + diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index 0f2aa7a4f..360dd12d4 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -39,15 +39,14 @@ create or replace package body ut_annotation_parser as ,modifier => 'n'); end; - function get_annotations(a_source varchar2, a_comments tt_comment_list) return tt_annotations is - l_loop_index pls_integer := 1; - l_comment_index pls_integer; - l_comment varchar2(32767); - l_annotation_str varchar2(32767); - l_annotation_text varchar2(32767); - l_annotation_name varchar2(1000); - l_annotation t_annotation; - l_annotations_list tt_annotations; + function get_annotations(a_source varchar2, a_comments tt_comment_list, a_subobject_name varchar2 := null) return ut_annotations is + l_loop_index pls_integer := 1; + l_annotation_index pls_integer; + l_comment varchar2(32767); + l_annotation_str varchar2(32767); + l_annotation_text varchar2(32767); + l_annotation_name varchar2(1000); + l_annotations ut_annotations := ut_annotations(); begin -- loop while there are unprocessed comment blocks while 0 != nvl(regexp_instr(srcstr => a_source @@ -57,13 +56,13 @@ create or replace package body ut_annotation_parser as ,0) loop -- define index of the comment block and get it's content from cache - l_comment_index := to_number(regexp_substr(a_source + l_annotation_index := to_number(regexp_substr(a_source ,c_comment_replacer_regex_ptrn ,1 ,l_loop_index ,subexpression => 1)); - l_comment := a_comments(l_comment_index); + l_comment := a_comments( l_annotation_index ); -- strip everything except the annotation itself (spaces and others) l_annotation_str := regexp_substr(l_comment, c_annotation_pattern, 1, 1, modifier => 'i'); @@ -76,18 +75,19 @@ create or replace package body ut_annotation_parser as ,subexpression => 1)); l_annotation_text := trim(regexp_substr(l_annotation_str, '\((.*?)\)\s*$', subexpression => 1)); - l_annotation.text := l_annotation_text; - l_annotations_list(l_annotation_name) := l_annotation; + l_annotations.extend; + l_annotations( l_annotations.last) := + ut_annotation(l_annotation_index, l_annotation_name, l_annotation_text, a_subobject_name); end if; l_loop_index := l_loop_index + 1; end loop; - - return l_annotations_list; + return l_annotations; end get_annotations; - function get_package_annotations(a_source clob, a_comments tt_comment_list) return tt_annotations is + function get_package_annotations(a_source clob, a_comments tt_comment_list) return ut_annotations is l_package_comments varchar2(32767); + l_annotations ut_annotations := ut_annotations(); begin l_package_comments := regexp_substr(srcstr => a_source ,pattern => '^\s*(CREATE\s+(OR\s+REPLACE)?(\s+(NON)?EDITIONABLE)?\s+)?PACKAGE\s[^;]*?(\s+(AS|IS)\s+)((.*?{COMMENT#\d+}\s?)+)' @@ -95,19 +95,19 @@ create or replace package body ut_annotation_parser as ,subexpression => 7); -- parsing for package annotations - return - case when l_package_comments is not null then - get_annotations(l_package_comments, a_comments) - end; + if l_package_comments is not null then + l_annotations := get_annotations(l_package_comments, a_comments); + end if; + + return l_annotations; end; - function get_procedure_list(a_source clob, a_comments tt_comment_list) return tt_procedure_list is + function get_procedure_list(a_source clob, a_comments tt_comment_list) return ut_annotations is l_proc_comments varchar2(32767); l_proc_name t_annotation_name; l_annot_proc_ind number; l_annot_proc_block varchar2(32767); - l_procedure_annotations tt_procedure_annotations; - l_procedure_list tt_procedure_list; + l_annotations ut_annotations := ut_annotations(); begin -- loop through procedures and functions of the package and get all the comment blocks just before it's declaration l_annot_proc_ind := 1; @@ -139,10 +139,7 @@ create or replace package body ut_annotation_parser as ,subexpression => 5)); -- parse the comment block for the syntactically correct annotations and store them as an array - l_procedure_annotations.name := l_proc_name; - l_procedure_annotations.annotations := get_annotations(l_proc_comments, a_comments); - - l_procedure_list(l_procedure_list.count+1) := l_procedure_annotations; + l_annotations := l_annotations multiset union all get_annotations(l_proc_comments, a_comments, l_proc_name); --l_annot_proc_ind := l_annot_proc_ind + length(l_annot_proc_block); l_annot_proc_ind := regexp_instr(srcstr => a_source @@ -150,7 +147,8 @@ create or replace package body ut_annotation_parser as ,occurrence => 1 ,position => l_annot_proc_ind + length(l_annot_proc_block)); end loop; - return l_procedure_list; + + return l_annotations; end; function extract_and_replace_comments(a_source in out nocopy clob) return tt_comment_list is @@ -199,62 +197,21 @@ create or replace package body ut_annotation_parser as return l_comments; end extract_and_replace_comments; --- $if $$ut_trace $then --- procedure print_parse_results(a_annotated_pkg typ_annotated_package) is --- l_name t_annotation_name := a_annotated_pkg.package_annotations.first; --- l_proc_name t_annotation_name; --- begin --- dbms_output.put_line('Annotations count: ' || a_annotated_pkg.package_annotations.count); --- --- while l_name is not null loop --- dbms_output.put_line(' @' || l_name); --- if a_annotated_pkg.package_annotations(l_name).count > 0 then --- dbms_output.put_line(' Parameters:'); --- --- for j in 1 .. a_annotated_pkg.package_annotations(l_name).count loop --- dbms_output.put_line(' ' || nvl(a_annotated_pkg.package_annotations(l_name)(j).key, '') || ' = ' || --- nvl(a_annotated_pkg.package_annotations(l_name)(j).val, 'NULL')); --- end loop; --- else --- dbms_output.put_line(' No parameters.'); --- end if; --- --- l_name := a_annotated_pkg.package_annotations.next(l_name); --- --- end loop; --- --- dbms_output.put_line('Procedures count: ' || a_annotated_pkg.procedure_annotations.count); --- --- for i in 1 .. a_annotated_pkg.procedure_annotations.count loop --- l_proc_name := a_annotated_pkg.procedure_annotations(i).name; --- dbms_output.put_line(rpad('-', 80, '-')); --- dbms_output.put_line(' Procedure: ' || l_proc_name); --- dbms_output.put_line(' Annotations count: ' || a_annotated_pkg.procedure_annotations(i).annotations.count); --- l_name := a_annotated_pkg.procedure_annotations(i).annotations.first; --- while l_name is not null loop --- dbms_output.put_line(' @' || l_name); --- if a_annotated_pkg.procedure_annotations(i).annotations(l_name).count > 0 then --- dbms_output.put_line(' Parameters:'); --- for j in 1 .. a_annotated_pkg.procedure_annotations(i).annotations(l_name).count loop --- dbms_output.put_line(' ' || --- nvl(a_annotated_pkg.procedure_annotations(i).annotations(l_name)(j).key, '') || --- ' = ' || nvl(a_annotated_pkg.procedure_annotations(i).annotations(l_name)(j).val, 'NULL')); --- end loop; --- else --- dbms_output.put_line(' No parameters.'); --- end if; --- --- l_name := a_annotated_pkg.procedure_annotations(i).annotations.next(l_name); --- end loop; --- end loop; --- --- end print_parse_results; --- $end - - function parse_package_annotations(a_source clob) return typ_annotated_package is + $if $$ut_trace $then + procedure print_parse_results(a_annotations ut_annotations) is + begin + dbms_output.put_line('Annotations count: ' || a_annotations.count); + for i in 1 .. a_annotations.count loop + dbms_output.put_line(xmltype(a_annotations(i)).getclobval()); + end loop; + end print_parse_results; + $end + + function parse_package_annotations(a_source clob) return ut_annotations is l_source clob := a_source; l_comments tt_comment_list; - l_annotated_pkg typ_annotated_package; + l_annotations ut_annotations; + l_result ut_annotations; begin l_source := delete_multiline_comments(l_source); @@ -263,53 +220,37 @@ create or replace package body ut_annotation_parser as -- this call modifies l_source l_comments := extract_and_replace_comments(l_source); - l_annotated_pkg.package_annotations := get_package_annotations(l_source, l_comments); - - l_annotated_pkg.procedure_annotations := get_procedure_list(l_source, l_comments); - --- -- printing out parsed structure for debugging --- $if $$ut_trace $then --- print_parse_results(l_annotated_pkg); --- $end + l_annotations := + get_package_annotations(l_source, l_comments) + multiset union all get_procedure_list(l_source, l_comments); dbms_lob.freetemporary(l_source); - return l_annotated_pkg; + select value(x) + bulk collect into l_result + from table(l_annotations) x + order by x.position; + + -- printing out parsed structure for debugging + $if $$ut_trace $then + print_parse_results(l_result); + $end + return l_result; end parse_package_annotations; ------------------------------ --public definitions - function get_package_annotations(a_owner_name varchar2, a_name varchar2) return typ_annotated_package is + function get_package_annotations(a_owner_name varchar2, a_name varchar2) return ut_annotations is l_source clob; ex_package_is_wrapped exception; pragma exception_init(ex_package_is_wrapped, -24241); begin - - -- TODO: Add cache of annotations. Cache invalidation should be based on DDL timestamp. - -- Cache garbage collection should be executed once in a while to remove annotations cache for packages that were dropped. - - begin - l_source := ut_metadata.get_package_spec_source(a_owner_name, a_name); - exception - when ex_package_is_wrapped then - null; - end; - - if l_source is null or sys.dbms_lob.getlength(l_source)=0 then - return null; - else - return parse_package_annotations(l_source); - end if; + return parse_package_annotations(ut_metadata.get_package_spec_source(a_owner_name, a_name)); + exception + when ex_package_is_wrapped then + return ut_annotations(); end; --- function get_annotation_param(a_param_list tt_annotation_params, a_def_index pls_integer) return varchar2 is --- l_result varchar2(32767); --- begin --- if a_param_list.exists(a_def_index) then --- l_result := a_param_list(a_def_index).val; --- end if; --- return l_result; --- end get_annotation_param; -- parse the annotation parameters and return as key-value pair array function parse_annotation_params(a_annotation_text varchar2) return tt_annotation_params is diff --git a/source/core/annotations/ut_annotation_parser.pks b/source/core/annotations/ut_annotation_parser.pks index 1cf04ac2a..a54fd9668 100644 --- a/source/core/annotations/ut_annotation_parser.pks +++ b/source/core/annotations/ut_annotation_parser.pks @@ -46,53 +46,19 @@ create or replace package ut_annotation_parser authid current_user as */ type tt_annotation_params is table of typ_annotation_param index by pls_integer; - type t_annotation is record( - text varchar2(4000) - ); - - /* - type: tt_annotations - a list of tt_annotation_params index by the annotation name - */ - type tt_annotations is table of t_annotation index by t_annotation_name; - - /* - type: tt_procedure_annotations - a list of tt_annotations index by the procedure name - */ - type tt_procedure_annotations is record(name t_procedure_name, annotations tt_annotations); - - type tt_procedure_list is table of tt_procedure_annotations index by pls_integer; - - /* - type: typ_annotated_package - a structure containing a list of package level annotations and a list of procedure level annotations - - */ - type typ_annotated_package is record( - procedure_annotations tt_procedure_list - ,package_annotations tt_annotations); - /* INTERNAL USE ONLY */ - function parse_package_annotations(a_source clob) return typ_annotated_package; + function parse_package_annotations(a_source clob) return ut_annotations; /* function: get_package_annotations get annotations for specified package specification and return its annotated schema */ - function get_package_annotations(a_owner_name varchar2, a_name varchar2) return typ_annotated_package; + function get_package_annotations(a_owner_name varchar2, a_name varchar2) return ut_annotations; - /* - function: get_annotation_param - - get annotation parameter on a specified index position - */ --- function get_annotation_param(a_param_list tt_annotation_params, a_def_index pls_integer) return varchar2; - /* function: parse_annotation_params diff --git a/source/core/annotations/ut_annotations.tps b/source/core/annotations/ut_annotations.tps new file mode 100644 index 000000000..55f71e1de --- /dev/null +++ b/source/core/annotations/ut_annotations.tps @@ -0,0 +1,2 @@ +create type ut_annotations as table of ut_annotation +/ diff --git a/source/core/types/ut_test.tpb b/source/core/types/ut_test.tpb index 50a6f4882..1c6a4436d 100644 --- a/source/core/types/ut_test.tpb +++ b/source/core/types/ut_test.tpb @@ -33,6 +33,14 @@ create or replace type body ut_test as return; end; + member procedure set_beforeeach(self in out nocopy ut_test, a_before_each_proc_name varchar2) is + begin + self.before_each := ut_executable(self, a_before_each_proc_name, ut_utils.gc_before_each); + end; + member procedure set_aftereach(self in out nocopy ut_test, a_after_each_proc_name varchar2) is + begin + self.after_each := ut_executable(self, a_after_each_proc_name, ut_utils.gc_after_each); + end; member function is_valid(self in out nocopy ut_test) return boolean is l_is_valid boolean; begin diff --git a/source/core/types/ut_test.tps b/source/core/types/ut_test.tps index a8284c58e..bab100283 100644 --- a/source/core/types/ut_test.tps +++ b/source/core/types/ut_test.tps @@ -52,6 +52,8 @@ create or replace type ut_test under ut_suite_item ( a_after_test_proc_name varchar2 := null, a_after_each_proc_name varchar2 := null ) return self as result, member function is_valid(self in out nocopy ut_test) return boolean, + member procedure set_beforeeach(self in out nocopy ut_test, a_before_each_proc_name varchar2), + member procedure set_aftereach(self in out nocopy ut_test, a_after_each_proc_name varchar2), overriding member function do_execute(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_test), overriding member procedure mark_as_errored(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2), diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index 20c56843b..b6bbc7322 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -1,567 +1,569 @@ -create or replace package body ut_suite_manager is - /* - utPLSQL - Version X.X.X.X - Copyright 2016 - 2017 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. - */ - - type t_schema_info is record (changed_at date, obj_cnt integer); - - type tt_schema_suites is table of ut_logical_suite index by varchar2(4000 char); - type t_schema_cache is record( - schema_suites tt_schema_suites - ,changed_at date - ,obj_cnt integer); - type tt_schema_suites_list is table of t_schema_cache index by varchar2(128 char); - - g_schema_suites tt_schema_suites_list; - - function trim_path(a_path varchar2, a_part varchar2) return varchar2 is - begin - return substr(a_path, nvl(length(a_part), 0) + 1); - end; - - function get_schema_info(a_owner_name varchar2) return t_schema_info is - l_info t_schema_info; - l_view_name varchar2(200) := ut_metadata.get_dba_view('all_objects'); - begin - execute immediate q'[ - select nvl(max(t.last_ddl_time), date '4999-12-31'), count(*) - from ]'||l_view_name||q'[ t - where t.owner = :a_owner_name - and t.object_type in ('PACKAGE')]' - into l_info using a_owner_name; - return l_info; - end; - - function config_package(a_owner_name varchar2, a_object_name varchar2) return ut_logical_suite is - l_annotation_data ut_annotation_parser.typ_annotated_package; - l_suite_name ut_annotation_parser.t_annotation_name; - l_test ut_test; - l_proc_annotations ut_annotation_parser.tt_annotations; - - l_default_setup_proc varchar2(250 char); - l_default_teardown_proc varchar2(250 char); - l_suite_setup_proc varchar2(250 char); - l_suite_teardown_proc varchar2(250 char); - l_suite_path varchar2(4000 char); - - l_proc_name ut_annotation_parser.t_procedure_name; - - l_owner_name varchar2(250 char); - l_object_name varchar2(250 char); - l_suite ut_logical_suite; - - l_suite_rollback integer; - l_suite_rollback_annotation varchar2(4000); - e_insufficient_priv exception; - pragma exception_init(e_insufficient_priv,-01031); - begin - l_owner_name := a_owner_name; - l_object_name := a_object_name; - begin - ut_metadata.do_resolve(a_owner => l_owner_name, a_object => l_object_name); - exception - when e_insufficient_priv then - return null; - end; - l_annotation_data := ut_annotation_parser.get_package_annotations(a_owner_name => l_owner_name, a_name => l_object_name); + create or replace package body ut_suite_manager is + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 utPLSQL Project - if l_annotation_data.package_annotations.exists('suite') then + 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 - if l_annotation_data.package_annotations.exists('displayname') then - l_suite_name := l_annotation_data.package_annotations('displayname').text; - else - l_suite_name := l_annotation_data.package_annotations('suite').text; - end if; + http://www.apache.org/licenses/LICENSE-2.0 - if l_annotation_data.package_annotations.exists('suitepath') and l_annotation_data.package_annotations('suitepath').text is not null then - l_suite_path := l_annotation_data.package_annotations('suitepath').text || '.' || lower(l_object_name); - end if; + 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. + */ - if l_annotation_data.package_annotations.exists('rollback') then - l_suite_rollback_annotation := l_annotation_data.package_annotations('rollback').text; - if lower(l_suite_rollback_annotation) = 'manual' then - l_suite_rollback := ut_utils.gc_rollback_manual; - else - l_suite_rollback := ut_utils.gc_rollback_auto; - end if; - else - l_suite_rollback := ut_utils.gc_rollback_auto; - end if; + type t_schema_info is record (changed_at date, obj_cnt integer); - for i in 1 .. l_annotation_data.procedure_annotations.count loop - exit when l_default_setup_proc is not null and l_default_teardown_proc is not null and l_suite_setup_proc is not null and l_suite_teardown_proc is not null; - l_proc_name := l_annotation_data.procedure_annotations(i).name; - l_proc_annotations := l_annotation_data.procedure_annotations(i).annotations; - - if l_proc_annotations.exists('beforeeach') and l_default_setup_proc is null then - l_default_setup_proc := l_proc_name; - elsif l_proc_annotations.exists('aftereach') and l_default_teardown_proc is null then - l_default_teardown_proc := l_proc_name; - elsif l_proc_annotations.exists('beforeall') and l_suite_setup_proc is null then - l_suite_setup_proc := l_proc_name; - elsif l_proc_annotations.exists('afterall') and l_suite_teardown_proc is null then - l_suite_teardown_proc := l_proc_name; - end if; + type tt_schema_suites is table of ut_logical_suite index by varchar2(4000 char); + type t_schema_cache is record( + schema_suites tt_schema_suites + ,changed_at date + ,obj_cnt integer); + type tt_schema_suites_list is table of t_schema_cache index by varchar2(128 char); - end loop; - l_suite := ut_suite ( - a_object_owner => l_owner_name, - a_object_name => l_object_name, - a_name => l_object_name, --this could be different for sub-suite (context) - a_path => l_suite_path, --a patch for this suite (excluding the package name of current suite) - a_description => l_suite_name, - a_rollback_type => l_suite_rollback, - a_disabled_flag => l_annotation_data.package_annotations.exists('disabled'), - a_before_all_proc_name => l_suite_setup_proc, - a_after_all_proc_name => l_suite_teardown_proc - ); + g_schema_suites tt_schema_suites_list; + function trim_path(a_path varchar2, a_part varchar2) return varchar2 is + begin + return substr(a_path, nvl(length(a_part), 0) + 1); + end; - for i in 1 .. l_annotation_data.procedure_annotations.count loop - l_proc_name := l_annotation_data.procedure_annotations(i).name; - l_proc_annotations := l_annotation_data.procedure_annotations(i).annotations; - if l_proc_annotations.exists('test') then - declare - l_beforetest_procedure varchar2(30 char); - l_aftertest_procedure varchar2(30 char); - l_rollback_annotation varchar2(4000); - l_rollback_type integer := l_suite_rollback; - l_displayname varchar2(4000); - begin - if l_proc_annotations.exists('beforetest') then - l_beforetest_procedure := l_proc_annotations('beforetest').text; - end if; + function get_schema_info(a_owner_name varchar2) return t_schema_info is + l_info t_schema_info; + l_view_name varchar2(200) := ut_metadata.get_dba_view('all_objects'); + begin + execute immediate q'[ + select nvl(max(t.last_ddl_time), date '4999-12-31'), count(*) + from ]'||l_view_name||q'[ t + where t.owner = :a_owner_name + and t.object_type in ('PACKAGE')]' + into l_info using a_owner_name; + return l_info; + end; - if l_proc_annotations.exists('aftertest') then - l_aftertest_procedure := l_proc_annotations('aftertest').text; - end if; + function config_package(a_owner_name varchar2, a_object_name varchar2) return ut_logical_suite is + l_annotation_data ut_annotations; + l_is_suite boolean := false; + l_is_test boolean := false; + l_suite_disabled boolean := false; + l_test_disabled boolean := false; + l_suite_items ut_suite_items := ut_suite_items(); + l_suite_name ut_annotation_parser.t_annotation_name; + + l_default_setup_proc varchar2(250 char); + l_default_teardown_proc varchar2(250 char); + l_suite_setup_proc varchar2(250 char); + l_suite_teardown_proc varchar2(250 char); + l_suite_path varchar2(4000 char); + + l_proc_name ut_annotation_parser.t_procedure_name; + + l_owner_name varchar2(250 char); + l_object_name varchar2(250 char); + l_suite ut_logical_suite; + l_test ut_test; + + l_suite_rollback integer; + + l_beforetest_procedure varchar2(250 char); + l_aftertest_procedure varchar2(250 char); + l_rollback_type integer; + l_displayname varchar2(4000); - if l_proc_annotations.exists('displayname') then - l_displayname := l_proc_annotations('displayname').text; + e_insufficient_priv exception; + pragma exception_init(e_insufficient_priv,-01031); + begin + l_owner_name := a_owner_name; + l_object_name := a_object_name; + begin + ut_metadata.do_resolve(a_owner => l_owner_name, a_object => l_object_name); + exception + when e_insufficient_priv then + return null; + end; + l_annotation_data := ut_annotation_parser.get_package_annotations(a_owner_name => l_owner_name, a_name => l_object_name); + + l_suite_rollback := ut_utils.gc_rollback_auto; + for i in 1 .. l_annotation_data.count loop + + if l_annotation_data(i).subobject_name is null then + + if l_annotation_data(i).name in ('suite','displayname') then + l_suite_name := l_annotation_data(i).text; + if l_annotation_data(i).name = 'suite' then + l_is_suite := true; + end if; + elsif l_annotation_data(i).name = 'disabled' then + l_suite_disabled := true; + elsif l_annotation_data(i).name = 'suitepath' and l_annotation_data(i).text is not null then + l_suite_path := l_annotation_data(i).text || '.' || lower(l_object_name); + elsif l_annotation_data(i).name = 'rollback' then + if lower(l_annotation_data(i).text) = 'manual' then + l_suite_rollback := ut_utils.gc_rollback_manual; else - l_displayname := l_proc_annotations('test').text; + l_suite_rollback := ut_utils.gc_rollback_auto; end if; + end if; - if l_proc_annotations.exists('rollback') then - l_rollback_annotation := l_proc_annotations('rollback').text; - if lower(l_rollback_annotation) = 'manual' then - l_rollback_type := ut_utils.gc_rollback_manual; - elsif lower(l_rollback_annotation) = 'auto' then - l_rollback_type := ut_utils.gc_rollback_auto; - else - l_rollback_type := l_suite_rollback; - end if; + elsif l_is_suite then + + l_proc_name := l_annotation_data(i).subobject_name; + + if l_annotation_data(i).name = 'beforeeach' and l_default_setup_proc is null then + l_default_setup_proc := l_proc_name; + elsif l_annotation_data(i).name = 'aftereach' and l_default_teardown_proc is null then + l_default_teardown_proc := l_proc_name; + elsif l_annotation_data(i).name = 'beforeall' and l_suite_setup_proc is null then + l_suite_setup_proc := l_proc_name; + elsif l_annotation_data(i).name = 'afterall' and l_suite_teardown_proc is null then + l_suite_teardown_proc := l_proc_name; + + + elsif l_annotation_data(i).name = 'disabled' then + l_test_disabled := true; + elsif l_annotation_data(i).name = 'beforetest' then + l_beforetest_procedure := l_annotation_data(i).text; + elsif l_annotation_data(i).name = 'aftertest' then + l_aftertest_procedure := l_annotation_data(i).text; + elsif l_annotation_data(i).name in ('displayname','test') then + l_displayname := l_annotation_data(i).text; + if l_annotation_data(i).name = 'test' then + l_is_test := true; + end if; + elsif l_annotation_data(i).name = 'rollback' then + if lower(l_annotation_data(i).text) = 'manual' then + l_rollback_type := ut_utils.gc_rollback_manual; + elsif lower(l_annotation_data(i).text) = 'auto' then + l_rollback_type := ut_utils.gc_rollback_auto; end if; + end if; - l_test := ut_test(a_object_owner => l_owner_name - ,a_object_name => l_object_name - ,a_name => l_proc_name - ,a_description => l_displayname - ,a_path => l_suite.path || '.' || l_proc_name - ,a_rollback_type => l_rollback_type - ,a_disabled_flag => l_annotation_data.package_annotations.exists('disabled') or l_proc_annotations.exists('disabled') - ,a_before_test_proc_name => l_beforetest_procedure - ,a_after_test_proc_name => l_aftertest_procedure - ,a_before_each_proc_name => l_default_setup_proc - ,a_after_each_proc_name => l_default_teardown_proc); - - l_suite.add_item(l_test); - end; - end if; + if l_is_test + and (i = l_annotation_data.count or l_proc_name != nvl(l_annotation_data(i+1).subobject_name, ' ') ) then + l_suite_items.extend; + l_suite_items(l_suite_items.last) := + ut_test(a_object_owner => l_owner_name + ,a_object_name => l_object_name + ,a_name => l_proc_name + ,a_description => l_displayname + ,a_rollback_type => coalesce(l_rollback_type, l_suite_rollback) + ,a_disabled_flag => l_suite_disabled or l_test_disabled + ,a_before_test_proc_name => l_beforetest_procedure + ,a_after_test_proc_name => l_aftertest_procedure); + + l_is_test := false; + l_test_disabled := false; + l_aftertest_procedure := null; + l_beforetest_procedure := null; + l_rollback_type := null; + end if; + end if; end loop; - end if; - return l_suite; - - end config_package; - - procedure update_cache(a_owner_name varchar2, a_schema_suites tt_schema_suites, a_total_obj_cnt integer) is - begin - if a_schema_suites.count > 0 then - g_schema_suites(a_owner_name).schema_suites := a_schema_suites; - g_schema_suites(a_owner_name).changed_at := sysdate; - g_schema_suites(a_owner_name).obj_cnt := a_total_obj_cnt; - elsif g_schema_suites.exists(a_owner_name) then - g_schema_suites.delete(a_owner_name); - end if; - end; - - procedure config_schema(a_owner_name varchar2) is - l_suite ut_logical_suite; - - l_all_suites tt_schema_suites; - l_ind varchar2(4000 char); - l_path varchar2(4000 char); - l_root varchar2(4000 char); - l_root_suite ut_logical_suite; - - type t_object_name is record( - owner all_objects.owner%type, - object_name all_objects.object_name%type - ); - type t_object_names is table of t_object_name; - - l_object_names t_object_names; - l_view_name varchar2(200) := ut_metadata.get_dba_view('dba_objects'); - - l_schema_suites tt_schema_suites; - - procedure put(a_root_suite in out nocopy ut_logical_suite, a_path varchar2, a_suite ut_logical_suite, a_parent_path varchar2 default null) is - l_temp_root varchar2(4000 char); - l_path varchar2(4000 char); - l_cur_item ut_logical_suite; - l_ind pls_integer; + + if l_is_suite then + l_suite := ut_suite ( + a_object_owner => l_owner_name, + a_object_name => l_object_name, + a_name => l_object_name, --this could be different for sub-suite (context) + a_path => l_suite_path, --a patch for this suite (excluding the package name of current suite) + a_description => l_suite_name, + a_rollback_type => l_suite_rollback, + a_disabled_flag => l_suite_disabled, + a_before_all_proc_name => l_suite_setup_proc, + a_after_all_proc_name => l_suite_teardown_proc + ); + for i in 1 .. l_suite_items.count loop + l_test := treat(l_suite_items(i) as ut_test); + l_test.set_beforeeach(l_default_setup_proc); + l_test.set_aftereach(l_default_teardown_proc); + l_test.path := l_suite.path || '.' || l_test.name; + l_suite.add_item(l_test); + end loop; + end if; + + return l_suite; + + end config_package; + + procedure update_cache(a_owner_name varchar2, a_schema_suites tt_schema_suites, a_total_obj_cnt integer) is begin - if a_path like '%.%' then - l_temp_root := regexp_substr(a_path, '^[^.]+'); - l_path := ltrim(a_parent_path || '.' || l_temp_root, '.'); + if a_schema_suites.count > 0 then + g_schema_suites(a_owner_name).schema_suites := a_schema_suites; + g_schema_suites(a_owner_name).changed_at := sysdate; + g_schema_suites(a_owner_name).obj_cnt := a_total_obj_cnt; + elsif g_schema_suites.exists(a_owner_name) then + g_schema_suites.delete(a_owner_name); + end if; + end; - if a_root_suite is not null then + procedure config_schema(a_owner_name varchar2) is + l_suite ut_logical_suite; - l_ind := a_root_suite.item_index(l_temp_root); + l_all_suites tt_schema_suites; + l_ind varchar2(4000 char); + l_path varchar2(4000 char); + l_root varchar2(4000 char); + l_root_suite ut_logical_suite; - if l_ind is null then - --this only happens when a path of a real suite contains a parent-suite that is not a real package. - l_cur_item := ut_logical_suite(a_object_owner => a_owner_name, a_object_name => l_temp_root, a_name => l_temp_root, a_path => l_path); - else - l_cur_item := treat(a_root_suite.items(l_ind) as ut_logical_suite); - end if; + type t_object_name is record( + owner all_objects.owner%type, + object_name all_objects.object_name%type + ); + type t_object_names is table of t_object_name; + + l_object_names t_object_names; + l_view_name varchar2(200) := ut_metadata.get_dba_view('dba_objects'); + + l_schema_suites tt_schema_suites; + + procedure put(a_root_suite in out nocopy ut_logical_suite, a_path varchar2, a_suite ut_logical_suite, a_parent_path varchar2 default null) is + l_temp_root varchar2(4000 char); + l_path varchar2(4000 char); + l_cur_item ut_logical_suite; + l_ind pls_integer; + begin + if a_path like '%.%' then + l_temp_root := regexp_substr(a_path, '^[^.]+'); + l_path := ltrim(a_parent_path || '.' || l_temp_root, '.'); + + if a_root_suite is not null then + + l_ind := a_root_suite.item_index(l_temp_root); + + if l_ind is null then + --this only happens when a path of a real suite contains a parent-suite that is not a real package. + l_cur_item := ut_logical_suite(a_object_owner => a_owner_name, a_object_name => l_temp_root, a_name => l_temp_root, a_path => l_path); + else + l_cur_item := treat(a_root_suite.items(l_ind) as ut_logical_suite); + end if; - put(l_cur_item, trim_path(a_path, l_temp_root || '.'), a_suite, l_path); + put(l_cur_item, trim_path(a_path, l_temp_root || '.'), a_suite, l_path); + + if l_ind is null then + a_root_suite.add_item(l_cur_item); + else + a_root_suite.items(l_ind) := l_cur_item; + end if; - if l_ind is null then - a_root_suite.add_item(l_cur_item); else - a_root_suite.items(l_ind) := l_cur_item; + a_root_suite := ut_logical_suite(a_object_owner => a_owner_name, a_object_name => l_temp_root, a_name => l_temp_root, a_path => l_path); + put(a_root_suite, trim_path(a_path, l_temp_root || '.'), a_suite, l_path); end if; - else - a_root_suite := ut_logical_suite(a_object_owner => a_owner_name, a_object_name => l_temp_root, a_name => l_temp_root, a_path => l_path); - put(a_root_suite, trim_path(a_path, l_temp_root || '.'), a_suite, l_path); + if a_root_suite is not null then + a_root_suite.add_item(a_suite); + else + a_root_suite := a_suite; + end if; end if; - else - if a_root_suite is not null then - a_root_suite.add_item(a_suite); + end; + + $if $$ut_trace $then + procedure print(a_item ut_suite_item, a_pad pls_integer) is + l_suite ut_logical_suite; + l_pad varchar2(1000) := lpad(' ', a_pad, ' '); + begin + if a_item is of (ut_logical_suite) then + dbms_output.put_line(l_pad || 'Suite: ' || a_item.name || '(' || a_item.path || ')'); + dbms_output.put_line(l_pad || 'Items: '); + l_suite := treat(a_item as ut_logical_suite); + for i in 1 .. l_suite.items.count loop + print(l_suite.items(i), a_pad + 2); + end loop; else - a_root_suite := a_suite; + dbms_output.put_line(l_pad || 'Test: ' || a_item.name || '(' || a_item.path || ')' ); end if; - end if; - end; + end print; + $end - $if $$ut_trace $then - procedure print(a_item ut_suite_item, a_pad pls_integer) is - l_suite ut_logical_suite; - l_pad varchar2(1000) := lpad(' ', a_pad, ' '); begin - if a_item is of (ut_logical_suite) then - dbms_output.put_line(l_pad || 'Suite: ' || a_item.name || '(' || a_item.path || ')'); - dbms_output.put_line(l_pad || 'Items: '); - l_suite := treat(a_item as ut_logical_suite); - for i in 1 .. l_suite.items.count loop - print(l_suite.items(i), a_pad + 2); - end loop; - else - dbms_output.put_line(l_pad || 'Test: ' || a_item.name || '(' || a_item.path || ')' ); - end if; - end print; - $end - - begin - -- form the single-dimension list of suites constructed from parsed packages - execute immediate - 'select t.owner, t.object_name from '||l_view_name||' t ' - ||q'[ where t.owner = :a_owner_name and t.status = 'VALID' and t.object_type in ('PACKAGE')]' - bulk collect into l_object_names using a_owner_name; - for i in 1 .. cardinality(l_object_names) loop - -- parse the source of the package - l_suite := config_package(l_object_names(i).owner, l_object_names(i).object_name); - - if l_suite is not null then - l_all_suites(l_suite.path) := l_suite; - end if; + -- form the single-dimension list of suites constructed from parsed packages + execute immediate + 'select t.owner, t.object_name from '||l_view_name||' t ' + ||q'[ where t.owner = :a_owner_name and t.status = 'VALID' and t.object_type in ('PACKAGE')]' + bulk collect into l_object_names using a_owner_name; + for i in 1 .. cardinality(l_object_names) loop + -- parse the source of the package + l_suite := config_package(l_object_names(i).owner, l_object_names(i).object_name); + + if l_suite is not null then + l_all_suites(l_suite.path) := l_suite; + end if; - end loop; + end loop; - l_schema_suites.delete; + l_schema_suites.delete; - -- Restructure single-dimenstion list into hierarchy of suites by the value of %suitepath attribute value - -- All root suite compose the root-suite list of the schema - l_ind := l_all_suites.first; - while l_ind is not null loop + -- Restructure single-dimenstion list into hierarchy of suites by the value of %suitepath attribute value + -- All root suite compose the root-suite list of the schema + l_ind := l_all_suites.first; + while l_ind is not null loop - l_root := regexp_substr(l_ind, '^[^.]+'); + l_root := regexp_substr(l_ind, '^[^.]+'); - if l_schema_suites.exists(l_root) then - l_root_suite := l_schema_suites(l_root); - l_path := trim_path(l_ind, l_root || '.'); - else - l_root_suite := null; - l_path := l_ind; - end if; - put(l_root_suite, l_path, l_all_suites(l_ind)); - - l_schema_suites(l_root) := l_root_suite; - - l_ind := l_all_suites.next(l_ind); - end loop; - - -- Caching - update_cache(a_owner_name, l_schema_suites, get_schema_info(a_owner_name).obj_cnt ); - - -- printing results for debugging purpose - $if $$ut_trace $then - l_ind := l_schema_suites.first; - while l_ind is not null loop - print(l_schema_suites(l_ind), 0); - l_ind := l_schema_suites.next(l_ind); - end loop; - $end - - end config_schema; - - function get_schema_suites(a_schema_name in varchar2) return tt_schema_suites is - l_schema_info t_schema_info; - begin - -- Currently cache invalidation on DDL is not implemented so schema is rescaned each time - l_schema_info := get_schema_info(a_schema_name); - if not g_schema_suites.exists(a_schema_name) or g_schema_suites(a_schema_name).changed_at <= l_schema_info.changed_at or - g_schema_suites(a_schema_name).obj_cnt != l_schema_info.obj_cnt then - ut_utils.debug_log('Rescanning schema ' || a_schema_name); - config_schema(a_schema_name); - end if; - - if g_schema_suites.exists(a_schema_name) then - return g_schema_suites(a_schema_name).schema_suites; - else - return cast(null as tt_schema_suites); - end if; - end get_schema_suites; - - function get_schema_ut_packages(a_schema_names ut_varchar2_rows) return ut_object_names is - l_schema_ut_packages ut_object_names := ut_object_names(); - l_schema_suites tt_schema_suites; - l_iter varchar2(4000); - procedure populate_suite_ut_packages(a_suite ut_logical_suite, a_packages in out nocopy ut_object_names) is - l_sub_suite ut_logical_suite; - begin - if a_suite is of (ut_suite) then - a_packages.extend; - a_packages(a_packages.last) := ut_object_name(a_suite.object_owner, a_suite.object_name); - end if; - for i in 1 .. a_suite.items.count loop - if a_suite.items(i) is of (ut_logical_suite) then - l_sub_suite := treat(a_suite.items(i) as ut_logical_suite); - populate_suite_ut_packages(l_sub_suite, a_packages); + if l_schema_suites.exists(l_root) then + l_root_suite := l_schema_suites(l_root); + l_path := trim_path(l_ind, l_root || '.'); + else + l_root_suite := null; + l_path := l_ind; end if; + put(l_root_suite, l_path, l_all_suites(l_ind)); + + l_schema_suites(l_root) := l_root_suite; + + l_ind := l_all_suites.next(l_ind); end loop; - end; - begin - if a_schema_names is not null then - for i in 1 .. a_schema_names.count loop - l_schema_suites := get_schema_suites(a_schema_names(i)); - l_iter := l_schema_suites.first; - while l_iter is not null loop - populate_suite_ut_packages(l_schema_suites(l_iter), l_schema_ut_packages); - l_iter := l_schema_suites.next(l_iter); - end loop; + + -- Caching + update_cache(a_owner_name, l_schema_suites, get_schema_info(a_owner_name).obj_cnt ); + + -- printing results for debugging purpose + $if $$ut_trace $then + l_ind := l_schema_suites.first; + while l_ind is not null loop + print(l_schema_suites(l_ind), 0); + l_ind := l_schema_suites.next(l_ind); end loop; - l_schema_ut_packages := set(l_schema_ut_packages); - end if; - - return l_schema_ut_packages; - end; - - -- Validate all paths are correctly formatted - procedure validate_paths(a_paths in ut_varchar2_list) is - l_path varchar2(32767); - begin - if a_paths is null or a_paths.count = 0 then - raise_application_error(ut_utils.gc_path_list_is_empty, 'Path list is empty'); - else - for i in 1 .. a_paths.count loop - l_path := a_paths(i); - if l_path is null or not (regexp_like(l_path, '^[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+){0,2}$') or regexp_like(l_path, '^([A-Za-z0-9$#_]+)?:[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+)*$')) then - raise_application_error(ut_utils.gc_invalid_path_format, 'Invalid path format: ' || nvl(l_path, 'NULL')); + $end + + end config_schema; + + function get_schema_suites(a_schema_name in varchar2) return tt_schema_suites is + l_schema_info t_schema_info; + begin + -- Currently cache invalidation on DDL is not implemented so schema is rescaned each time + l_schema_info := get_schema_info(a_schema_name); + if not g_schema_suites.exists(a_schema_name) or g_schema_suites(a_schema_name).changed_at <= l_schema_info.changed_at or + g_schema_suites(a_schema_name).obj_cnt != l_schema_info.obj_cnt then + ut_utils.debug_log('Rescanning schema ' || a_schema_name); + config_schema(a_schema_name); + end if; + + if g_schema_suites.exists(a_schema_name) then + return g_schema_suites(a_schema_name).schema_suites; + else + return cast(null as tt_schema_suites); + end if; + end get_schema_suites; + + function get_schema_ut_packages(a_schema_names ut_varchar2_rows) return ut_object_names is + l_schema_ut_packages ut_object_names := ut_object_names(); + l_schema_suites tt_schema_suites; + l_iter varchar2(4000); + procedure populate_suite_ut_packages(a_suite ut_logical_suite, a_packages in out nocopy ut_object_names) is + l_sub_suite ut_logical_suite; + begin + if a_suite is of (ut_suite) then + a_packages.extend; + a_packages(a_packages.last) := ut_object_name(a_suite.object_owner, a_suite.object_name); end if; - end loop; - end if; - end validate_paths; - - function configure_execution_by_path(a_paths in ut_varchar2_list) return ut_suite_items is - l_paths ut_varchar2_list; - l_path varchar2(32767); - l_schema varchar2(4000); - l_schema_suites tt_schema_suites; - l_index varchar2(4000 char); - l_suite ut_logical_suite; - l_suite_path varchar2(4000); - l_root_suite_name varchar2(4000); - l_objects_to_run ut_suite_items; - c_current_schema constant all_tables.owner%type := sys_context('USERENV','CURRENT_SCHEMA'); - - function clean_paths(a_paths ut_varchar2_list) return ut_varchar2_list is - l_paths_temp ut_varchar2_list := ut_varchar2_list(); + for i in 1 .. a_suite.items.count loop + if a_suite.items(i) is of (ut_logical_suite) then + l_sub_suite := treat(a_suite.items(i) as ut_logical_suite); + populate_suite_ut_packages(l_sub_suite, a_packages); + end if; + end loop; + end; begin - l_paths_temp.extend(a_paths.count); - for i in 1 .. a_paths.count loop - l_paths_temp(i) := trim(lower(a_paths(i))); - end loop; - l_paths_temp := set(l_paths_temp); - return l_paths_temp; - end clean_paths; + if a_schema_names is not null then + for i in 1 .. a_schema_names.count loop + l_schema_suites := get_schema_suites(a_schema_names(i)); + l_iter := l_schema_suites.first; + while l_iter is not null loop + populate_suite_ut_packages(l_schema_suites(l_iter), l_schema_ut_packages); + l_iter := l_schema_suites.next(l_iter); + end loop; + end loop; + l_schema_ut_packages := set(l_schema_ut_packages); + end if; - procedure skip_by_path(a_suite in out nocopy ut_suite_item, a_path varchar2) is - c_root constant varchar2(32767) := upper(regexp_substr(a_path, '[A-Za-z0-9$#_]+')); - c_rest_path constant varchar2(32767) := regexp_substr(a_path, '\.(.+)', subexpression => 1); - l_suite ut_logical_suite; - l_item ut_suite_item; - l_items ut_suite_items := ut_suite_items(); + return l_schema_ut_packages; + end; + + -- Validate all paths are correctly formatted + procedure validate_paths(a_paths in ut_varchar2_list) is + l_path varchar2(32767); begin - if a_path is not null and a_suite is not null and a_suite is of (ut_logical_suite) then - l_suite := treat(a_suite as ut_logical_suite); + if a_paths is null or a_paths.count = 0 then + raise_application_error(ut_utils.gc_path_list_is_empty, 'Path list is empty'); + else + for i in 1 .. a_paths.count loop + l_path := a_paths(i); + if l_path is null or not (regexp_like(l_path, '^[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+){0,2}$') or regexp_like(l_path, '^([A-Za-z0-9$#_]+)?:[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+)*$')) then + raise_application_error(ut_utils.gc_invalid_path_format, 'Invalid path format: ' || nvl(l_path, 'NULL')); + end if; + end loop; + end if; + end validate_paths; + + function configure_execution_by_path(a_paths in ut_varchar2_list) return ut_suite_items is + l_paths ut_varchar2_list; + l_path varchar2(32767); + l_schema varchar2(4000); + l_schema_suites tt_schema_suites; + l_index varchar2(4000 char); + l_suite ut_logical_suite; + l_suite_path varchar2(4000); + l_root_suite_name varchar2(4000); + l_objects_to_run ut_suite_items; + c_current_schema constant all_tables.owner%type := sys_context('USERENV','CURRENT_SCHEMA'); + + function clean_paths(a_paths ut_varchar2_list) return ut_varchar2_list is + l_paths_temp ut_varchar2_list := ut_varchar2_list(); + begin + l_paths_temp.extend(a_paths.count); + for i in 1 .. a_paths.count loop + l_paths_temp(i) := trim(lower(a_paths(i))); + end loop; + l_paths_temp := set(l_paths_temp); + return l_paths_temp; + end clean_paths; - for i in 1 .. l_suite.items.count loop + procedure skip_by_path(a_suite in out nocopy ut_suite_item, a_path varchar2) is + c_root constant varchar2(32767) := upper(regexp_substr(a_path, '[A-Za-z0-9$#_]+')); + c_rest_path constant varchar2(32767) := regexp_substr(a_path, '\.(.+)', subexpression => 1); + l_suite ut_logical_suite; + l_item ut_suite_item; + l_items ut_suite_items := ut_suite_items(); + begin + if a_path is not null and a_suite is not null and a_suite is of (ut_logical_suite) then + l_suite := treat(a_suite as ut_logical_suite); - l_item := l_suite.items(i); + for i in 1 .. l_suite.items.count loop - if upper(l_item.name) = c_root then + l_item := l_suite.items(i); - skip_by_path(l_item, c_rest_path); - l_items.extend; - l_items(l_items.count) := l_item; + if upper(l_item.name) = c_root then - end if; + skip_by_path(l_item, c_rest_path); + l_items.extend; + l_items(l_items.count) := l_item; - end loop; + end if; - if l_items.count = 0 then - --not l_found then - raise_application_error(-20203, 'Suite not found'); - end if; + end loop; - l_suite.items := l_items; - a_suite := l_suite; + if l_items.count = 0 then + --not l_found then + raise_application_error(-20203, 'Suite not found'); + end if; - end if; - end skip_by_path; + l_suite.items := l_items; + a_suite := l_suite; - function package_exists_in_cur_schema(a_package_name varchar2) return boolean is - l_cnt number; - begin - select count(*) - into l_cnt - from all_objects t - where t.object_name = upper(a_package_name) - and t.object_type = 'PACKAGE' - and t.owner = c_current_schema; - return l_cnt > 0; - end package_exists_in_cur_schema; - - begin - l_paths := clean_paths(a_paths); - - validate_paths(l_paths); - l_objects_to_run := ut_suite_items(); - - -- current implementation operates only on a single path - -- to be improved later - for i in 1 .. l_paths.count loop - l_path := l_paths(i); - - if regexp_like(l_path, '^([A-Za-z0-9$#_]+)?:') then - l_schema := regexp_substr(l_path, '^([A-Za-z0-9$#_]+)?:',subexpression => 1); - -- transform ":path1[.path2]" to "schema:path1[.path2]" - if l_schema is not null then - l_schema := sys.dbms_assert.schema_name(upper(l_schema)); - else - l_path := c_current_schema || l_path; - l_schema := c_current_schema; end if; - else - -- When path is one of: schema or schema.package[.object] or package[.object] - -- transform it back to schema[.package[.object]] - begin - l_schema := regexp_substr(l_path, '^[A-Za-z0-9$#_]+'); - l_schema := sys.dbms_assert.schema_name(upper(l_schema)); - exception - when sys.dbms_assert.invalid_schema_name then - if package_exists_in_cur_schema(l_schema) then - l_path := c_current_schema || '.' || l_path; - l_schema := c_current_schema; - else - raise; - end if; - end; + end skip_by_path; + + function package_exists_in_cur_schema(a_package_name varchar2) return boolean is + l_cnt number; + begin + select count(*) + into l_cnt + from all_objects t + where t.object_name = upper(a_package_name) + and t.object_type = 'PACKAGE' + and t.owner = c_current_schema; + return l_cnt > 0; + end package_exists_in_cur_schema; - end if; + begin + l_paths := clean_paths(a_paths); - l_schema_suites := get_schema_suites(upper(l_schema)); + validate_paths(l_paths); + l_objects_to_run := ut_suite_items(); - if regexp_like(l_path, '^[A-Za-z0-9$#_]+$') then - -- run whole schema - l_index := l_schema_suites.first; - while l_index is not null loop - l_objects_to_run.extend; - l_objects_to_run(l_objects_to_run.count) := l_schema_suites(l_index); - l_index := l_schema_suites.next(l_index); - end loop; - else - -- convert SCHEMA.PACKAGE.PROCEDURE syntax to fully qualified path - if regexp_like(l_path, '^[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+){1,2}$') then - declare - l_temp_suite ut_logical_suite; - l_package_name varchar2(4000); - l_procedure_name varchar2(4000); + -- current implementation operates only on a single path + -- to be improved later + for i in 1 .. l_paths.count loop + l_path := l_paths(i); + + if regexp_like(l_path, '^([A-Za-z0-9$#_]+)?:') then + l_schema := regexp_substr(l_path, '^([A-Za-z0-9$#_]+)?:',subexpression => 1); + -- transform ":path1[.path2]" to "schema:path1[.path2]" + if l_schema is not null then + l_schema := sys.dbms_assert.schema_name(upper(l_schema)); + else + l_path := c_current_schema || l_path; + l_schema := c_current_schema; + end if; + else + -- When path is one of: schema or schema.package[.object] or package[.object] + -- transform it back to schema[.package[.object]] begin - l_package_name := regexp_substr(l_path, '^[A-Za-z0-9$#_]+\.([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?$', subexpression => 1); - l_procedure_name := regexp_substr(l_path, '^[A-Za-z0-9$#_]+\.([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?$', subexpression => 3); + l_schema := regexp_substr(l_path, '^[A-Za-z0-9$#_]+'); + l_schema := sys.dbms_assert.schema_name(upper(l_schema)); + exception + when sys.dbms_assert.invalid_schema_name then + if package_exists_in_cur_schema(l_schema) then + l_path := c_current_schema || '.' || l_path; + l_schema := c_current_schema; + else + raise; + end if; + end; - l_temp_suite := config_package(l_schema, l_package_name); + end if; - if l_temp_suite is null then - raise_application_error(ut_utils.gc_suite_package_not_found,'Suite package '||l_schema||'.'||l_package_name|| ' not found'); - end if; + l_schema_suites := get_schema_suites(upper(l_schema)); - l_path := rtrim(l_schema || ':' || l_temp_suite.path || '.' || l_procedure_name, '.'); - end; - end if; + if regexp_like(l_path, '^[A-Za-z0-9$#_]+$') then + -- run whole schema + l_index := l_schema_suites.first; + while l_index is not null loop + l_objects_to_run.extend; + l_objects_to_run(l_objects_to_run.count) := l_schema_suites(l_index); + l_index := l_schema_suites.next(l_index); + end loop; + else + -- convert SCHEMA.PACKAGE.PROCEDURE syntax to fully qualified path + if regexp_like(l_path, '^[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+){1,2}$') then + declare + l_temp_suite ut_logical_suite; + l_package_name varchar2(4000); + l_procedure_name varchar2(4000); + begin + l_package_name := regexp_substr(l_path, '^[A-Za-z0-9$#_]+\.([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?$', subexpression => 1); + l_procedure_name := regexp_substr(l_path, '^[A-Za-z0-9$#_]+\.([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?$', subexpression => 3); + + l_temp_suite := config_package(l_schema, l_package_name); + + if l_temp_suite is null then + raise_application_error(ut_utils.gc_suite_package_not_found,'Suite package '||l_schema||'.'||l_package_name|| ' not found'); + end if; - -- fully qualified path branch in the form - -- by this time it's the only format left - -- schema:suite.suite.suite - l_suite_path := regexp_substr(l_path, ':(.+)', subexpression => 1); - l_root_suite_name := regexp_substr(l_suite_path, '^[A-Za-z0-9$#_]+'); + l_path := rtrim(l_schema || ':' || l_temp_suite.path || '.' || l_procedure_name, '.'); + end; + end if; - begin - l_suite := l_schema_suites(l_root_suite_name); - exception - when no_data_found then - raise_application_error(-20203, 'Suite ' || l_root_suite_name || ' does not exist or is invalid'); - end; + -- fully qualified path branch in the form + -- by this time it's the only format left + -- schema:suite.suite.suite + l_suite_path := regexp_substr(l_path, ':(.+)', subexpression => 1); + l_root_suite_name := regexp_substr(l_suite_path, '^[A-Za-z0-9$#_]+'); - skip_by_path(l_suite, regexp_substr(l_suite_path, '\.(.+)', subexpression => 1)); + begin + l_suite := l_schema_suites(l_root_suite_name); + exception + when no_data_found then + raise_application_error(-20203, 'Suite ' || l_root_suite_name || ' does not exist or is invalid'); + end; - l_objects_to_run.extend; - l_objects_to_run(l_objects_to_run.count) := l_suite; + skip_by_path(l_suite, regexp_substr(l_suite_path, '\.(.+)', subexpression => 1)); - end if; + l_objects_to_run.extend; + l_objects_to_run(l_objects_to_run.count) := l_suite; + + end if; - end loop; - return l_objects_to_run; - end configure_execution_by_path; + end loop; + return l_objects_to_run; + end configure_execution_by_path; -end ut_suite_manager; -/ + end ut_suite_manager; + / diff --git a/source/install.sql b/source/install.sql index 563f4293f..6d58cf0d0 100644 --- a/source/install.sql +++ b/source/install.sql @@ -75,6 +75,8 @@ alter session set plsql_warnings = 'ENABLE:ALL', 'DISABLE:(5004,5018,6000,6001,6 @@install_component.sql 'core/ut_output_buffer.pkb' --annoations +@@install_component.sql 'core/annotations/ut_annotation.tps' +@@install_component.sql 'core/annotations/ut_annotations.tps' @@install_component.sql 'core/annotations/ut_annotation_parser.pks' @@install_component.sql 'core/annotations/ut_annotation_parser.pkb' diff --git a/source/uninstall.sql b/source/uninstall.sql index b3d4c7048..0c04227ad 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -174,6 +174,10 @@ drop table ut_cursor_data; drop package ut_annotation_parser; +drop type ut_annotation force; + +drop type ut_annotations force; + drop package ut_file_mapper; drop package ut_metadata; diff --git a/test/ut_annotation_parser/test_annotation_parser.pkb b/test/ut_annotation_parser/test_annotation_parser.pkb index 0a71ebccb..9af022b3d 100644 --- a/test/ut_annotation_parser/test_annotation_parser.pkb +++ b/test/ut_annotation_parser/test_annotation_parser.pkb @@ -1,63 +1,9 @@ create or replace package body test_annotation_parser is - procedure check_annotation_parsing(a_expected ut3.ut_annotation_parser.typ_annotated_package, a_parsing_result ut3.ut_annotation_parser.typ_annotated_package) is - - procedure check_annotations(a_msg varchar2, a_expected ut3.ut_annotation_parser.tt_annotations, a_actual ut3.ut_annotation_parser.tt_annotations) is - l_ind varchar2(500); - begin - ut.expect(a_actual.count, '[' || a_msg || ']Check number of annotations parsed').to_equal(a_expected.count); - - if a_expected.count = a_actual.count and a_expected.count > 0 then - l_ind := a_expected.first; - while l_ind is not null loop - - ut.expect(a_actual.exists(l_ind), ('[' || a_msg || ']Check annotation exists')).to_be_true; - if a_actual.exists(l_ind) then - ut.expect(a_actual(l_ind).text, ('[' || a_msg || ']Check annotation text')).to_equal(a_expected(l_ind).text); --- check_annotation_params(a_msg || '.' || l_ind, a_expected(l_ind).params, a_actual(l_ind).params); - end if; - l_ind := a_expected.next(l_ind); - end loop; - end if; - end; - - procedure check_procedures(a_msg varchar2, a_expected ut3.ut_annotation_parser.tt_procedure_list, a_actual ut3.ut_annotation_parser.tt_procedure_list) is - l_found boolean := false; - l_index pls_integer; - begin - ut.expect(a_actual.count, '[' || a_msg || ']Check number of procedures parsed').to_equal(a_expected.count); - - if a_expected.count = a_actual.count and a_expected.count > 0 then - for i in 1 .. a_expected.count loop - l_found := false; - l_index := null; - for j in 1 .. a_actual.count loop - if a_expected(i).name = a_actual(j).name then - l_found := true; - l_index := j; - exit; - end if; - end loop; - - ut.expect(l_found, '[' || a_msg || ']Check procedure exists').to_be_true; - if l_found then - check_annotations(a_msg || '.' || a_expected(i).name - ,a_expected(i).annotations - ,a_actual(l_index).annotations); - end if; - end loop; - end if; - end; - begin - check_annotations('PACKAGE', a_expected.package_annotations, a_parsing_result.package_annotations); - check_procedures('PROCEDURES', a_expected.procedure_annotations, a_parsing_result.procedure_annotations); - end check_annotation_parsing; - procedure test1 is - l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; + l_source clob; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; begin l_source := 'PACKAGE test_tt AS @@ -72,27 +18,24 @@ create or replace package body test_annotation_parser is END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('displayname').text := 'Name of suite'; - - l_expected.package_annotations('suitepath').text := 'all.globaltests'; - - l_expected.procedure_annotations(1).annotations('ann2').text := 'some_value'; - l_expected.procedure_annotations(1).name := 'foo'; - check_annotation_parsing(l_expected, l_parsing_result); + l_expected := ut3.ut_annotations( + ut3.ut_annotation(1,'suite',null, null), + ut3.ut_annotation(2,'displayname','Name of suite',null), + ut3.ut_annotation(3,'suitepath','all.globaltests',null), + ut3.ut_annotation(5,'ann2','some_value','foo') + ); + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure test2 is - l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; - + l_source clob; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; begin l_source := 'PACKAGE test_tt AS -- %suite @@ -106,23 +49,23 @@ create or replace package body test_annotation_parser is END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('displayname').text := 'Name of suite'; - - l_expected.package_annotations('suitepath').text := 'all.globaltests'; + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', null, null ), + ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ) + ); - check_annotation_parsing(l_expected, l_parsing_result); + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure test3 is l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; begin l_source := 'PACKAGE test_tt AS @@ -152,32 +95,26 @@ create or replace package body test_annotation_parser is END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('displayname').text := 'Name of suite'; - - l_expected.package_annotations('suitepath').text := 'all.globaltests'; - - l_expected.procedure_annotations(1).name := 'foo'; - l_expected.procedure_annotations(1).annotations('test').text := null; + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', null, null ), + ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ), + ut3.ut_annotation( 4, 'test', null, 'foo' ), + ut3.ut_annotation( 5, 'beforeeach', null,'foo2' ), + ut3.ut_annotation( 6, 'beforeeach', 'key=testval','foo3' ) + ); - l_expected.procedure_annotations(2).name := 'foo2'; - l_expected.procedure_annotations(2).annotations('beforeeach').text := null; - - l_expected.procedure_annotations(3).name := 'foo3'; - l_expected.procedure_annotations(3).annotations('beforeeach').text := 'key=testval'; - - check_annotation_parsing(l_expected, l_parsing_result); + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure test4 is l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; begin l_source := 'PACKAGE test_tt AS @@ -190,25 +127,23 @@ create or replace package body test_annotation_parser is END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('displayname').text := 'Name of suite'; - l_expected.package_annotations('suitepath').text := 'all.globaltests'; - - l_expected.procedure_annotations(1).name := 'foo'; - l_expected.procedure_annotations(1).annotations('test').text := null; - - check_annotation_parsing(l_expected, l_parsing_result); - + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', null, null ), + ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ), + ut3.ut_annotation( 4, 'test', null, 'foo' ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure test5 is l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; begin l_source := 'PACKAGE test_tt AS @@ -220,22 +155,23 @@ create or replace package body test_annotation_parser is END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('displayname').text := 'Name of suite'; - l_expected.package_annotations('suitepath').text := 'all.globaltests'; + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', null, null ), + ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ) + ); - check_annotation_parsing(l_expected, l_parsing_result); + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure test6 is l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; begin l_source := 'PACKAGE test_tt accessible by (foo) AS @@ -247,22 +183,23 @@ create or replace package body test_annotation_parser is END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('displayname').text := 'Name of suite'; - l_expected.package_annotations('suitepath').text := 'all.globaltests'; + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', null, null ), + ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ) + ); - check_annotation_parsing(l_expected, l_parsing_result); + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure test7 is l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; begin l_source := 'PACKAGE test_tt @@ -277,22 +214,23 @@ create or replace package body test_annotation_parser is END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('displayname').text := 'Name of suite'; - l_expected.package_annotations('suitepath').text := 'all.globaltests'; + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', null, null ), + ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ) + ); - check_annotation_parsing(l_expected, l_parsing_result); + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure test8 is l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; begin l_source := 'PACKAGE test_tt AS @@ -304,22 +242,23 @@ create or replace package body test_annotation_parser is END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('displayname').text := 'name = Name of suite'; - l_expected.package_annotations('suitepath').text := 'key=all.globaltests,key2=foo'; + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', null, null ), + ut3.ut_annotation( 2, 'displayname', 'name = Name of suite', null ), + ut3.ut_annotation( 3, 'suitepath', 'key=all.globaltests,key2=foo', null ) + ); - check_annotation_parsing(l_expected, l_parsing_result); + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure test9 is l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; begin l_source := 'PACKAGE test_tt AS @@ -335,25 +274,27 @@ create or replace package body test_annotation_parser is END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('displayname').text := 'Name of suite'; - l_expected.package_annotations('suitepath').text := 'all.globaltests'; + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', null, null ), + ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ) + ); - check_annotation_parsing(l_expected, l_parsing_result); + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure ignore_wrapped_package is - l_pck_annotation ut3.ut_annotation_parser.typ_annotated_package; + l_actual ut3.ut_annotations; pragma autonomous_transaction; begin - l_pck_annotation := ut3.ut_annotation_parser.get_package_annotations(user, 'TST_WRAPPED_PCK'); - ut.expect(l_pck_annotation.procedure_annotations.count).to_equal(0); - ut.expect(l_pck_annotation.package_annotations.count).to_equal(0); + l_actual := ut3.ut_annotation_parser.get_package_annotations(user, 'TST_WRAPPED_PCK'); + + ut.expect(l_actual.count).to_equal(0); end; @@ -381,8 +322,8 @@ END; procedure brackets_in_desc is l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; l_ann_param ut3.ut_annotation_parser.typ_annotation_param := null; --l_results ut_expectation_results; begin @@ -391,18 +332,20 @@ END; END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := 'Name of suite (including some brackets) and some more text'; + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', 'Name of suite (including some brackets) and some more text', null ) + ); - check_annotation_parsing(l_expected, l_parsing_result); + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure test_space_Before_Annot_Params is l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin @@ -418,21 +361,22 @@ END;'; END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('suitepath').text := 'all.globaltests'; + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', null, null ), + ut3.ut_annotation( 2, 'suitepath', 'all.globaltests', null ) + ); - check_annotation_parsing(l_expected, l_parsing_result); + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure test_windows_newline as - l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; + l_source clob; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; begin l_source := 'PACKAGE test_tt AS -- %suite @@ -441,20 +385,23 @@ END;'; END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('displayname').text := 'Name of suite'; - l_expected.package_annotations('suitepath').text := 'all.globaltests'; - check_annotation_parsing(l_expected, l_parsing_result); + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', null, null ), + ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; procedure test_annot_very_long_name as l_source clob; - l_parsing_result ut3.ut_annotation_parser.typ_annotated_package; - l_expected ut3.ut_annotation_parser.typ_annotated_package; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS @@ -467,16 +414,17 @@ END;'; END;'; --Act - l_parsing_result := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); --Assert - l_expected.package_annotations('suite').text := null; - l_expected.package_annotations('displayname').text := 'Name of suite'; - l_expected.package_annotations('suitepath').text := 'all.globaltests'; - l_expected.procedure_annotations(1).name := 'very_long_procedure_name_valid_for_oracle_12_so_utplsql_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_ditit'; - l_expected.procedure_annotations(1).annotations('test').text := null; - - check_annotation_parsing(l_expected, l_parsing_result); + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', null, null ), + ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ), + ut3.ut_annotation( 4, 'test', null, 'very_long_procedure_name_valid_for_oracle_12_so_utplsql_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_ditit' ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; From df2c76388a0b346c2a7f1fb4f495b5ffd51727e8 Mon Sep 17 00:00:00 2001 From: Jacek Date: Sun, 8 Oct 2017 23:44:41 +0100 Subject: [PATCH 07/19] Added cache mechanism to `ut_annotation_parser`. Changed the way schema is scanned, so that all annotations/sources are scanned with single execution of a query. TODO - change the way annotations are parsed, so that we can scan for "floating" annotations. Annotations that are on top of suite, but associated with procedure, are not valid suite-level annotations. This is why the package `old_tests/helpers/test_reporters` was updated. --- old_tests/helpers/test_reporters.pks | 1 + ...e_manager.CacheInvalidaesOnPackageDrop.sql | 4 +- .../core/annotations/ut_annotated_object.tps | 8 + .../core/annotations/ut_annotated_objects.tps | 3 + .../core/annotations/ut_annotation_cache.sql | 14 + .../annotations/ut_annotation_cache_info.sql | 12 + .../ut_annotation_cache_manager.pkb | 47 + .../ut_annotation_cache_manager.pks | 6 + .../annotations/ut_annotation_cache_seq.sql | 2 + .../core/annotations/ut_annotation_parser.pkb | 133 ++- .../core/annotations/ut_annotation_parser.pks | 16 + source/core/ut_suite_manager.pkb | 1029 +++++++++-------- source/core/ut_suite_manager.pks | 5 +- source/core/ut_utils.pkb | 6 + source/core/ut_utils.pks | 2 + .../create_synonyms_and_grants_for_public.sql | 9 + .../create_synonyms_and_grants_for_user.sql | 8 + source/install.sql | 7 + source/uninstall.sql | 14 +- test/ut_suite_manager/test_suite_manager.pkb | 388 +++---- 20 files changed, 998 insertions(+), 716 deletions(-) create mode 100644 source/core/annotations/ut_annotated_object.tps create mode 100644 source/core/annotations/ut_annotated_objects.tps create mode 100644 source/core/annotations/ut_annotation_cache.sql create mode 100644 source/core/annotations/ut_annotation_cache_info.sql create mode 100644 source/core/annotations/ut_annotation_cache_manager.pkb create mode 100644 source/core/annotations/ut_annotation_cache_manager.pks create mode 100644 source/core/annotations/ut_annotation_cache_seq.sql diff --git a/old_tests/helpers/test_reporters.pks b/old_tests/helpers/test_reporters.pks index be3f6dde2..ae040bbef 100644 --- a/old_tests/helpers/test_reporters.pks +++ b/old_tests/helpers/test_reporters.pks @@ -2,6 +2,7 @@ create or replace package test_reporters as --%suite(A suite for testing different outcomes from reporters) --%suitepath(org.utplsql.utplsql.test) + --%beforeall procedure beforeall; diff --git a/old_tests/ut_suite_manager/ut_suite_manager.CacheInvalidaesOnPackageDrop.sql b/old_tests/ut_suite_manager/ut_suite_manager.CacheInvalidaesOnPackageDrop.sql index 63a449f49..edad12a77 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.CacheInvalidaesOnPackageDrop.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.CacheInvalidaesOnPackageDrop.sql @@ -34,12 +34,12 @@ begin select * bulk collect into l_test_report from table(ut.run(user || '.tst_package_to_be_dropped')); exception when others then - if sqlerrm like '%tst_package_to_be_dropped%does not exist%' then + if sqlerrm like '%tst_package_to_be_dropped%not found%' then :test_result := ut_utils.tr_success; end if; end; if :test_result != ut_utils.tr_success or :test_result is null then - dbms_output.put_line('Failed: Expected exception with text like ''%tst_package_to_be_dropped%does not exist%'' but got:''' || + dbms_output.put_line('Failed: Expected exception with text like ''%tst_package_to_be_dropped%not found%'' but got:''' || sqlerrm || ''''); end if; end; diff --git a/source/core/annotations/ut_annotated_object.tps b/source/core/annotations/ut_annotated_object.tps new file mode 100644 index 000000000..bcfcb6cda --- /dev/null +++ b/source/core/annotations/ut_annotated_object.tps @@ -0,0 +1,8 @@ +create type ut_annotated_object as object( + object_owner varchar2(250), + object_name varchar2(250), + object_type varchar2(50), + annotations ut_annotations +) +/ + diff --git a/source/core/annotations/ut_annotated_objects.tps b/source/core/annotations/ut_annotated_objects.tps new file mode 100644 index 000000000..991a8aa1d --- /dev/null +++ b/source/core/annotations/ut_annotated_objects.tps @@ -0,0 +1,3 @@ +create type ut_annotated_objects as table of ut_annotated_object +/ + diff --git a/source/core/annotations/ut_annotation_cache.sql b/source/core/annotations/ut_annotation_cache.sql new file mode 100644 index 000000000..c22b6c5f3 --- /dev/null +++ b/source/core/annotations/ut_annotation_cache.sql @@ -0,0 +1,14 @@ +create table ut_annotation_cache ( + cache_id number(20,0) not null, + annotation_position number(5,0) not null, + annotation_name varchar2(1000) not null, + annotation_text varchar2(4000), + subobject_name varchar2(250), +-- subobject_name_not_null varchar2(250) generated always as (nvl(subobject_name,'null')) not null, + constraint ut_annotation_cache_pk primary key(cache_id, annotation_position), +-- constraint ut_annotation_cache_pk primary key(cache_id, annotation_position, subobject_name_not_null), + constraint ut_annotation_cache_fk foreign key(cache_id) references ut_annotation_cache_info(cache_id) on delete cascade +); + +create index ut_annotation_cache_fk on ut_annotation_cache(cache_id); + diff --git a/source/core/annotations/ut_annotation_cache_info.sql b/source/core/annotations/ut_annotation_cache_info.sql new file mode 100644 index 000000000..b8d414b5b --- /dev/null +++ b/source/core/annotations/ut_annotation_cache_info.sql @@ -0,0 +1,12 @@ +create table ut_annotation_cache_info ( + cache_id number(20,0) not null, + object_owner varchar2(250) not null, + object_name varchar2(250) not null, + object_type varchar2(250) not null, + parse_time date not null, + is_annotated varchar2(1) not null, + constraint ut_annotation_cache_info_pk primary key(cache_id), + constraint ut_annotation_cache_info_uk unique (object_owner, object_name, object_type), + constraint ut_annotation_cache_info_ck1 check (is_annotated in ('Y','N')) +) organization index; + diff --git a/source/core/annotations/ut_annotation_cache_manager.pkb b/source/core/annotations/ut_annotation_cache_manager.pkb new file mode 100644 index 000000000..3db1271b9 --- /dev/null +++ b/source/core/annotations/ut_annotation_cache_manager.pkb @@ -0,0 +1,47 @@ +create or replace package body ut_annotation_cache_manager as + + procedure update_cache(a_object ut_annotated_object, a_cache_id integer) is + l_is_annotated varchar2(1); + v_cache_id integer := a_cache_id; + l_current_schema varchar2(250) := ut_utils.ut_owner; + pragma autonomous_transaction; + begin + if a_object.annotations is not null and a_object.annotations.count > 0 then + l_is_annotated := 'Y'; + else + l_is_annotated := 'N'; + end if; + + if v_cache_id is not null then + update ut_annotation_cache_info i + set i.parse_time = sysdate, + i.is_annotated = l_is_annotated + where i.cache_id = v_cache_id; + else + insert into ut_annotation_cache_info + (cache_id, object_owner, object_name, object_type, parse_time, is_annotated) + values (ut_annotation_cache_seq.nextval, a_object.object_owner, a_object.object_name, a_object.object_type, sysdate, l_is_annotated) + returning cache_id into v_cache_id; + end if; + + delete from ut_annotation_cache c + where cache_id = v_cache_id; + + if l_is_annotated = 'Y' then +-- begin + insert into ut_annotation_cache + (cache_id, annotation_position, annotation_name, annotation_text, subobject_name) + select v_cache_id, a.position, a.name, a.text, a.subobject_name + from table(a_object.annotations) a; + --TODO - duplicate annotations found?? - should not happen, getting standalone annotations need to happen after procedure annotations were parsed +-- exception +-- when others then +-- dbms_output.put_line(xmltype(anydata.convertCollection(a_object.annotations)).getclobval); +-- raise; +-- end; + end if; + commit; + end; + +end; +/ diff --git a/source/core/annotations/ut_annotation_cache_manager.pks b/source/core/annotations/ut_annotation_cache_manager.pks new file mode 100644 index 000000000..9462baf54 --- /dev/null +++ b/source/core/annotations/ut_annotation_cache_manager.pks @@ -0,0 +1,6 @@ +create or replace package ut_annotation_cache_manager authid definer as + + procedure update_cache(a_object ut_annotated_object, a_cache_id integer); + +end; +/ diff --git a/source/core/annotations/ut_annotation_cache_seq.sql b/source/core/annotations/ut_annotation_cache_seq.sql new file mode 100644 index 000000000..0827892a7 --- /dev/null +++ b/source/core/annotations/ut_annotation_cache_seq.sql @@ -0,0 +1,2 @@ +create sequence ut_annotation_cache_seq cache 20 +/ diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index 360dd12d4..b5f33d2ba 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -230,15 +230,138 @@ create or replace package body ut_annotation_parser as from table(l_annotations) x order by x.position; - -- printing out parsed structure for debugging - $if $$ut_trace $then - print_parse_results(l_result); - $end +-- -- printing out parsed structure for debugging +-- $if $$ut_trace $then +-- print_parse_results(l_result); +-- $end return l_result; end parse_package_annotations; - ------------------------------ + function get_post_processed_source(a_source_lines ut_varchar2_rows) return clob is + l_lines sys.dbms_preprocessor.source_lines_t; + l_source clob; + begin + --convert to preprocessor lines + for i in 1 .. a_source_lines.count loop + l_lines(i) := a_source_lines(i); + end loop; + --get post-processed source + l_lines := sys.dbms_preprocessor.get_post_processed_source(l_lines); + --convert to clob + for i in 1..l_lines.count loop + ut_utils.append_to_clob(l_source, replace(l_lines(i), chr(13)||chr(10), chr(10))); + end loop; + return l_source; + end; + + ------------------------------------------------------------ --public definitions + ------------------------------------------------------------ + + function parse_annotations(a_cursor t_object_sources_cur) return ut_annotated_objects pipelined is + l_rec t_object_source; + l_source clob; + l_annotations ut_annotations; + l_result ut_annotated_object; + ex_package_is_wrapped exception; + pragma exception_init(ex_package_is_wrapped, -24241); + + begin + if not a_cursor% isopen then + return; + end if; + loop + + fetch a_cursor into l_rec; + exit when a_cursor%notfound; + begin + --convert to post-processed source clob + l_source := get_post_processed_source(l_rec.lines); + --parse annotations + l_annotations := parse_package_annotations(l_source); + dbms_lob.freetemporary(l_source); + exception + when ex_package_is_wrapped then + null; + end; + --convert to query results + l_result := ut_annotated_object( l_rec.owner, l_rec.name, l_rec.type, l_annotations); + + ut_annotation_cache_manager.update_cache(l_result, l_rec.cache_id); + if l_annotations is not empty then + pipe row (l_result); + end if; + end loop; + close a_cursor; + return; + end; + + + function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2) return ut_annotated_objects pipelined is + l_objects_view varchar2(200) := ut_metadata.get_dba_view('dba_objects'); + l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source'); + l_obj ut_annotated_object; + l_current_schema varchar2(250) := ut_utils.ut_owner; + l_cursor sys_refcursor; + l_cursor_sql varchar2(32767); + begin + l_cursor_sql := + q'[with object_cache_info + as (select /*+ cardinality(i 10000) */ o.owner as object_owner, o.object_name, o.object_type, i.cache_id, + case when o.last_ddl_time < i.parse_time then 'N' else 'Y' end as cache_stale + from ]'||l_objects_view||q'[ o + left join ]'||l_current_schema||q'[.ut_annotation_cache_info i + on o.owner = i.object_owner and o.object_name = i.object_name and o.object_type = i.object_type + where o.object_type = :a_object_type and o.status = 'VALID' and o.owner = :a_object_owner + ), + obj_info_with_source + as (select /*+ cardinality(o 10000) */o.object_owner, o.object_name, o.object_type, o.cache_stale, o.cache_id, s.line, s.text + from object_cache_info o + left join ]'||l_sources_view||q'[ s + on s.name = o.object_name + and s.type = :a_object_type + and s.owner = :a_object_owner + and o.cache_stale = 'Y' + ), + obj_info_source_grouped + as (select o.object_owner, o.object_name, o.object_type, o.cache_stale, o.cache_id, + cast(collect(o.text order by o.line) as ]'||l_current_schema||q'[.ut_varchar2_rows) as texts + from obj_info_with_source o + group by o.object_owner, o.object_type, o.object_name, o.cache_stale, cache_id + order by o.object_owner, o.object_type, o.object_name + ) + select obj + from ( + select ]'||l_current_schema||q'[.ut_annotated_object(o.object_owner, o.object_name, o.object_type, + cast(collect( + ]'||l_current_schema||q'[.ut_annotation(c.annotation_position, c.annotation_name, c.annotation_text, c.subobject_name) order by c.annotation_position ) + as ]'||l_current_schema||q'[.ut_annotations) + ) as obj + from obj_info_source_grouped o + join ]'||l_current_schema||q'[.ut_annotation_cache c + on o.cache_id = c.cache_id + where o.cache_stale = 'N' + group by o.object_owner, o.object_name, o.object_type + union all + -- this query needs to be executed as last part of the union + -- as it is updating the cache_stale flag in an autonomous transaction + select value(c) as obj + from table( + ]'||l_current_schema||q'[.ut_annotation_parser.parse_annotations( + cursor(select o.object_owner, o.object_name, o.object_type, o.cache_id, o.texts from obj_info_source_grouped o where o.cache_stale = 'Y') + ) + ) c + ) a + order by a.obj.object_owner, a.obj.object_type, a.obj.object_name]'; + open l_cursor for l_cursor_sql using a_object_type, a_object_owner, a_object_type, a_object_owner; + loop + fetch l_cursor into l_obj; + exit when l_cursor%notfound; + pipe row (l_obj); + end loop; + close l_cursor; + return; + end; function get_package_annotations(a_owner_name varchar2, a_name varchar2) return ut_annotations is l_source clob; diff --git a/source/core/annotations/ut_annotation_parser.pks b/source/core/annotations/ut_annotation_parser.pks index a54fd9668..75867f565 100644 --- a/source/core/annotations/ut_annotation_parser.pks +++ b/source/core/annotations/ut_annotation_parser.pks @@ -46,11 +46,25 @@ create or replace package ut_annotation_parser authid current_user as */ type tt_annotation_params is table of typ_annotation_param index by pls_integer; + type t_object_source is record( + owner varchar2(250), + name varchar2(250), + type varchar2(50), + cache_id integer, + lines ut_varchar2_rows + ); + + type t_object_sources_cur is ref cursor return t_object_source; + /* INTERNAL USE ONLY */ function parse_package_annotations(a_source clob) return ut_annotations; + function parse_annotations(a_cursor t_object_sources_cur) return ut_annotated_objects pipelined; + + function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2) return ut_annotated_objects pipelined; + /* function: get_package_annotations @@ -58,6 +72,8 @@ create or replace package ut_annotation_parser authid current_user as */ function get_package_annotations(a_owner_name varchar2, a_name varchar2) return ut_annotations; + function get_post_processed_source(a_source_lines ut_varchar2_rows) return clob; + /* function: parse_annotation_params diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index b6bbc7322..fcb62fb68 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -1,569 +1,574 @@ - create or replace package body ut_suite_manager is - /* - utPLSQL - Version X.X.X.X - Copyright 2016 - 2017 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. - */ - - type t_schema_info is record (changed_at date, obj_cnt integer); - - type tt_schema_suites is table of ut_logical_suite index by varchar2(4000 char); - type t_schema_cache is record( - schema_suites tt_schema_suites - ,changed_at date - ,obj_cnt integer); - type tt_schema_suites_list is table of t_schema_cache index by varchar2(128 char); - - g_schema_suites tt_schema_suites_list; - - function trim_path(a_path varchar2, a_part varchar2) return varchar2 is - begin - return substr(a_path, nvl(length(a_part), 0) + 1); - end; - - function get_schema_info(a_owner_name varchar2) return t_schema_info is - l_info t_schema_info; - l_view_name varchar2(200) := ut_metadata.get_dba_view('all_objects'); - begin - execute immediate q'[ - select nvl(max(t.last_ddl_time), date '4999-12-31'), count(*) - from ]'||l_view_name||q'[ t - where t.owner = :a_owner_name - and t.object_type in ('PACKAGE')]' - into l_info using a_owner_name; - return l_info; - end; - - function config_package(a_owner_name varchar2, a_object_name varchar2) return ut_logical_suite is - l_annotation_data ut_annotations; - l_is_suite boolean := false; - l_is_test boolean := false; - l_suite_disabled boolean := false; - l_test_disabled boolean := false; - l_suite_items ut_suite_items := ut_suite_items(); - l_suite_name ut_annotation_parser.t_annotation_name; - - l_default_setup_proc varchar2(250 char); - l_default_teardown_proc varchar2(250 char); - l_suite_setup_proc varchar2(250 char); - l_suite_teardown_proc varchar2(250 char); - l_suite_path varchar2(4000 char); - - l_proc_name ut_annotation_parser.t_procedure_name; - - l_owner_name varchar2(250 char); - l_object_name varchar2(250 char); - l_suite ut_logical_suite; - l_test ut_test; - - l_suite_rollback integer; - - l_beforetest_procedure varchar2(250 char); - l_aftertest_procedure varchar2(250 char); - l_rollback_type integer; - l_displayname varchar2(4000); - - e_insufficient_priv exception; - pragma exception_init(e_insufficient_priv,-01031); - begin - l_owner_name := a_owner_name; - l_object_name := a_object_name; - begin - ut_metadata.do_resolve(a_owner => l_owner_name, a_object => l_object_name); - exception - when e_insufficient_priv then - return null; - end; - l_annotation_data := ut_annotation_parser.get_package_annotations(a_owner_name => l_owner_name, a_name => l_object_name); - - l_suite_rollback := ut_utils.gc_rollback_auto; - for i in 1 .. l_annotation_data.count loop - - if l_annotation_data(i).subobject_name is null then - - if l_annotation_data(i).name in ('suite','displayname') then - l_suite_name := l_annotation_data(i).text; - if l_annotation_data(i).name = 'suite' then - l_is_suite := true; - end if; - elsif l_annotation_data(i).name = 'disabled' then - l_suite_disabled := true; - elsif l_annotation_data(i).name = 'suitepath' and l_annotation_data(i).text is not null then - l_suite_path := l_annotation_data(i).text || '.' || lower(l_object_name); - elsif l_annotation_data(i).name = 'rollback' then - if lower(l_annotation_data(i).text) = 'manual' then - l_suite_rollback := ut_utils.gc_rollback_manual; - else - l_suite_rollback := ut_utils.gc_rollback_auto; - end if; +create or replace package body ut_suite_manager is + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ + + type t_schema_info is record (changed_at date, obj_cnt integer); + + type tt_schema_suites is table of ut_logical_suite index by varchar2(4000 char); + type t_schema_cache is record( + schema_suites tt_schema_suites + ,changed_at date + ,obj_cnt integer); + type tt_schema_suites_list is table of t_schema_cache index by varchar2(128 char); + + g_schema_suites tt_schema_suites_list; + + type t_object_suite_path is table of varchar2(4000) index by varchar2(4000 char); + type t_schema_suite_paths is table of t_object_suite_path index by varchar2(250); + + g_schema_object_path_map t_schema_suite_paths; + + ------------------ + + function trim_path(a_path varchar2, a_part varchar2) return varchar2 is + begin + return substr(a_path, nvl(length(a_part), 0) + 1); + end; + + function get_schema_info(a_owner_name varchar2) return t_schema_info is + l_info t_schema_info; + l_view_name varchar2(200) := ut_metadata.get_dba_view('dba_objects'); + begin + execute immediate q'[ + select nvl(max(t.last_ddl_time), date '4999-12-31'), count(*) + from ]'||l_view_name||q'[ t + where t.owner = :a_owner_name + and t.object_type in ('PACKAGE')]' + into l_info using a_owner_name; + return l_info; + end; + + function config_package(a_object ut_annotated_object) return ut_logical_suite is + l_is_suite boolean := false; + l_is_test boolean := false; + l_suite_disabled boolean := false; + l_test_disabled boolean := false; + l_suite_items ut_suite_items := ut_suite_items(); + l_suite_name ut_annotation_parser.t_annotation_name; + + l_default_setup_proc varchar2(250 char); + l_default_teardown_proc varchar2(250 char); + l_suite_setup_proc varchar2(250 char); + l_suite_teardown_proc varchar2(250 char); + l_suite_path varchar2(4000 char); + + l_proc_name ut_annotation_parser.t_procedure_name; + + l_suite ut_logical_suite; + l_test ut_test; + + l_suite_rollback integer; + + l_beforetest_procedure varchar2(250 char); + l_aftertest_procedure varchar2(250 char); + l_rollback_type integer; + l_displayname varchar2(4000); + + e_insufficient_priv exception; + pragma exception_init(e_insufficient_priv,-01031); + begin + l_suite_rollback := ut_utils.gc_rollback_auto; + for i in 1 .. a_object.annotations.count loop + + if a_object.annotations(i).subobject_name is null then + + if a_object.annotations(i).name in ('suite','displayname') then + l_suite_name := a_object.annotations(i).text; + if a_object.annotations(i).name = 'suite' then + l_is_suite := true; end if; - - elsif l_is_suite then - - l_proc_name := l_annotation_data(i).subobject_name; - - if l_annotation_data(i).name = 'beforeeach' and l_default_setup_proc is null then - l_default_setup_proc := l_proc_name; - elsif l_annotation_data(i).name = 'aftereach' and l_default_teardown_proc is null then - l_default_teardown_proc := l_proc_name; - elsif l_annotation_data(i).name = 'beforeall' and l_suite_setup_proc is null then - l_suite_setup_proc := l_proc_name; - elsif l_annotation_data(i).name = 'afterall' and l_suite_teardown_proc is null then - l_suite_teardown_proc := l_proc_name; - - - elsif l_annotation_data(i).name = 'disabled' then - l_test_disabled := true; - elsif l_annotation_data(i).name = 'beforetest' then - l_beforetest_procedure := l_annotation_data(i).text; - elsif l_annotation_data(i).name = 'aftertest' then - l_aftertest_procedure := l_annotation_data(i).text; - elsif l_annotation_data(i).name in ('displayname','test') then - l_displayname := l_annotation_data(i).text; - if l_annotation_data(i).name = 'test' then - l_is_test := true; - end if; - elsif l_annotation_data(i).name = 'rollback' then - if lower(l_annotation_data(i).text) = 'manual' then - l_rollback_type := ut_utils.gc_rollback_manual; - elsif lower(l_annotation_data(i).text) = 'auto' then - l_rollback_type := ut_utils.gc_rollback_auto; - end if; + elsif a_object.annotations(i).name = 'disabled' then + l_suite_disabled := true; + elsif a_object.annotations(i).name = 'suitepath' and a_object.annotations(i).text is not null then + l_suite_path := a_object.annotations(i).text || '.' || lower(a_object.object_name); + elsif a_object.annotations(i).name = 'rollback' then + if lower(a_object.annotations(i).text) = 'manual' then + l_suite_rollback := ut_utils.gc_rollback_manual; + else + l_suite_rollback := ut_utils.gc_rollback_auto; end if; + end if; - if l_is_test - and (i = l_annotation_data.count or l_proc_name != nvl(l_annotation_data(i+1).subobject_name, ' ') ) then - l_suite_items.extend; - l_suite_items(l_suite_items.last) := - ut_test(a_object_owner => l_owner_name - ,a_object_name => l_object_name - ,a_name => l_proc_name - ,a_description => l_displayname - ,a_rollback_type => coalesce(l_rollback_type, l_suite_rollback) - ,a_disabled_flag => l_suite_disabled or l_test_disabled - ,a_before_test_proc_name => l_beforetest_procedure - ,a_after_test_proc_name => l_aftertest_procedure); - - l_is_test := false; - l_test_disabled := false; - l_aftertest_procedure := null; - l_beforetest_procedure := null; - l_rollback_type := null; + elsif l_is_suite then + + l_proc_name := a_object.annotations(i).subobject_name; + + if a_object.annotations(i).name = 'beforeeach' and l_default_setup_proc is null then + l_default_setup_proc := l_proc_name; + elsif a_object.annotations(i).name = 'aftereach' and l_default_teardown_proc is null then + l_default_teardown_proc := l_proc_name; + elsif a_object.annotations(i).name = 'beforeall' and l_suite_setup_proc is null then + l_suite_setup_proc := l_proc_name; + elsif a_object.annotations(i).name = 'afterall' and l_suite_teardown_proc is null then + l_suite_teardown_proc := l_proc_name; + + + elsif a_object.annotations(i).name = 'disabled' then + l_test_disabled := true; + elsif a_object.annotations(i).name = 'beforetest' then + l_beforetest_procedure := a_object.annotations(i).text; + elsif a_object.annotations(i).name = 'aftertest' then + l_aftertest_procedure := a_object.annotations(i).text; + elsif a_object.annotations(i).name in ('displayname','test') then + l_displayname := a_object.annotations(i).text; + if a_object.annotations(i).name = 'test' then + l_is_test := true; + end if; + elsif a_object.annotations(i).name = 'rollback' then + if lower(a_object.annotations(i).text) = 'manual' then + l_rollback_type := ut_utils.gc_rollback_manual; + elsif lower(a_object.annotations(i).text) = 'auto' then + l_rollback_type := ut_utils.gc_rollback_auto; end if; - end if; - end loop; - - if l_is_suite then - l_suite := ut_suite ( - a_object_owner => l_owner_name, - a_object_name => l_object_name, - a_name => l_object_name, --this could be different for sub-suite (context) - a_path => l_suite_path, --a patch for this suite (excluding the package name of current suite) - a_description => l_suite_name, - a_rollback_type => l_suite_rollback, - a_disabled_flag => l_suite_disabled, - a_before_all_proc_name => l_suite_setup_proc, - a_after_all_proc_name => l_suite_teardown_proc - ); - for i in 1 .. l_suite_items.count loop - l_test := treat(l_suite_items(i) as ut_test); - l_test.set_beforeeach(l_default_setup_proc); - l_test.set_aftereach(l_default_teardown_proc); - l_test.path := l_suite.path || '.' || l_test.name; - l_suite.add_item(l_test); - end loop; - end if; - - return l_suite; - end config_package; + if l_is_test + and (i = a_object.annotations.count + or l_proc_name != nvl(a_object.annotations(i+1).subobject_name, ' ') ) then + l_suite_items.extend; + l_suite_items(l_suite_items.last) := + ut_test(a_object_owner => a_object.object_owner + ,a_object_name => a_object.object_name + ,a_name => l_proc_name + ,a_description => l_displayname + ,a_rollback_type => coalesce(l_rollback_type, l_suite_rollback) + ,a_disabled_flag => l_suite_disabled or l_test_disabled + ,a_before_test_proc_name => l_beforetest_procedure + ,a_after_test_proc_name => l_aftertest_procedure); + + l_is_test := false; + l_test_disabled := false; + l_aftertest_procedure := null; + l_beforetest_procedure := null; + l_rollback_type := null; + end if; - procedure update_cache(a_owner_name varchar2, a_schema_suites tt_schema_suites, a_total_obj_cnt integer) is - begin - if a_schema_suites.count > 0 then - g_schema_suites(a_owner_name).schema_suites := a_schema_suites; - g_schema_suites(a_owner_name).changed_at := sysdate; - g_schema_suites(a_owner_name).obj_cnt := a_total_obj_cnt; - elsif g_schema_suites.exists(a_owner_name) then - g_schema_suites.delete(a_owner_name); end if; - end; - - procedure config_schema(a_owner_name varchar2) is - l_suite ut_logical_suite; - - l_all_suites tt_schema_suites; - l_ind varchar2(4000 char); - l_path varchar2(4000 char); - l_root varchar2(4000 char); - l_root_suite ut_logical_suite; - - type t_object_name is record( - owner all_objects.owner%type, - object_name all_objects.object_name%type + end loop; + + if l_is_suite then + l_suite := ut_suite ( + a_object_owner => a_object.object_owner, + a_object_name => a_object.object_name, + a_name => a_object.object_name, --this could be different for sub-suite (context) + a_path => l_suite_path, --a patch for this suite (excluding the package name of current suite) + a_description => l_suite_name, + a_rollback_type => l_suite_rollback, + a_disabled_flag => l_suite_disabled, + a_before_all_proc_name => l_suite_setup_proc, + a_after_all_proc_name => l_suite_teardown_proc ); - type t_object_names is table of t_object_name; - - l_object_names t_object_names; - l_view_name varchar2(200) := ut_metadata.get_dba_view('dba_objects'); - - l_schema_suites tt_schema_suites; - - procedure put(a_root_suite in out nocopy ut_logical_suite, a_path varchar2, a_suite ut_logical_suite, a_parent_path varchar2 default null) is - l_temp_root varchar2(4000 char); - l_path varchar2(4000 char); - l_cur_item ut_logical_suite; - l_ind pls_integer; - begin - if a_path like '%.%' then - l_temp_root := regexp_substr(a_path, '^[^.]+'); - l_path := ltrim(a_parent_path || '.' || l_temp_root, '.'); - - if a_root_suite is not null then + for i in 1 .. l_suite_items.count loop + l_test := treat(l_suite_items(i) as ut_test); + l_test.set_beforeeach(l_default_setup_proc); + l_test.set_aftereach(l_default_teardown_proc); + l_test.path := l_suite.path || '.' || l_test.name; + l_suite.add_item(l_test); + end loop; + end if; + + return l_suite; + + end config_package; + + function build_suites(a_cursor sys_refcursor) return ut_suite_items pipelined is + l_object ut_annotated_object; + begin + loop + fetch a_cursor into l_object; + exit when a_cursor%notfound; + pipe row (config_package(l_object)); + end loop; + close a_cursor; + return; + end; + + procedure update_cache(a_owner_name varchar2, a_schema_suites tt_schema_suites, a_total_obj_cnt integer) is + begin + if a_schema_suites.count > 0 then + g_schema_suites(a_owner_name).schema_suites := a_schema_suites; + g_schema_suites(a_owner_name).changed_at := sysdate; + g_schema_suites(a_owner_name).obj_cnt := a_total_obj_cnt; + elsif g_schema_suites.exists(a_owner_name) then + g_schema_suites.delete(a_owner_name); + end if; + end; + + procedure config_schema(a_owner_name varchar2) is + l_suite ut_logical_suite; + + l_all_suites tt_schema_suites; + l_ind varchar2(4000 char); + l_path varchar2(4000 char); + l_root varchar2(4000 char); + l_root_suite ut_logical_suite; + + type t_object_name is record( + owner all_objects.owner%type, + object_name all_objects.object_name%type + ); + type t_object_names is table of t_object_name; + + l_object_names t_object_names; + l_view_name varchar2(200) := ut_metadata.get_dba_view('dba_objects'); + + l_schema_suites tt_schema_suites; + + procedure put(a_root_suite in out nocopy ut_logical_suite, a_path varchar2, a_suite ut_logical_suite, a_parent_path varchar2 default null) is + l_temp_root varchar2(4000 char); + l_path varchar2(4000 char); + l_cur_item ut_logical_suite; + l_ind pls_integer; + begin + if a_path like '%.%' then + l_temp_root := regexp_substr(a_path, '^[^.]+'); + l_path := ltrim(a_parent_path || '.' || l_temp_root, '.'); - l_ind := a_root_suite.item_index(l_temp_root); + if a_root_suite is not null then - if l_ind is null then - --this only happens when a path of a real suite contains a parent-suite that is not a real package. - l_cur_item := ut_logical_suite(a_object_owner => a_owner_name, a_object_name => l_temp_root, a_name => l_temp_root, a_path => l_path); - else - l_cur_item := treat(a_root_suite.items(l_ind) as ut_logical_suite); - end if; + l_ind := a_root_suite.item_index(l_temp_root); - put(l_cur_item, trim_path(a_path, l_temp_root || '.'), a_suite, l_path); + if l_ind is null then + --this only happens when a path of a real suite contains a parent-suite that is not a real package. + l_cur_item := ut_logical_suite(a_object_owner => a_owner_name, a_object_name => l_temp_root, a_name => l_temp_root, a_path => l_path); + else + l_cur_item := treat(a_root_suite.items(l_ind) as ut_logical_suite); + end if; - if l_ind is null then - a_root_suite.add_item(l_cur_item); - else - a_root_suite.items(l_ind) := l_cur_item; - end if; + put(l_cur_item, trim_path(a_path, l_temp_root || '.'), a_suite, l_path); + if l_ind is null then + a_root_suite.add_item(l_cur_item); else - a_root_suite := ut_logical_suite(a_object_owner => a_owner_name, a_object_name => l_temp_root, a_name => l_temp_root, a_path => l_path); - put(a_root_suite, trim_path(a_path, l_temp_root || '.'), a_suite, l_path); + a_root_suite.items(l_ind) := l_cur_item; end if; + else - if a_root_suite is not null then - a_root_suite.add_item(a_suite); - else - a_root_suite := a_suite; - end if; + a_root_suite := ut_logical_suite(a_object_owner => a_owner_name, a_object_name => l_temp_root, a_name => l_temp_root, a_path => l_path); + put(a_root_suite, trim_path(a_path, l_temp_root || '.'), a_suite, l_path); end if; - end; - - $if $$ut_trace $then - procedure print(a_item ut_suite_item, a_pad pls_integer) is - l_suite ut_logical_suite; - l_pad varchar2(1000) := lpad(' ', a_pad, ' '); - begin - if a_item is of (ut_logical_suite) then - dbms_output.put_line(l_pad || 'Suite: ' || a_item.name || '(' || a_item.path || ')'); - dbms_output.put_line(l_pad || 'Items: '); - l_suite := treat(a_item as ut_logical_suite); - for i in 1 .. l_suite.items.count loop - print(l_suite.items(i), a_pad + 2); - end loop; + else + if a_root_suite is not null then + a_root_suite.add_item(a_suite); else - dbms_output.put_line(l_pad || 'Test: ' || a_item.name || '(' || a_item.path || ')' ); + a_root_suite := a_suite; end if; - end print; - $end + end if; + end; + $if $$ut_trace $then + procedure print(a_item ut_suite_item, a_pad pls_integer) is + l_suite ut_logical_suite; + l_pad varchar2(1000) := lpad(' ', a_pad, ' '); begin - -- form the single-dimension list of suites constructed from parsed packages - execute immediate - 'select t.owner, t.object_name from '||l_view_name||' t ' - ||q'[ where t.owner = :a_owner_name and t.status = 'VALID' and t.object_type in ('PACKAGE')]' - bulk collect into l_object_names using a_owner_name; - for i in 1 .. cardinality(l_object_names) loop - -- parse the source of the package - l_suite := config_package(l_object_names(i).owner, l_object_names(i).object_name); - - if l_suite is not null then - l_all_suites(l_suite.path) := l_suite; - end if; - - end loop; - - l_schema_suites.delete; - - -- Restructure single-dimenstion list into hierarchy of suites by the value of %suitepath attribute value - -- All root suite compose the root-suite list of the schema - l_ind := l_all_suites.first; - while l_ind is not null loop - - l_root := regexp_substr(l_ind, '^[^.]+'); - - if l_schema_suites.exists(l_root) then - l_root_suite := l_schema_suites(l_root); - l_path := trim_path(l_ind, l_root || '.'); - else - l_root_suite := null; - l_path := l_ind; - end if; - put(l_root_suite, l_path, l_all_suites(l_ind)); - - l_schema_suites(l_root) := l_root_suite; - - l_ind := l_all_suites.next(l_ind); - end loop; - - -- Caching - update_cache(a_owner_name, l_schema_suites, get_schema_info(a_owner_name).obj_cnt ); + if a_item is of (ut_logical_suite) then + dbms_output.put_line(l_pad || 'Suite: ' || a_item.name || '(' || a_item.path || ')'); + dbms_output.put_line(l_pad || 'Items: '); + l_suite := treat(a_item as ut_logical_suite); + for i in 1 .. l_suite.items.count loop + print(l_suite.items(i), a_pad + 2); + end loop; + else + dbms_output.put_line(l_pad || 'Test: ' || a_item.name || '(' || a_item.path || ')' ); + end if; + end print; + $end + + begin + g_schema_object_path_map.delete(a_owner_name); + -- form the single-dimension list of suites constructed from parsed packages + for i in ( + select treat(value(t) as ut3.ut_logical_suite) suite + from table( + ut3.ut_suite_manager.build_suites( + cursor( select value(x) from table (ut3.ut_annotation_parser.get_annotated_objects(a_owner_name, 'PACKAGE'))x ) + ) + ) t + ) loop + if i.suite is not null then + l_all_suites(i.suite.path) := i.suite; + g_schema_object_path_map(a_owner_name)(i.suite.object_name) := i.suite.path; + end if; + end loop; - -- printing results for debugging purpose - $if $$ut_trace $then - l_ind := l_schema_suites.first; - while l_ind is not null loop - print(l_schema_suites(l_ind), 0); - l_ind := l_schema_suites.next(l_ind); - end loop; - $end + --build hierarchical structure of the suite + l_schema_suites.delete; - end config_schema; + -- Restructure single-dimenstion list into hierarchy of suites by the value of %suitepath attribute value + -- All root suite compose the root-suite list of the schema + l_ind := l_all_suites.first; + while l_ind is not null loop - function get_schema_suites(a_schema_name in varchar2) return tt_schema_suites is - l_schema_info t_schema_info; - begin - -- Currently cache invalidation on DDL is not implemented so schema is rescaned each time - l_schema_info := get_schema_info(a_schema_name); - if not g_schema_suites.exists(a_schema_name) or g_schema_suites(a_schema_name).changed_at <= l_schema_info.changed_at or - g_schema_suites(a_schema_name).obj_cnt != l_schema_info.obj_cnt then - ut_utils.debug_log('Rescanning schema ' || a_schema_name); - config_schema(a_schema_name); - end if; + l_root := regexp_substr(l_ind, '^[^.]+'); - if g_schema_suites.exists(a_schema_name) then - return g_schema_suites(a_schema_name).schema_suites; + if l_schema_suites.exists(l_root) then + l_root_suite := l_schema_suites(l_root); + l_path := trim_path(l_ind, l_root || '.'); else - return cast(null as tt_schema_suites); + l_root_suite := null; + l_path := l_ind; end if; - end get_schema_suites; - - function get_schema_ut_packages(a_schema_names ut_varchar2_rows) return ut_object_names is - l_schema_ut_packages ut_object_names := ut_object_names(); - l_schema_suites tt_schema_suites; - l_iter varchar2(4000); - procedure populate_suite_ut_packages(a_suite ut_logical_suite, a_packages in out nocopy ut_object_names) is - l_sub_suite ut_logical_suite; - begin - if a_suite is of (ut_suite) then - a_packages.extend; - a_packages(a_packages.last) := ut_object_name(a_suite.object_owner, a_suite.object_name); - end if; - for i in 1 .. a_suite.items.count loop - if a_suite.items(i) is of (ut_logical_suite) then - l_sub_suite := treat(a_suite.items(i) as ut_logical_suite); - populate_suite_ut_packages(l_sub_suite, a_packages); - end if; - end loop; - end; + put(l_root_suite, l_path, l_all_suites(l_ind)); + + l_schema_suites(l_root) := l_root_suite; + + l_ind := l_all_suites.next(l_ind); + end loop; + + -- Caching + update_cache(a_owner_name, l_schema_suites, get_schema_info(a_owner_name).obj_cnt ); + + -- printing results for debugging purpose + $if $$ut_trace $then + l_ind := l_schema_suites.first; + while l_ind is not null loop + print(l_schema_suites(l_ind), 0); + l_ind := l_schema_suites.next(l_ind); + end loop; + $end + + end config_schema; + + function get_schema_suites(a_schema_name in varchar2) return tt_schema_suites is + l_schema_info t_schema_info; + begin + -- Currently cache invalidation on DDL is not implemented so schema is rescaned each time + l_schema_info := get_schema_info(a_schema_name); + if not g_schema_suites.exists(a_schema_name) or g_schema_suites(a_schema_name).changed_at <= l_schema_info.changed_at or + g_schema_suites(a_schema_name).obj_cnt != l_schema_info.obj_cnt then + ut_utils.debug_log('Rescanning schema ' || a_schema_name); + config_schema(a_schema_name); + end if; + + if g_schema_suites.exists(a_schema_name) then + return g_schema_suites(a_schema_name).schema_suites; + else + return cast(null as tt_schema_suites); + end if; + end get_schema_suites; + + function get_schema_ut_packages(a_schema_names ut_varchar2_rows) return ut_object_names is + l_schema_ut_packages ut_object_names := ut_object_names(); + l_schema_suites tt_schema_suites; + l_iter varchar2(4000); + procedure populate_suite_ut_packages(a_suite ut_logical_suite, a_packages in out nocopy ut_object_names) is + l_sub_suite ut_logical_suite; begin - if a_schema_names is not null then - for i in 1 .. a_schema_names.count loop - l_schema_suites := get_schema_suites(a_schema_names(i)); - l_iter := l_schema_suites.first; - while l_iter is not null loop - populate_suite_ut_packages(l_schema_suites(l_iter), l_schema_ut_packages); - l_iter := l_schema_suites.next(l_iter); - end loop; - end loop; - l_schema_ut_packages := set(l_schema_ut_packages); + if a_suite is of (ut_suite) then + a_packages.extend; + a_packages(a_packages.last) := ut_object_name(a_suite.object_owner, a_suite.object_name); end if; - - return l_schema_ut_packages; + for i in 1 .. a_suite.items.count loop + if a_suite.items(i) is of (ut_logical_suite) then + l_sub_suite := treat(a_suite.items(i) as ut_logical_suite); + populate_suite_ut_packages(l_sub_suite, a_packages); + end if; + end loop; end; - - -- Validate all paths are correctly formatted - procedure validate_paths(a_paths in ut_varchar2_list) is - l_path varchar2(32767); - begin - if a_paths is null or a_paths.count = 0 then - raise_application_error(ut_utils.gc_path_list_is_empty, 'Path list is empty'); - else - for i in 1 .. a_paths.count loop - l_path := a_paths(i); - if l_path is null or not (regexp_like(l_path, '^[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+){0,2}$') or regexp_like(l_path, '^([A-Za-z0-9$#_]+)?:[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+)*$')) then - raise_application_error(ut_utils.gc_invalid_path_format, 'Invalid path format: ' || nvl(l_path, 'NULL')); - end if; - end loop; - end if; - end validate_paths; - - function configure_execution_by_path(a_paths in ut_varchar2_list) return ut_suite_items is - l_paths ut_varchar2_list; - l_path varchar2(32767); - l_schema varchar2(4000); - l_schema_suites tt_schema_suites; - l_index varchar2(4000 char); - l_suite ut_logical_suite; - l_suite_path varchar2(4000); - l_root_suite_name varchar2(4000); - l_objects_to_run ut_suite_items; - c_current_schema constant all_tables.owner%type := sys_context('USERENV','CURRENT_SCHEMA'); - - function clean_paths(a_paths ut_varchar2_list) return ut_varchar2_list is - l_paths_temp ut_varchar2_list := ut_varchar2_list(); - begin - l_paths_temp.extend(a_paths.count); - for i in 1 .. a_paths.count loop - l_paths_temp(i) := trim(lower(a_paths(i))); + begin + if a_schema_names is not null then + for i in 1 .. a_schema_names.count loop + l_schema_suites := get_schema_suites(a_schema_names(i)); + l_iter := l_schema_suites.first; + while l_iter is not null loop + populate_suite_ut_packages(l_schema_suites(l_iter), l_schema_ut_packages); + l_iter := l_schema_suites.next(l_iter); end loop; - l_paths_temp := set(l_paths_temp); - return l_paths_temp; - end clean_paths; - - procedure skip_by_path(a_suite in out nocopy ut_suite_item, a_path varchar2) is - c_root constant varchar2(32767) := upper(regexp_substr(a_path, '[A-Za-z0-9$#_]+')); - c_rest_path constant varchar2(32767) := regexp_substr(a_path, '\.(.+)', subexpression => 1); - l_suite ut_logical_suite; - l_item ut_suite_item; - l_items ut_suite_items := ut_suite_items(); - begin - if a_path is not null and a_suite is not null and a_suite is of (ut_logical_suite) then - l_suite := treat(a_suite as ut_logical_suite); - - for i in 1 .. l_suite.items.count loop + end loop; + l_schema_ut_packages := set(l_schema_ut_packages); + end if; + + return l_schema_ut_packages; + end; + + -- Validate all paths are correctly formatted + procedure validate_paths(a_paths in ut_varchar2_list) is + l_path varchar2(32767); + begin + if a_paths is null or a_paths.count = 0 then + raise_application_error(ut_utils.gc_path_list_is_empty, 'Path list is empty'); + else + for i in 1 .. a_paths.count loop + l_path := a_paths(i); + if l_path is null or not (regexp_like(l_path, '^[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+){0,2}$') or regexp_like(l_path, '^([A-Za-z0-9$#_]+)?:[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+)*$')) then + raise_application_error(ut_utils.gc_invalid_path_format, 'Invalid path format: ' || nvl(l_path, 'NULL')); + end if; + end loop; + end if; + end validate_paths; + + function configure_execution_by_path(a_paths in ut_varchar2_list) return ut_suite_items is + l_paths ut_varchar2_list; + l_path varchar2(32767); + l_schema varchar2(4000); + l_schema_suites tt_schema_suites; + l_index varchar2(4000 char); + l_suite ut_logical_suite; + l_suite_path varchar2(4000); + l_root_suite_name varchar2(4000); + l_objects_to_run ut_suite_items; + c_current_schema constant all_tables.owner%type := sys_context('USERENV','CURRENT_SCHEMA'); + + function clean_paths(a_paths ut_varchar2_list) return ut_varchar2_list is + l_paths_temp ut_varchar2_list := ut_varchar2_list(); + begin + l_paths_temp.extend(a_paths.count); + for i in 1 .. a_paths.count loop + l_paths_temp(i) := trim(lower(a_paths(i))); + end loop; + l_paths_temp := set(l_paths_temp); + return l_paths_temp; + end clean_paths; - l_item := l_suite.items(i); + procedure skip_by_path(a_suite in out nocopy ut_suite_item, a_path varchar2) is + c_root constant varchar2(32767) := upper(regexp_substr(a_path, '[A-Za-z0-9$#_]+')); + c_rest_path constant varchar2(32767) := regexp_substr(a_path, '\.(.+)', subexpression => 1); + l_suite ut_logical_suite; + l_item ut_suite_item; + l_items ut_suite_items := ut_suite_items(); + begin + if a_path is not null and a_suite is not null and a_suite is of (ut_logical_suite) then + l_suite := treat(a_suite as ut_logical_suite); - if upper(l_item.name) = c_root then + for i in 1 .. l_suite.items.count loop - skip_by_path(l_item, c_rest_path); - l_items.extend; - l_items(l_items.count) := l_item; + l_item := l_suite.items(i); - end if; + if upper(l_item.name) = c_root then - end loop; + skip_by_path(l_item, c_rest_path); + l_items.extend; + l_items(l_items.count) := l_item; - if l_items.count = 0 then - --not l_found then - raise_application_error(-20203, 'Suite not found'); end if; - l_suite.items := l_items; - a_suite := l_suite; + end loop; + if l_items.count = 0 then + --not l_found then + raise_application_error(-20203, 'Suite not found'); end if; - end skip_by_path; - - function package_exists_in_cur_schema(a_package_name varchar2) return boolean is - l_cnt number; - begin - select count(*) - into l_cnt - from all_objects t - where t.object_name = upper(a_package_name) - and t.object_type = 'PACKAGE' - and t.owner = c_current_schema; - return l_cnt > 0; - end package_exists_in_cur_schema; - begin - l_paths := clean_paths(a_paths); - - validate_paths(l_paths); - l_objects_to_run := ut_suite_items(); + l_suite.items := l_items; + a_suite := l_suite; - -- current implementation operates only on a single path - -- to be improved later - for i in 1 .. l_paths.count loop - l_path := l_paths(i); + end if; + end skip_by_path; - if regexp_like(l_path, '^([A-Za-z0-9$#_]+)?:') then - l_schema := regexp_substr(l_path, '^([A-Za-z0-9$#_]+)?:',subexpression => 1); - -- transform ":path1[.path2]" to "schema:path1[.path2]" - if l_schema is not null then - l_schema := sys.dbms_assert.schema_name(upper(l_schema)); - else - l_path := c_current_schema || l_path; - l_schema := c_current_schema; - end if; + function package_exists_in_cur_schema(a_package_name varchar2) return boolean is + l_cnt number; + begin + select count(*) + into l_cnt + from all_objects t + where t.object_name = upper(a_package_name) + and t.object_type = 'PACKAGE' + and t.owner = c_current_schema; + return l_cnt > 0; + end package_exists_in_cur_schema; + + begin + l_paths := clean_paths(a_paths); + + validate_paths(l_paths); + l_objects_to_run := ut_suite_items(); + + -- current implementation operates only on a single path + -- to be improved later + for i in 1 .. l_paths.count loop + l_path := l_paths(i); + if regexp_like(l_path, '^([A-Za-z0-9$#_]+)?:') then + l_schema := regexp_substr(l_path, '^([A-Za-z0-9$#_]+)?:',subexpression => 1); + -- transform ":path1[.path2]" to "schema:path1[.path2]" + if l_schema is not null then + l_schema := sys.dbms_assert.schema_name(upper(l_schema)); else - -- When path is one of: schema or schema.package[.object] or package[.object] - -- transform it back to schema[.package[.object]] - begin - l_schema := regexp_substr(l_path, '^[A-Za-z0-9$#_]+'); - l_schema := sys.dbms_assert.schema_name(upper(l_schema)); - exception - when sys.dbms_assert.invalid_schema_name then - if package_exists_in_cur_schema(l_schema) then - l_path := c_current_schema || '.' || l_path; - l_schema := c_current_schema; - else - raise; - end if; - end; - + l_path := c_current_schema || l_path; + l_schema := c_current_schema; end if; + else + -- When path is one of: schema or schema.package[.object] or package[.object] + -- transform it back to schema[.package[.object]] + begin + l_schema := regexp_substr(l_path, '^[A-Za-z0-9$#_]+'); + l_schema := sys.dbms_assert.schema_name(upper(l_schema)); + exception + when sys.dbms_assert.invalid_schema_name then + if package_exists_in_cur_schema(l_schema) then + l_path := c_current_schema || '.' || l_path; + l_schema := c_current_schema; + else + raise; + end if; + end; - l_schema_suites := get_schema_suites(upper(l_schema)); - - if regexp_like(l_path, '^[A-Za-z0-9$#_]+$') then - -- run whole schema - l_index := l_schema_suites.first; - while l_index is not null loop - l_objects_to_run.extend; - l_objects_to_run(l_objects_to_run.count) := l_schema_suites(l_index); - l_index := l_schema_suites.next(l_index); - end loop; - else - -- convert SCHEMA.PACKAGE.PROCEDURE syntax to fully qualified path - if regexp_like(l_path, '^[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+){1,2}$') then - declare - l_temp_suite ut_logical_suite; - l_package_name varchar2(4000); - l_procedure_name varchar2(4000); - begin - l_package_name := regexp_substr(l_path, '^[A-Za-z0-9$#_]+\.([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?$', subexpression => 1); - l_procedure_name := regexp_substr(l_path, '^[A-Za-z0-9$#_]+\.([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?$', subexpression => 3); - - l_temp_suite := config_package(l_schema, l_package_name); - - if l_temp_suite is null then - raise_application_error(ut_utils.gc_suite_package_not_found,'Suite package '||l_schema||'.'||l_package_name|| ' not found'); - end if; - - l_path := rtrim(l_schema || ':' || l_temp_suite.path || '.' || l_procedure_name, '.'); - end; - end if; + end if; - -- fully qualified path branch in the form - -- by this time it's the only format left - -- schema:suite.suite.suite - l_suite_path := regexp_substr(l_path, ':(.+)', subexpression => 1); - l_root_suite_name := regexp_substr(l_suite_path, '^[A-Za-z0-9$#_]+'); + l_schema_suites := get_schema_suites(upper(l_schema)); + if regexp_like(l_path, '^[A-Za-z0-9$#_]+$') then + -- run whole schema + l_index := l_schema_suites.first; + while l_index is not null loop + l_objects_to_run.extend; + l_objects_to_run(l_objects_to_run.count) := l_schema_suites(l_index); + l_index := l_schema_suites.next(l_index); + end loop; + else + -- convert SCHEMA.PACKAGE.PROCEDURE syntax to fully qualified path + if regexp_like(l_path, '^[A-Za-z0-9$#_]+(\.[A-Za-z0-9$#_]+){1,2}$') then + declare + l_temp_suite ut_logical_suite; + l_package_name varchar2(4000); + l_procedure_name varchar2(4000); begin - l_suite := l_schema_suites(l_root_suite_name); - exception - when no_data_found then - raise_application_error(-20203, 'Suite ' || l_root_suite_name || ' does not exist or is invalid'); + l_package_name := regexp_substr(l_path, '^[A-Za-z0-9$#_]+\.([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?$', subexpression => 1); + l_procedure_name := regexp_substr(l_path, '^[A-Za-z0-9$#_]+\.([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?$', subexpression => 3); + + if not g_schema_object_path_map(l_schema).exists(l_package_name) then + raise_application_error(ut_utils.gc_suite_package_not_found,'Suite package '||l_schema||'.'||l_package_name|| ' not found'); + end if; + l_path := rtrim(l_schema || ':' || g_schema_object_path_map(l_schema)(l_package_name) || '.' || l_procedure_name, '.'); end; + end if; - skip_by_path(l_suite, regexp_substr(l_suite_path, '\.(.+)', subexpression => 1)); + -- fully qualified path branch in the form + -- by this time it's the only format left + -- schema:suite.suite.suite + l_suite_path := regexp_substr(l_path, ':(.+)', subexpression => 1); + l_root_suite_name := regexp_substr(l_suite_path, '^[A-Za-z0-9$#_]+'); - l_objects_to_run.extend; - l_objects_to_run(l_objects_to_run.count) := l_suite; + begin + l_suite := l_schema_suites(l_root_suite_name); + exception + when no_data_found then + raise_application_error(-20203, 'Suite ' || l_root_suite_name || ' does not exist or is invalid'); + end; - end if; + skip_by_path(l_suite, regexp_substr(l_suite_path, '\.(.+)', subexpression => 1)); - end loop; - return l_objects_to_run; - end configure_execution_by_path; + l_objects_to_run.extend; + l_objects_to_run(l_objects_to_run.count) := l_suite; + + end if; + + end loop; + return l_objects_to_run; + end configure_execution_by_path; - end ut_suite_manager; - / +end ut_suite_manager; +/ diff --git a/source/core/ut_suite_manager.pks b/source/core/ut_suite_manager.pks index d13df91f5..57d336c54 100644 --- a/source/core/ut_suite_manager.pks +++ b/source/core/ut_suite_manager.pks @@ -16,9 +16,10 @@ create or replace package ut_suite_manager authid current_user is limitations under the License. */ - function config_package(a_owner_name varchar2, a_object_name varchar2) return ut_logical_suite; + --builds individual suites from a cursor of objects and their annotations + function build_suites(a_cursor sys_refcursor) return ut_suite_items pipelined; - procedure config_schema(a_owner_name varchar2); +-- procedure config_schema(a_owner_name varchar2); function get_schema_ut_packages(a_schema_names ut_varchar2_rows) return ut_object_names; diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index ceb962f23..bd2355235 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -397,5 +397,11 @@ create or replace package body ut_utils is end if; return l_result; end; + + function ut_owner return varchar2 is + begin + return sys_context('userenv','current_schema'); + end; + end ut_utils; / diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 4d2c2c070..49b326fc0 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -257,5 +257,7 @@ create or replace package ut_utils authid definer is */ function to_version(a_version_no varchar2) return t_version; + function ut_owner return varchar2; + end ut_utils; / diff --git a/source/create_synonyms_and_grants_for_public.sql b/source/create_synonyms_and_grants_for_public.sql index b4fedff80..8353f60d5 100644 --- a/source/create_synonyms_and_grants_for_public.sql +++ b/source/create_synonyms_and_grants_for_public.sql @@ -67,6 +67,15 @@ grant execute on &&ut3_owner..ut_key_value_pairs to public; grant execute on &&ut3_owner..ut_key_value_pair to public; grant select, insert, delete on &&ut3_owner..ut_cursor_data to public; grant execute on &&ut3_owner..ut_sonar_test_reporter to public; +grant execute on &&ut3_owner..ut_annotations to public; +grant execute on &&ut3_owner..ut_annotation to public; +grant execute on &&ut3_owner..ut_annotated_object to public; +grant execute on &&ut3_owner..ut_annotated_objects to public; +grant select on &&ut3_owner..ut_annotation_cache_info to public; +grant select on &&ut3_owner..ut_annotation_cache to public; +grant execute on &&ut3_owner..ut_annotation_cache_manager to public; +grant execute on &&ut3_owner..ut_annotation_parser to public; + prompt Creating synonyms for UTPLSQL objects in &&ut3_owner schema to PUBLIC diff --git a/source/create_synonyms_and_grants_for_user.sql b/source/create_synonyms_and_grants_for_user.sql index e7f72270f..76dd3c0a9 100644 --- a/source/create_synonyms_and_grants_for_user.sql +++ b/source/create_synonyms_and_grants_for_user.sql @@ -87,6 +87,14 @@ grant execute on &&ut3_owner..ut_key_value_pairs to &ut3_user; grant execute on &&ut3_owner..ut_key_value_pair to &ut3_user; grant select, insert, delete on &&ut3_owner..ut_cursor_data to &ut3_user; grant execute on &&ut3_owner..ut_sonar_test_reporter to &ut3_user; +grant execute on &&ut3_owner..ut_annotations to &ut3_user; +grant execute on &&ut3_owner..ut_annotation to &ut3_user; +grant execute on &&ut3_owner..ut_annotated_object to &ut3_user; +grant execute on &&ut3_owner..ut_annotated_objects to &ut3_user; +grant select on &&ut3_owner..ut_annotation_cache_info to &ut3_user; +grant select on &&ut3_owner..ut_annotation_cache to &ut3_user; +grant execute on &&ut3_owner..ut_annotation_cache_manager to &ut3_user; +grant execute on &&ut3_owner..ut_annotation_parser to &ut3_user; prompt Creating synonyms for UTPLSQL objects in &&ut3_owner schema to user &&ut3_user diff --git a/source/install.sql b/source/install.sql index 6d58cf0d0..5b3e6493f 100644 --- a/source/install.sql +++ b/source/install.sql @@ -77,6 +77,13 @@ alter session set plsql_warnings = 'ENABLE:ALL', 'DISABLE:(5004,5018,6000,6001,6 --annoations @@install_component.sql 'core/annotations/ut_annotation.tps' @@install_component.sql 'core/annotations/ut_annotations.tps' +@@install_component.sql 'core/annotations/ut_annotated_object.tps' +@@install_component.sql 'core/annotations/ut_annotated_objects.tps' +@@install_component.sql 'core/annotations/ut_annotation_cache_seq.sql' +@@install_component.sql 'core/annotations/ut_annotation_cache_info.sql' +@@install_component.sql 'core/annotations/ut_annotation_cache.sql' +@@install_component.sql 'core/annotations/ut_annotation_cache_manager.pks' +@@install_component.sql 'core/annotations/ut_annotation_cache_manager.pkb' @@install_component.sql 'core/annotations/ut_annotation_parser.pks' @@install_component.sql 'core/annotations/ut_annotation_parser.pkb' diff --git a/source/uninstall.sql b/source/uninstall.sql index 0c04227ad..6a3ac600c 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -174,10 +174,22 @@ drop table ut_cursor_data; drop package ut_annotation_parser; -drop type ut_annotation force; +drop package ut_annotation_cache_manager; + +drop table ut_annotation_cache cascade constraints; + +drop table ut_annotation_cache_info cascade constraints; + +drop sequence ut_annotation_cache_seq; + +drop type ut_annotated_objects force; + +drop type ut_annotated_object force; drop type ut_annotations force; +drop type ut_annotation force; + drop package ut_file_mapper; drop package ut_metadata; diff --git a/test/ut_suite_manager/test_suite_manager.pkb b/test/ut_suite_manager/test_suite_manager.pkb index dcd7344f9..7f1b5cbe8 100644 --- a/test/ut_suite_manager/test_suite_manager.pkb +++ b/test/ut_suite_manager/test_suite_manager.pkb @@ -14,7 +14,7 @@ create or replace package body test_suite_manager is begin --Act l_all_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + for i in 1..l_all_objects_to_run.count loop if l_all_objects_to_run(i).name in ('tests', 'tests2') then l_objects_to_run.extend; @@ -28,9 +28,9 @@ create or replace package body test_suite_manager is for i in 1 .. 2 loop l_test0_suite := treat(l_objects_to_run(i) as ut3.ut_logical_suite); ut.expect(l_test0_suite.name in ('tests', 'tests2')).to_be_true; - + l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + case l_test0_suite.name when 'tests' then ut.expect(l_test1_suite.name).to_equal('test_package_1'); @@ -39,576 +39,576 @@ create or replace package body test_suite_manager is ut.expect(l_test2_suite.name).to_equal('test_package_2'); ut.expect(l_test2_suite.items.count).to_equal(2); - when 'tests2' then + when 'tests2' then ut.expect(l_test1_suite.name).to_equal('test_package_3'); ut.expect(l_test1_suite.items.count).to_equal(3); end case; - + end loop; end; - + procedure test_top2_by_name is c_path varchar2(100) := USER||'.test_package_2'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test2_suite ut3.ut_logical_suite; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); l_test2_suite := treat(l_test1_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test2_suite.name).to_equal('test_package_2'); ut.expect(l_test2_suite.items.count).to_equal(2); end; - + procedure test_top2_bt_name_cur_user is c_path varchar2(100) := 'test_package_2'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test2_suite ut3.ut_logical_suite; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); l_test2_suite := treat(l_test1_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test2_suite.name).to_equal('test_package_2'); ut.expect(l_test2_suite.items.count).to_equal(2); end; - + procedure test_by_path_to_subsuite is c_path varchar2(100) := USER||':tests.test_package_1.test_package_2'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test2_suite ut3.ut_logical_suite; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); l_test2_suite := treat(l_test1_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test2_suite.name).to_equal('test_package_2'); ut.expect(l_test2_suite.items.count).to_equal(2); end; - + procedure test_by_path_to_subsuite_cu is c_path varchar2(100) := ':tests.test_package_1.test_package_2'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test2_suite ut3.ut_logical_suite; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); l_test2_suite := treat(l_test1_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test2_suite.name).to_equal('test_package_2'); ut.expect(l_test2_suite.items.count).to_equal(2); end; - + procedure test_subsute_proc_by_path is c_path varchar2(100) := USER||':tests.test_package_1.test_package_2.test2'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test2_suite ut3.ut_logical_suite; l_test_proc ut3.ut_test; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); l_test2_suite := treat(l_test1_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test2_suite.name).to_equal('test_package_2'); ut.expect(l_test2_suite.items.count).to_equal(1); - - l_test_proc := treat(l_test2_suite.items(1) as ut3.ut_test); + + l_test_proc := treat(l_test2_suite.items(1) as ut3.ut_test); ut.expect(l_test_proc.name).to_equal('test2'); ut.expect(l_test_proc.before_test is not null).to_be_true; ut.expect(l_test_proc.after_test is not null).to_be_true; end; - + procedure test_subsute_proc_by_path_cu is c_path varchar2(100) := ':tests.test_package_1.test_package_2.test2'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test2_suite ut3.ut_logical_suite; l_test_proc ut3.ut_test; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); l_test2_suite := treat(l_test1_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test2_suite.name).to_equal('test_package_2'); ut.expect(l_test2_suite.items.count).to_equal(1); - - l_test_proc := treat(l_test2_suite.items(1) as ut3.ut_test); + + l_test_proc := treat(l_test2_suite.items(1) as ut3.ut_test); ut.expect(l_test_proc.name).to_equal('test2'); ut.expect(l_test_proc.before_test is not null).to_be_true; ut.expect(l_test_proc.after_test is not null).to_be_true; end; - + procedure test_top_pack_by_name is c_path varchar2(100) := USER||'.test_package_1'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_suite; l_test2_suite ut3.ut_suite; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(3); - + ut.expect(l_test1_suite.items(1).name).to_equal('test1'); ut.expect(l_test1_suite.items(1).description).to_equal('Test1 from test package 1'); ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_test.is_defined).to_be_false; ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).after_test.is_defined).to_be_false; ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_each.is_defined).to_be_true; ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).DISABLED_FLAG).to_equal(0); - + ut.expect(l_test1_suite.items(2).name).to_equal('test2'); ut.expect(l_test1_suite.items(2).description).to_equal('Test2 from test package 1'); ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_test.is_defined).to_be_true; ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).after_test.is_defined).to_be_true; ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_each.is_defined).to_be_true; ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).DISABLED_FLAG).to_equal(0); - + -- temporary behavior. -- decided that when executed by package, not path, only that package has to execute l_test2_suite := treat(l_test1_suite.items(3) as ut3.ut_suite); - + ut.expect(l_test2_suite.name).to_equal('test_package_2'); ut.expect(l_test2_suite.items.count).to_equal(2); end; - + procedure test_top_pack_by_name_cu is c_path varchar2(100) := 'test_package_1'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_suite; l_test2_suite ut3.ut_suite; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(3); - + ut.expect(l_test1_suite.items(1).name).to_equal('test1'); ut.expect(l_test1_suite.items(1).description).to_equal('Test1 from test package 1'); ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_test.is_defined).to_be_false; ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).after_test.is_defined).to_be_false; ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).before_each.is_defined).to_be_true; ut.expect(treat(l_test1_suite.items(1) as ut3.ut_test).DISABLED_FLAG).to_equal(0); - + ut.expect(l_test1_suite.items(2).name).to_equal('test2'); ut.expect(l_test1_suite.items(2).description).to_equal('Test2 from test package 1'); ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_test.is_defined).to_be_true; ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).after_test.is_defined).to_be_true; ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).before_each.is_defined).to_be_true; ut.expect(treat(l_test1_suite.items(2) as ut3.ut_test).DISABLED_FLAG).to_equal(0); - + -- temporary behavior. -- decided that when executed by package, not path, only that package has to execute l_test2_suite := treat(l_test1_suite.items(3) as ut3.ut_suite); - + ut.expect(l_test2_suite.name).to_equal('test_package_2'); ut.expect(l_test2_suite.items.count).to_equal(2); end; - + procedure test_top_pack_by_path is c_path varchar2(100) := USER||':tests'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test2_suite ut3.ut_logical_suite; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(3); l_test2_suite := treat(l_test1_suite.items(3) as ut3.ut_logical_suite); - + ut.expect(l_test2_suite.name).to_equal('test_package_2'); ut.expect(l_test2_suite.items.count).to_equal(2); end; - + procedure test_top_pack_by_path_cu is c_path varchar2(100) := ':tests'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test2_suite ut3.ut_logical_suite; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(3); l_test2_suite := treat(l_test1_suite.items(3) as ut3.ut_logical_suite); - + ut.expect(l_test2_suite.name).to_equal('test_package_2'); ut.expect(l_test2_suite.items.count).to_equal(2); end; - + procedure test_top_pck_proc_by_path is c_path varchar2(100) := USER||':tests.test_package_1.test2'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test2_suite ut3.ut_logical_suite; l_test_proc ut3.ut_test; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); - l_test_proc := treat(l_test1_suite.items(1) as ut3.ut_test); - + l_test_proc := treat(l_test1_suite.items(1) as ut3.ut_test); + ut.expect(l_test_proc.name).to_equal('test2'); ut.expect(l_test_proc.description).to_equal('Test2 from test package 1'); ut.expect(l_test_proc.before_test is not null).to_be_true; ut.expect(l_test_proc.after_test is not null).to_be_true; end; - + procedure test_top_pck_proc_by_path_cu is c_path varchar2(100) := ':tests.test_package_1.test2'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test2_suite ut3.ut_logical_suite; l_test_proc ut3.ut_test; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); - l_test_proc := treat(l_test1_suite.items(1) as ut3.ut_test); - + l_test_proc := treat(l_test1_suite.items(1) as ut3.ut_test); + ut.expect(l_test_proc.name).to_equal('test2'); ut.expect(l_test_proc.description).to_equal('Test2 from test package 1'); ut.expect(l_test_proc.before_test is not null).to_be_true; ut.expect(l_test_proc.after_test is not null).to_be_true; end; - + procedure test_top_pkc_proc_by_name is c_path varchar2(100) := USER||'.test_package_1.test2'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test_proc ut3.ut_test; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); - - l_test_proc := treat(l_test1_suite.items(1) as ut3.ut_test); + + l_test_proc := treat(l_test1_suite.items(1) as ut3.ut_test); ut.expect(l_test_proc.name).to_equal('test2'); ut.expect(l_test_proc.before_test is not null).to_be_true; ut.expect(l_test_proc.after_test is not null).to_be_true; end; - + procedure test_top_pkc_proc_by_name_cu is c_path varchar2(100) := 'test_package_1.test2'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test_proc ut3.ut_test; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); - - l_test_proc := treat(l_test1_suite.items(1) as ut3.ut_test); + + l_test_proc := treat(l_test1_suite.items(1) as ut3.ut_test); ut.expect(l_test_proc.name).to_equal('test2'); ut.expect(l_test_proc.before_test is not null).to_be_true; ut.expect(l_test_proc.after_test is not null).to_be_true; end; - + procedure test_top_pkc_nosub_by_name is c_path varchar2(100) := USER||'.test_package_3'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test1 ut3.ut_test; l_test3 ut3.ut_test; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests2'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_3'); ut.expect(l_test1_suite.items.count).to_equal(3); - + l_test1 := treat(l_test1_suite.items(1) as ut3.ut_test); ut.expect(l_test1.name).to_equal('test1'); ut.expect(l_test1.DISABLED_FLAG).to_equal(0); - + l_test3 := treat(l_test1_suite.items(3) as ut3.ut_test); ut.expect(l_test3.name).to_equal('disabled_test'); ut.expect(l_test3.DISABLED_FLAG).to_equal(1); end; - + procedure test_top_pkc_nosub_by_name_cu is c_path varchar2(100) := 'test_package_3'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test1 ut3.ut_test; l_test3 ut3.ut_test; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests2'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_3'); ut.expect(l_test1_suite.items.count).to_equal(3); - + l_test1 := treat(l_test1_suite.items(1) as ut3.ut_test); ut.expect(l_test1.name).to_equal('test1'); ut.expect(l_test1.DISABLED_FLAG).to_equal(0); - + l_test3 := treat(l_test1_suite.items(3) as ut3.ut_test); ut.expect(l_test3.name).to_equal('disabled_test'); ut.expect(l_test3.DISABLED_FLAG).to_equal(1); end; - + procedure test_top_subpck_by_path is c_path varchar2(100) := USER||':tests2.test_package_3'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test1 ut3.ut_test; l_test3 ut3.ut_test; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests2'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_3'); ut.expect(l_test1_suite.items.count).to_equal(3); - + l_test1 := treat(l_test1_suite.items(1) as ut3.ut_test); ut.expect(l_test1.name).to_equal('test1'); ut.expect(l_test1.DISABLED_FLAG).to_equal(0); - + l_test3 := treat(l_test1_suite.items(3) as ut3.ut_test); ut.expect(l_test3.name).to_equal('disabled_test'); ut.expect(l_test3.DISABLED_FLAG).to_equal(1); - end; - + end; + procedure test_top_subpck_by_path_cu is c_path varchar2(100) := ':tests2.test_package_3'; l_objects_to_run ut3.ut_suite_items; - + l_test0_suite ut3.ut_logical_suite; l_test1_suite ut3.ut_logical_suite; l_test1 ut3.ut_test; l_test3 ut3.ut_test; - begin + begin --Act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(c_path)); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_test0_suite := treat(l_objects_to_run(1) as ut3.ut_logical_suite); - + ut.expect(l_test0_suite.name).to_equal('tests2'); ut.expect(l_test0_suite.items.count).to_equal(1); l_test1_suite := treat(l_test0_suite.items(1) as ut3.ut_logical_suite); - + ut.expect(l_test1_suite.name).to_equal('test_package_3'); ut.expect(l_test1_suite.items.count).to_equal(3); - + l_test1 := treat(l_test1_suite.items(1) as ut3.ut_test); ut.expect(l_test1.name).to_equal('test1'); ut.expect(l_test1.DISABLED_FLAG).to_equal(0); - + l_test3 := treat(l_test1_suite.items(3) as ut3.ut_test); ut.expect(l_test3.name).to_equal('disabled_test'); ut.expect(l_test3.DISABLED_FLAG).to_equal(1); - end; - + end; + procedure test_search_invalid_pck is l_objects_to_run ut3.ut_suite_items; begin @@ -618,7 +618,7 @@ create or replace package body test_suite_manager is when others then ut.expect(sqlerrm).to_be_like('%failing_invalid_spec%'); end; - + procedure compile_invalid_package is ex_compilation_error exception; pragma exception_init(ex_compilation_error,-24344); @@ -652,7 +652,7 @@ end;]'; begin execute immediate 'drop package failing_invalid_spec'; end; - + procedure test_desc_with_comma is l_objects_to_run ut3.ut_suite_items; l_suite ut3.ut_suite; @@ -709,26 +709,26 @@ end;'; null; end; end; - + procedure test_inv_cache_on_drop is l_test_report ut3.ut_varchar2_list; begin - + select * bulk collect into l_test_report from table(ut3.ut.run(USER||'.tst_package_to_be_dropped')); -- drop package - clean_inv_cache_on_drop; + clean_inv_cache_on_drop; begin select * bulk collect into l_test_report from table(ut3.ut.run(user || '.tst_package_to_be_dropped')); ut.fail('Cache not invalidated on package drop'); exception when others then - ut.expect(sqlerrm).to_be_like('%tst_package_to_be_dropped%does not exist%'); + ut.expect(sqlerrm).to_be_like('%tst_package_to_be_dropped%not found%'); end; - + end; - procedure setup_inv_cache_on_drop is + procedure setup_inv_cache_on_drop is pragma autonomous_transaction; begin execute immediate 'create or replace package tst_package_to_be_dropped as @@ -743,7 +743,7 @@ end;'; procedure test2 is begin ut.expect(1).to_equal(1); end; end;'; end; - + procedure clean_inv_cache_on_drop is pragma autonomous_transaction; begin @@ -752,16 +752,16 @@ end;'; when ex_obj_doesnt_exist then null; end; - + procedure test_inv_pck_bodies is l_test_report ut3.ut_varchar2_list; begin - + select * bulk collect into l_test_report from table(ut3.ut.run(USER||'.test_dependencies')); - + ut.expect(l_test_report(l_test_report.count-1)).to_be_like('1 test_, 0 failed, 0 errored, 0 disabled, 0 warning(s)'); --execute immediate 'select * from table(ut3.ut.run(''UT3.test_dependencies'', ut3.utplsql_test_reporter()))' into l_result; - + -- ut.expect(l_result).to_equal(ut3.ut_utils.tr_success); end; procedure setup_inv_pck_bodies is @@ -788,14 +788,14 @@ end;'; execute immediate 'drop table test_dependency_table'; execute immediate 'drop package test_dependencies'; end; - + procedure test_pck_with_dollar is l_objects_to_run ut3.ut_suite_items; l_suite ut3.ut_suite; begin --act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list('tst_package_with$dollar')); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); @@ -822,15 +822,15 @@ end;'; begin execute immediate 'drop package tst_package_with$dollar'; end; - - + + procedure test_pck_with_hash is l_objects_to_run ut3.ut_suite_items; l_suite ut3.ut_suite; begin --act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list('tst_package_with#hash')); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); @@ -857,8 +857,8 @@ end;'; begin execute immediate 'drop package tst_package_with#hash'; end; - - + + procedure test_test_with_dollar is l_objects_to_run ut3.ut_suite_items; l_suite ut3.ut_suite; @@ -866,7 +866,7 @@ end;'; begin --act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list('tst_package_with_dollar_test.test$1')); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); @@ -899,7 +899,7 @@ end;'; begin execute immediate 'drop package tst_package_with_dollar_test'; end; - + procedure test_test_with_hash is l_objects_to_run ut3.ut_suite_items; l_suite ut3.ut_suite; @@ -907,7 +907,7 @@ end;'; begin --act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list('tst_package_with_hash_test.test#1')); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); @@ -940,7 +940,7 @@ end;'; begin execute immediate 'drop package tst_package_with_hash_test'; end; - + procedure test_empty_suite_path is l_objects_to_run ut3.ut_suite_items; l_suite ut3.ut_suite; @@ -948,7 +948,7 @@ end;'; --act l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list('tst_empty_suite_path')); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); @@ -956,7 +956,7 @@ end;'; ut.expect(l_suite.name).to_equal('tst_empty_suite_path'); end; - + procedure setup_empty_suite_path is pragma autonomous_transaction; begin @@ -971,7 +971,7 @@ end;'; procedure test1 is begin ut.expect(1).to_equal(1); end; end;'; end; - + procedure clean_empty_suite_path is pragma autonomous_transaction; begin @@ -1146,7 +1146,7 @@ end test_package_2;]'; procedure test2_setup; procedure test2_teardown; - + --%test --%disabled procedure disabled_test; @@ -1191,7 +1191,7 @@ end test_package_3;]'; gv_var_1 := gv_var_1_temp; gv_var_1_temp := null; end; - + procedure disabled_test is begin null; @@ -1207,7 +1207,7 @@ end test_package_3;]'; execute immediate 'drop package test_package_2'; execute immediate 'drop package test_package_3'; end; - + procedure test_pck_with_same_path is l_objects_to_run ut3.ut_suite_items; l_suite1 ut3.ut_logical_suite; @@ -1215,24 +1215,24 @@ end test_package_3;]'; l_suite3 ut3.ut_suite; begin l_objects_to_run := ut3.ut_suite_manager.configure_execution_by_path(ut3.ut_varchar2_list(':test1.test2$.test_package_same_1')); - + --Assert ut.expect(l_objects_to_run.count).to_equal(1); l_suite1 := treat(l_objects_to_run(1) as ut3.ut_logical_suite); ut.expect(l_suite1.name).to_equal('test1'); - ut.expect(l_suite1.items.count).to_equal(1); - + ut.expect(l_suite1.items.count).to_equal(1); + l_suite2 := treat(l_suite1.items(1) as ut3.ut_logical_suite); - ut.expect(l_suite2.name).to_equal('test2$'); - ut.expect(l_suite2.items.count).to_equal(1); - + ut.expect(l_suite2.name).to_equal('test2$'); + ut.expect(l_suite2.items.count).to_equal(1); + l_suite3 := treat(l_suite2.items(1) as ut3.ut_suite); - ut.expect(l_suite3.name).to_equal('test_package_same_1'); + ut.expect(l_suite3.name).to_equal('test_package_same_1'); end; - + procedure setup_pck_with_same_path is - pragma autonomous_transaction; + pragma autonomous_transaction; begin execute immediate 'create or replace package test_package_same_1 as --%suite @@ -1255,14 +1255,14 @@ end;'; procedure test1 is begin null; end; end;'; end; - + procedure clean_pck_with_same_path is - pragma autonomous_transaction; + pragma autonomous_transaction; begin execute immediate 'drop package test_package_same_1'; execute immediate 'drop package test_package_same_1_a'; null; end; - + end test_suite_manager; / From 6088c814b5d2f1e5e23756097b7b983f37c0c7e0 Mon Sep 17 00:00:00 2001 From: Jacek Date: Mon, 9 Oct 2017 07:25:12 +0100 Subject: [PATCH 08/19] Added ability to get annotations for single object. Added-back public procedure `ut_suite_manager.config_package` for testing purposes (example with huge ut package) --- .../core/annotations/ut_annotation_parser.pkb | 14 +++++++--- .../core/annotations/ut_annotation_parser.pks | 2 +- source/core/ut_suite_manager.pkb | 26 +++++++++++++++++-- source/core/ut_suite_manager.pks | 3 ++- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index b5f33d2ba..9ad68c774 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -297,14 +297,20 @@ create or replace package body ut_annotation_parser as end; - function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2) return ut_annotated_objects pipelined is + function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2 := null) return ut_annotated_objects pipelined is l_objects_view varchar2(200) := ut_metadata.get_dba_view('dba_objects'); l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source'); l_obj ut_annotated_object; l_current_schema varchar2(250) := ut_utils.ut_owner; l_cursor sys_refcursor; l_cursor_sql varchar2(32767); + l_object_filter varchar2(32767); begin + if a_object_name is not null then + l_object_filter := 'and o.object_name = :a_object_name'; + else + l_object_filter := 'and :a_object_name is null'; + end if; l_cursor_sql := q'[with object_cache_info as (select /*+ cardinality(i 10000) */ o.owner as object_owner, o.object_name, o.object_type, i.cache_id, @@ -312,7 +318,8 @@ create or replace package body ut_annotation_parser as from ]'||l_objects_view||q'[ o left join ]'||l_current_schema||q'[.ut_annotation_cache_info i on o.owner = i.object_owner and o.object_name = i.object_name and o.object_type = i.object_type - where o.object_type = :a_object_type and o.status = 'VALID' and o.owner = :a_object_owner + where o.object_type = :a_object_type and o.owner = :a_object_owner ]'||l_object_filter||q'[ + and o.status = 'VALID' ), obj_info_with_source as (select /*+ cardinality(o 10000) */o.object_owner, o.object_name, o.object_type, o.cache_stale, o.cache_id, s.line, s.text @@ -321,6 +328,7 @@ create or replace package body ut_annotation_parser as on s.name = o.object_name and s.type = :a_object_type and s.owner = :a_object_owner + ]'||l_object_filter||q'[ and o.cache_stale = 'Y' ), obj_info_source_grouped @@ -353,7 +361,7 @@ create or replace package body ut_annotation_parser as ) c ) a order by a.obj.object_owner, a.obj.object_type, a.obj.object_name]'; - open l_cursor for l_cursor_sql using a_object_type, a_object_owner, a_object_type, a_object_owner; + open l_cursor for l_cursor_sql using a_object_type, a_object_owner, a_object_name, a_object_type, a_object_owner, a_object_name; loop fetch l_cursor into l_obj; exit when l_cursor%notfound; diff --git a/source/core/annotations/ut_annotation_parser.pks b/source/core/annotations/ut_annotation_parser.pks index 75867f565..cb8fa6ae7 100644 --- a/source/core/annotations/ut_annotation_parser.pks +++ b/source/core/annotations/ut_annotation_parser.pks @@ -63,7 +63,7 @@ create or replace package ut_annotation_parser authid current_user as function parse_annotations(a_cursor t_object_sources_cur) return ut_annotated_objects pipelined; - function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2) return ut_annotated_objects pipelined; + function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2 := null) return ut_annotated_objects pipelined; /* function: get_package_annotations diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index fcb62fb68..0581608c1 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -78,8 +78,6 @@ create or replace package body ut_suite_manager is l_rollback_type integer; l_displayname varchar2(4000); - e_insufficient_priv exception; - pragma exception_init(e_insufficient_priv,-01031); begin l_suite_rollback := ut_utils.gc_rollback_auto; for i in 1 .. a_object.annotations.count loop @@ -185,6 +183,30 @@ create or replace package body ut_suite_manager is end config_package; + function config_package(a_owner_name varchar2, a_object_name varchar2) return ut_logical_suite is + l_owner_name varchar2(250 char); + l_object_name varchar2(250 char); + l_cursor sys_refcursor; + l_annotated_object ut_annotated_object; + e_insufficient_priv exception; + pragma exception_init(e_insufficient_priv,-01031); + begin + l_owner_name := a_owner_name; + l_object_name := a_object_name; + begin + ut_metadata.do_resolve(a_owner => l_owner_name, a_object => l_object_name); + exception + when e_insufficient_priv then + return null; + end; + + open l_cursor for select value(x) from table (ut3.ut_annotation_parser.get_annotated_objects(l_owner_name, 'PACKAGE', l_object_name))x; + fetch l_cursor into l_annotated_object; + close l_cursor; + return config_package(l_annotated_object); + + end; + function build_suites(a_cursor sys_refcursor) return ut_suite_items pipelined is l_object ut_annotated_object; begin diff --git a/source/core/ut_suite_manager.pks b/source/core/ut_suite_manager.pks index 57d336c54..50f2a5774 100644 --- a/source/core/ut_suite_manager.pks +++ b/source/core/ut_suite_manager.pks @@ -19,7 +19,8 @@ create or replace package ut_suite_manager authid current_user is --builds individual suites from a cursor of objects and their annotations function build_suites(a_cursor sys_refcursor) return ut_suite_items pipelined; --- procedure config_schema(a_owner_name varchar2); + --for testing purposes only + function config_package(a_owner_name varchar2, a_object_name varchar2) return ut_logical_suite; function get_schema_ut_packages(a_schema_names ut_varchar2_rows) return ut_object_names; From 4ec9eb594af2eaec961e90a9ab0b6773b251ef0b Mon Sep 17 00:00:00 2001 From: Jacek Date: Fri, 13 Oct 2017 09:55:46 +0100 Subject: [PATCH 09/19] Reworking annotation cache - not to use cast(Collect()) on dba_source. Added filtering of dba_source by test containing annotations. Added cardinality hint with scaling. Removed public procedure `config_package` - used only for one unit test. Fixed naming of procedures. Performance improvements on collections. --- development/cleanup.sh | 5 +- .../demo_equal_matcher.sql | 12 - ...eTestAnnotationsParsingTimeHugePackage.sql | 4 +- old_tests/RunAll.sql | 2 + .../core/annotations/ut_annotated_object.tps | 16 + .../core/annotations/ut_annotated_objects.tps | 19 +- source/core/annotations/ut_annotation.tps | 16 + .../core/annotations/ut_annotation_cache.sql | 15 +- .../annotations/ut_annotation_cache_info.sql | 17 +- .../ut_annotation_cache_manager.pkb | 93 +++- .../ut_annotation_cache_manager.pks | 22 +- .../annotations/ut_annotation_cache_seq.sql | 18 +- .../ut_annotation_cached_object.tps | 24 ++ .../ut_annotation_cached_objects.tps | 19 + .../core/annotations/ut_annotation_parser.pkb | 404 +++++++++--------- .../core/annotations/ut_annotation_parser.pks | 84 +--- source/core/annotations/ut_annotations.tps | 19 +- source/core/ut_output_buffer_tmp.sql | 2 +- source/core/ut_suite_manager.pkb | 116 ++--- source/core/ut_suite_manager.pks | 26 +- source/core/ut_utils.pkb | 7 + source/core/ut_utils.pks | 162 +++---- .../create_synonyms_and_grants_for_public.sql | 2 + .../create_synonyms_and_grants_for_user.sql | 2 + source/install.sql | 2 + source/uninstall.sql | 4 + .../test_annotation_parser.pkb | 46 +- test/ut_reporters/test_coverage.pkb | 4 + 28 files changed, 671 insertions(+), 491 deletions(-) create mode 100644 source/core/annotations/ut_annotation_cached_object.tps create mode 100644 source/core/annotations/ut_annotation_cached_objects.tps diff --git a/development/cleanup.sh b/development/cleanup.sh index 7f72d093e..233956001 100755 --- a/development/cleanup.sh +++ b/development/cleanup.sh @@ -15,9 +15,10 @@ drop user ${UT3_USER} cascade; begin for i in ( select decode(owner,'PUBLIC','drop public synonym "','drop synonym "'||owner||'"."')|| synonym_name ||'"' drop_orphaned_synonym from dba_synonyms a - where not exists (select null from dba_objects b where a.table_name=b.object_name and a.table_owner=b.owner ) - and a.table_owner <> 'SYS' + where not exists (select 1 from dba_objects b where (a.table_name=b.object_name and a.table_owner=b.owner or b.owner='SYS' and a.table_owner=b.object_name) ) + and a.table_owner not in ('SYS','SYSTEM') ) loop + dbms_output.put_line(i.drop_orphaned_synonym); execute immediate i.drop_orphaned_synonym; end loop; end; diff --git a/examples/demo_of_expectations/demo_equal_matcher.sql b/examples/demo_of_expectations/demo_equal_matcher.sql index fdc438204..f9d47974d 100644 --- a/examples/demo_of_expectations/demo_equal_matcher.sql +++ b/examples/demo_of_expectations/demo_equal_matcher.sql @@ -36,10 +36,6 @@ create or replace package demo_equal_matcher as -- %displayname(Gives success when comparing null actual to a null expected) procedure object_compare_null_both_ok; - -- %test - -- %displayname(Gives failure when comparing null actual to a null expected, setting null equal to false) - procedure object_compare_null_both_fail; - -- %test -- %displayname(Gives failure when comparing identical objects containing different data) procedure object_compare_different_data; @@ -94,14 +90,6 @@ create or replace package body demo_equal_matcher as ut.expect(anydata.convertObject(l_actual)).to_equal(anydata.convertObject(l_expected)); end; - procedure object_compare_null_both_fail is - l_expected demo_department; - l_actual demo_department; - begin - ut.expect(anydata.convertObject(l_actual)).to_equal(anydata.convertObject(l_expected),false); - end; - - procedure object_compare_different_data is l_expected demo_department; l_actual demo_department; diff --git a/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql b/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql index bb67d374f..53afcda16 100644 --- a/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql +++ b/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql @@ -6,9 +6,9 @@ set echo off @@tst_pkg_huge.pks declare - l_suite ut_logical_suite; + l_suites ut_suite_items; begin - l_suite := ut_suite_manager.config_package(a_owner_name => USER,a_object_name => 'TST_PKG_HUGE'); + l_suites := ut_suite_manager.configure_execution_by_path(USER||'.TST_PKG_HUGE'); end; / diff --git a/old_tests/RunAll.sql b/old_tests/RunAll.sql index 22b806b0e..06b62c4b8 100644 --- a/old_tests/RunAll.sql +++ b/old_tests/RunAll.sql @@ -328,6 +328,8 @@ begin 'source/core/types', 'source/core/annotations/ut_annotation_parser.pkb', 'source/core/annotations/ut_annotation_parser.pks', + 'source/core/annotations/ut_annotation_cache_manager.pkb', + 'source/core/annotations/ut_annotation_cache_manager.pks', 'source/core/ut_expectation_processor.pkb', 'source/core/ut_expectation_processor.pks', 'source/core/ut_file_mapper.pkb', diff --git a/source/core/annotations/ut_annotated_object.tps b/source/core/annotations/ut_annotated_object.tps index bcfcb6cda..a9e1ae76e 100644 --- a/source/core/annotations/ut_annotated_object.tps +++ b/source/core/annotations/ut_annotated_object.tps @@ -1,4 +1,20 @@ create type ut_annotated_object as object( + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ object_owner varchar2(250), object_name varchar2(250), object_type varchar2(50), diff --git a/source/core/annotations/ut_annotated_objects.tps b/source/core/annotations/ut_annotated_objects.tps index 991a8aa1d..7f01f612c 100644 --- a/source/core/annotations/ut_annotated_objects.tps +++ b/source/core/annotations/ut_annotated_objects.tps @@ -1,3 +1,20 @@ -create type ut_annotated_objects as table of ut_annotated_object +create type ut_annotated_objects as + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ +table of ut_annotated_object / diff --git a/source/core/annotations/ut_annotation.tps b/source/core/annotations/ut_annotation.tps index 27a2cad0f..fdd489dcf 100644 --- a/source/core/annotations/ut_annotation.tps +++ b/source/core/annotations/ut_annotation.tps @@ -1,4 +1,20 @@ create type ut_annotation as object( + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ position number(5,0), name varchar2(1000), text varchar2(4000), diff --git a/source/core/annotations/ut_annotation_cache.sql b/source/core/annotations/ut_annotation_cache.sql index c22b6c5f3..f8aff469b 100644 --- a/source/core/annotations/ut_annotation_cache.sql +++ b/source/core/annotations/ut_annotation_cache.sql @@ -1,12 +1,23 @@ create table ut_annotation_cache ( + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ cache_id number(20,0) not null, annotation_position number(5,0) not null, annotation_name varchar2(1000) not null, annotation_text varchar2(4000), subobject_name varchar2(250), --- subobject_name_not_null varchar2(250) generated always as (nvl(subobject_name,'null')) not null, constraint ut_annotation_cache_pk primary key(cache_id, annotation_position), --- constraint ut_annotation_cache_pk primary key(cache_id, annotation_position, subobject_name_not_null), constraint ut_annotation_cache_fk foreign key(cache_id) references ut_annotation_cache_info(cache_id) on delete cascade ); diff --git a/source/core/annotations/ut_annotation_cache_info.sql b/source/core/annotations/ut_annotation_cache_info.sql index b8d414b5b..859c4a79b 100644 --- a/source/core/annotations/ut_annotation_cache_info.sql +++ b/source/core/annotations/ut_annotation_cache_info.sql @@ -1,12 +1,23 @@ create table ut_annotation_cache_info ( + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ cache_id number(20,0) not null, object_owner varchar2(250) not null, object_name varchar2(250) not null, object_type varchar2(250) not null, parse_time date not null, - is_annotated varchar2(1) not null, constraint ut_annotation_cache_info_pk primary key(cache_id), - constraint ut_annotation_cache_info_uk unique (object_owner, object_name, object_type), - constraint ut_annotation_cache_info_ck1 check (is_annotated in ('Y','N')) + constraint ut_annotation_cache_info_uk unique (object_owner, object_name, object_type) ) organization index; diff --git a/source/core/annotations/ut_annotation_cache_manager.pkb b/source/core/annotations/ut_annotation_cache_manager.pkb index 3db1271b9..a9c5e9916 100644 --- a/source/core/annotations/ut_annotation_cache_manager.pkb +++ b/source/core/annotations/ut_annotation_cache_manager.pkb @@ -1,33 +1,42 @@ create or replace package body ut_annotation_cache_manager as + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 utPLSQL Project - procedure update_cache(a_object ut_annotated_object, a_cache_id integer) is - l_is_annotated varchar2(1); - v_cache_id integer := a_cache_id; + 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. + */ + + procedure update_cache(a_object ut_annotated_object) is + v_cache_id integer; l_current_schema varchar2(250) := ut_utils.ut_owner; pragma autonomous_transaction; begin - if a_object.annotations is not null and a_object.annotations.count > 0 then - l_is_annotated := 'Y'; - else - l_is_annotated := 'N'; - end if; - - if v_cache_id is not null then - update ut_annotation_cache_info i - set i.parse_time = sysdate, - i.is_annotated = l_is_annotated - where i.cache_id = v_cache_id; - else + update ut_annotation_cache_info i + set i.parse_time = sysdate + where (i.object_owner, i.object_name, i.object_type) + in ((a_object.object_owner, a_object.object_name, a_object.object_type)) + returning cache_id into v_cache_id; + if sql%rowcount = 0 then insert into ut_annotation_cache_info - (cache_id, object_owner, object_name, object_type, parse_time, is_annotated) - values (ut_annotation_cache_seq.nextval, a_object.object_owner, a_object.object_name, a_object.object_type, sysdate, l_is_annotated) + (cache_id, object_owner, object_name, object_type, parse_time) + values (ut_annotation_cache_seq.nextval, a_object.object_owner, a_object.object_name, a_object.object_type, sysdate) returning cache_id into v_cache_id; end if; delete from ut_annotation_cache c where cache_id = v_cache_id; - if l_is_annotated = 'Y' then + if a_object.annotations is not null and a_object.annotations.count > 0 then -- begin insert into ut_annotation_cache (cache_id, annotation_position, annotation_name, annotation_text, subobject_name) @@ -43,5 +52,51 @@ create or replace package body ut_annotation_cache_manager as commit; end; -end; + + procedure cleanup_cache(a_objects ut_annotation_cached_objects) is + pragma autonomous_transaction; + begin + + delete from ut_annotation_cache c + where c.cache_id + in (select i.cache_id + from ut_annotation_cache_info i + join table (a_objects) o + using (object_name, object_type, object_owner) + ); + + merge into ut_annotation_cache_info i + using (select o.object_name, o.object_type, o.object_owner + from table(a_objects) o ) o + on (o.object_name = i.object_name + and o.object_type = i.object_type + and o.object_owner = i.object_owner) + when matched then update set parse_time = sysdate + when not matched then insert + (cache_id, object_owner, object_name, object_type, parse_time) + values (ut_annotation_cache_seq.nextval, o.object_owner, o.object_name, o.object_type, sysdate); + + commit; + end; + + function get_annotations_for_objects(a_cached_objects ut_annotation_cached_objects) return sys_refcursor is + l_results sys_refcursor; + begin + open l_results for + select ut_annotated_object( + o.object_owner, o.object_name, o.object_type, + cast( + collect( + ut_annotation( + c.annotation_position, c.annotation_name, c.annotation_text, c.subobject_name + ) order by c.annotation_position + ) as ut_annotations + ) + ) + from table(a_cached_objects) o + join ut_annotation_cache c on o.cache_id = c.cache_id + group by o.object_owner, o.object_name, o.object_type; + return l_results; + end; +end ut_annotation_cache_manager; / diff --git a/source/core/annotations/ut_annotation_cache_manager.pks b/source/core/annotations/ut_annotation_cache_manager.pks index 9462baf54..ce7d1bd7f 100644 --- a/source/core/annotations/ut_annotation_cache_manager.pks +++ b/source/core/annotations/ut_annotation_cache_manager.pks @@ -1,6 +1,26 @@ create or replace package ut_annotation_cache_manager authid definer as + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 utPLSQL Project - procedure update_cache(a_object ut_annotated_object, a_cache_id integer); + 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. + */ + + procedure update_cache(a_object ut_annotated_object); + + function get_annotations_for_objects(a_cached_objects ut_annotation_cached_objects) return sys_refcursor; + + procedure cleanup_cache(a_objects ut_annotation_cached_objects); end; / diff --git a/source/core/annotations/ut_annotation_cache_seq.sql b/source/core/annotations/ut_annotation_cache_seq.sql index 0827892a7..611e97ac0 100644 --- a/source/core/annotations/ut_annotation_cache_seq.sql +++ b/source/core/annotations/ut_annotation_cache_seq.sql @@ -1,2 +1,16 @@ -create sequence ut_annotation_cache_seq cache 20 -/ +create sequence ut_annotation_cache_seq + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ +cache 20; + diff --git a/source/core/annotations/ut_annotation_cached_object.tps b/source/core/annotations/ut_annotation_cached_object.tps new file mode 100644 index 000000000..24fd30e4a --- /dev/null +++ b/source/core/annotations/ut_annotation_cached_object.tps @@ -0,0 +1,24 @@ +create type ut_annotation_cached_object as object( + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ + object_owner varchar2(250), + object_name varchar2(250), + object_type varchar2(250), + needs_refresh varchar2(1), + cache_id number(20,0) + ) +/ diff --git a/source/core/annotations/ut_annotation_cached_objects.tps b/source/core/annotations/ut_annotation_cached_objects.tps new file mode 100644 index 000000000..b1f64711c --- /dev/null +++ b/source/core/annotations/ut_annotation_cached_objects.tps @@ -0,0 +1,19 @@ +create type ut_annotation_cached_objects as + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ +table of ut_annotation_cached_object +/ diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index 9ad68c774..e4d1219bf 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -39,14 +39,18 @@ create or replace package body ut_annotation_parser as ,modifier => 'n'); end; - function get_annotations(a_source varchar2, a_comments tt_comment_list, a_subobject_name varchar2 := null) return ut_annotations is + procedure add_annotations( + a_annotations in out nocopy ut_annotations, + a_source varchar2, + a_comments tt_comment_list, + a_subobject_name varchar2 := null + ) is l_loop_index pls_integer := 1; l_annotation_index pls_integer; l_comment varchar2(32767); l_annotation_str varchar2(32767); l_annotation_text varchar2(32767); l_annotation_name varchar2(1000); - l_annotations ut_annotations := ut_annotations(); begin -- loop while there are unprocessed comment blocks while 0 != nvl(regexp_instr(srcstr => a_source @@ -75,19 +79,17 @@ create or replace package body ut_annotation_parser as ,subexpression => 1)); l_annotation_text := trim(regexp_substr(l_annotation_str, '\((.*?)\)\s*$', subexpression => 1)); - l_annotations.extend; - l_annotations( l_annotations.last) := + a_annotations.extend; + a_annotations( a_annotations.last) := ut_annotation(l_annotation_index, l_annotation_name, l_annotation_text, a_subobject_name); end if; l_loop_index := l_loop_index + 1; end loop; - return l_annotations; - end get_annotations; + end add_annotations; - function get_package_annotations(a_source clob, a_comments tt_comment_list) return ut_annotations is + procedure add_package_annotations(a_annotations in out nocopy ut_annotations, a_source clob, a_comments tt_comment_list) is l_package_comments varchar2(32767); - l_annotations ut_annotations := ut_annotations(); begin l_package_comments := regexp_substr(srcstr => a_source ,pattern => '^\s*(CREATE\s+(OR\s+REPLACE)?(\s+(NON)?EDITIONABLE)?\s+)?PACKAGE\s[^;]*?(\s+(AS|IS)\s+)((.*?{COMMENT#\d+}\s?)+)' @@ -96,18 +98,15 @@ create or replace package body ut_annotation_parser as -- parsing for package annotations if l_package_comments is not null then - l_annotations := get_annotations(l_package_comments, a_comments); + add_annotations(a_annotations, l_package_comments, a_comments); end if; + end add_package_annotations; - return l_annotations; - end; - - function get_procedure_list(a_source clob, a_comments tt_comment_list) return ut_annotations is + procedure add_procedure_annotations(a_annotations in out nocopy ut_annotations, a_source clob, a_comments tt_comment_list) is l_proc_comments varchar2(32767); - l_proc_name t_annotation_name; + l_proc_name varchar2(250); l_annot_proc_ind number; l_annot_proc_block varchar2(32767); - l_annotations ut_annotations := ut_annotations(); begin -- loop through procedures and functions of the package and get all the comment blocks just before it's declaration l_annot_proc_ind := 1; @@ -139,17 +138,15 @@ create or replace package body ut_annotation_parser as ,subexpression => 5)); -- parse the comment block for the syntactically correct annotations and store them as an array - l_annotations := l_annotations multiset union all get_annotations(l_proc_comments, a_comments, l_proc_name); + add_annotations(a_annotations, l_proc_comments, a_comments, l_proc_name); --l_annot_proc_ind := l_annot_proc_ind + length(l_annot_proc_block); l_annot_proc_ind := regexp_instr(srcstr => a_source ,pattern => ';' ,occurrence => 1 - ,position => l_annot_proc_ind + length(l_annot_proc_block)); + ,position => l_annot_proc_ind + length(l_annot_proc_block) ); end loop; - - return l_annotations; - end; + end add_procedure_annotations; function extract_and_replace_comments(a_source in out nocopy clob) return tt_comment_list is l_comments tt_comment_list; @@ -197,20 +194,121 @@ create or replace package body ut_annotation_parser as return l_comments; end extract_and_replace_comments; - $if $$ut_trace $then - procedure print_parse_results(a_annotations ut_annotations) is + function parse_object_annotations(a_source_lines in out nocopy dbms_preprocessor.source_lines_t) return ut_annotations is + l_processed_lines dbms_preprocessor.source_lines_t; + l_source clob; + l_annotations ut_annotations := ut_annotations(); + ex_package_is_wrapped exception; + pragma exception_init(ex_package_is_wrapped, -24241); + begin - dbms_output.put_line('Annotations count: ' || a_annotations.count); - for i in 1 .. a_annotations.count loop - dbms_output.put_line(xmltype(a_annotations(i)).getclobval()); - end loop; - end print_parse_results; - $end + if a_source_lines.count > 0 then + --convert to post-processed source clob + begin + --get post-processed source + l_processed_lines := sys.dbms_preprocessor.get_post_processed_source(a_source_lines); + --convert to clob + for i in 1..l_processed_lines.count loop + ut_utils.append_to_clob(l_source, replace(l_processed_lines(i), chr(13)||chr(10), chr(10))); + end loop; + --parse annotations + l_annotations := parse_object_annotations(l_source); + dbms_lob.freetemporary(l_source); + exception + when ex_package_is_wrapped then + null; + end; + end if; + a_source_lines.delete; + return l_annotations; + end; + + function get_annotated_objects_cursor(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2) return sys_refcursor is + l_result sys_refcursor; + l_ut_owner varchar2(250) := ut_utils.ut_owner; + l_objects_view varchar2(200) := ut_metadata.get_dba_view('dba_objects'); + l_cursor_text long; + begin + l_cursor_text := + q'[select ]'||l_ut_owner||q'[.ut_annotation_cached_object( + object_owner => o.owner, + object_name => o.object_name, + object_type => o.object_type, + needs_refresh => case when o.last_ddl_time < i.parse_time then 'N' else 'Y' end, + cache_id => i.cache_id + ) + from ]'||l_objects_view||q'[ o + left join ut3.ut_annotation_cache_info i + on o.owner = i.object_owner and o.object_name = i.object_name and o.object_type = i.object_type + where o.owner = :a_object_owner + and o.object_type = :a_object_type + and o.status = 'VALID' + and :a_object_name ]'|| case when a_object_name is not null then '= o.object_name' else 'is null' end; + open l_result for l_cursor_text using a_object_owner, a_object_type, a_object_name; + return l_result; + end; - function parse_package_annotations(a_source clob) return ut_annotations is + function get_sources_for_annotations(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2) return sys_refcursor is + l_result sys_refcursor; + l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source'); + begin + open l_result for + q'[select s.name, s.text + from ]'||l_sources_view||q'[ s + where s.type = :a_object_type + and s.owner = :a_object_owner + and s.name + in (select x.name + from ]'||l_sources_view||q'[ x + where x.type = :a_object_type + and x.owner = :a_object_owner + and x.text like '%--%\%%' escape '\' + and :a_object_name ]'|| case when a_object_name is not null then '= x.name' else 'is null' end || q'[ + ) + order by name, line]' + using a_object_type, a_object_owner, a_object_type, a_object_owner, a_object_name; + + return l_result; + end; + + function get_sources_for_annotations(a_object_owner varchar2, a_object_type varchar2, a_objects_to_refresh ut_annotation_cached_objects) return sys_refcursor is + l_result sys_refcursor; + l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source'); + l_card natural; + begin + l_card := ut_utils.scale_cardinality(cardinality(a_objects_to_refresh)); + open l_result for + q'[select /*+ cardinality( r ]'||l_card||q'[ )*/ + s.name, s.text + from table(:a_objects_to_refresh) r + join ]'||l_sources_view||q'[ s + on s.name = r.object_name + where s.type = :a_object_type + and s.owner = :a_object_owner + and s.name + in (select /*+ cardinality( x ]'||l_card||q'[ )*/ + x.name + from table(:a_objects_to_refresh) t + join ]'||l_sources_view||q'[ x + on x.name = t.object_name + where x.type = :a_object_type + and x.owner = :a_object_owner + and x.text like '%--%\%%' escape '\' + ) + order by name, line]' + using a_objects_to_refresh, a_object_type, a_object_owner, a_objects_to_refresh, a_object_type, a_object_owner; + + return l_result; + end; + + ------------------------------------------------------------ + --public definitions + ------------------------------------------------------------ + + function parse_object_annotations(a_source clob) return ut_annotations is l_source clob := a_source; l_comments tt_comment_list; - l_annotations ut_annotations; + l_annotations ut_annotations := ut_annotations(); l_result ut_annotations; begin @@ -220,9 +318,9 @@ create or replace package body ut_annotation_parser as -- this call modifies l_source l_comments := extract_and_replace_comments(l_source); - l_annotations := - get_package_annotations(l_source, l_comments) - multiset union all get_procedure_list(l_source, l_comments); + add_package_annotations(l_annotations, l_source, l_comments); + add_procedure_annotations(l_annotations, l_source, l_comments); + dbms_lob.freetemporary(l_source); select value(x) @@ -230,184 +328,102 @@ create or replace package body ut_annotation_parser as from table(l_annotations) x order by x.position; --- -- printing out parsed structure for debugging --- $if $$ut_trace $then --- print_parse_results(l_result); --- $end + -- printing out parsed structure for debugging + $if $$ut_trace $then + print_parse_results(l_result); + dbms_output.put_line('Annotations count: ' || l_result.count); + for i in 1 .. l_result.count loop + dbms_output.put_line(xmltype(l_result(i)).getclobval()); + end loop; + $end return l_result; - end parse_package_annotations; + end parse_object_annotations; - function get_post_processed_source(a_source_lines ut_varchar2_rows) return clob is - l_lines sys.dbms_preprocessor.source_lines_t; - l_source clob; + function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2 := null) return ut_annotated_objects pipelined is + l_info_rows ut_annotation_cached_objects; + l_in_cache ut_annotation_cached_objects; + l_to_parse ut_annotation_cached_objects := ut_annotation_cached_objects(); + l_cursor sys_refcursor; + l_results ut_annotated_objects; + l_result ut_annotated_object; + c_object_fetch_limit constant integer := 10; + c_lines_fetch_limit constant integer := 1000; + l_lines dbms_preprocessor.source_lines_t; + l_names dbms_preprocessor.source_lines_t; + l_name varchar2(250) := ''''; + l_object_lines dbms_preprocessor.source_lines_t; begin - --convert to preprocessor lines - for i in 1 .. a_source_lines.count loop - l_lines(i) := a_source_lines(i); - end loop; - --get post-processed source - l_lines := sys.dbms_preprocessor.get_post_processed_source(l_lines); - --convert to clob - for i in 1..l_lines.count loop - ut_utils.append_to_clob(l_source, replace(l_lines(i), chr(13)||chr(10), chr(10))); - end loop; - return l_source; - end; + --get information about cached objects + l_cursor := get_annotated_objects_cursor(a_object_owner, a_object_type, a_object_name); + fetch l_cursor bulk collect into l_info_rows; + close l_cursor; - ------------------------------------------------------------ - --public definitions - ------------------------------------------------------------ + --get list of objects in cache + select value(x) bulk collect into l_in_cache from table(l_info_rows) x where x.needs_refresh = 'N'; - function parse_annotations(a_cursor t_object_sources_cur) return ut_annotated_objects pipelined is - l_rec t_object_source; - l_source clob; - l_annotations ut_annotations; - l_result ut_annotated_object; - ex_package_is_wrapped exception; - pragma exception_init(ex_package_is_wrapped, -24241); - - begin - if not a_cursor% isopen then - return; + --if not all in cache, get list of objects to refresh + if l_in_cache.count <= l_info_rows.count then + select value(x) bulk collect into l_to_parse from table(l_info_rows) x where x.needs_refresh = 'Y'; end if; - loop - fetch a_cursor into l_rec; - exit when a_cursor%notfound; - begin - --convert to post-processed source clob - l_source := get_post_processed_source(l_rec.lines); - --parse annotations - l_annotations := parse_package_annotations(l_source); - dbms_lob.freetemporary(l_source); - exception - when ex_package_is_wrapped then - null; - end; - --convert to query results - l_result := ut_annotated_object( l_rec.owner, l_rec.name, l_rec.type, l_annotations); + --pipe annotations from cache + if l_in_cache.count > 0 then + l_cursor := ut_annotation_cache_manager.get_annotations_for_objects(l_in_cache); + loop + fetch l_cursor bulk collect into l_results limit c_object_fetch_limit; + for i in 1 .. l_results.count loop + pipe row (l_results(i)); + end loop; + exit when l_cursor%notfound; + end loop; + close l_cursor; + end if; - ut_annotation_cache_manager.update_cache(l_result, l_rec.cache_id); - if l_annotations is not empty then - pipe row (l_result); + --if some source needs parsing + if l_to_parse.count > 0 then + --do we need to parse all of sources + if l_in_cache.count = 0 then + l_cursor := get_sources_for_annotations(a_object_owner, a_object_type, a_object_name); + else + -- sources need to be filtered by objects to parse + l_cursor := get_sources_for_annotations(a_object_owner, a_object_type, l_to_parse); end if; - end loop; - close a_cursor; - return; - end; + --remove cached annotations data for objects that will be refreshed + ut_annotation_cache_manager.cleanup_cache(l_to_parse); - function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2 := null) return ut_annotated_objects pipelined is - l_objects_view varchar2(200) := ut_metadata.get_dba_view('dba_objects'); - l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source'); - l_obj ut_annotated_object; - l_current_schema varchar2(250) := ut_utils.ut_owner; - l_cursor sys_refcursor; - l_cursor_sql varchar2(32767); - l_object_filter varchar2(32767); - begin - if a_object_name is not null then - l_object_filter := 'and o.object_name = :a_object_name'; - else - l_object_filter := 'and :a_object_name is null'; - end if; - l_cursor_sql := - q'[with object_cache_info - as (select /*+ cardinality(i 10000) */ o.owner as object_owner, o.object_name, o.object_type, i.cache_id, - case when o.last_ddl_time < i.parse_time then 'N' else 'Y' end as cache_stale - from ]'||l_objects_view||q'[ o - left join ]'||l_current_schema||q'[.ut_annotation_cache_info i - on o.owner = i.object_owner and o.object_name = i.object_name and o.object_type = i.object_type - where o.object_type = :a_object_type and o.owner = :a_object_owner ]'||l_object_filter||q'[ - and o.status = 'VALID' - ), - obj_info_with_source - as (select /*+ cardinality(o 10000) */o.object_owner, o.object_name, o.object_type, o.cache_stale, o.cache_id, s.line, s.text - from object_cache_info o - left join ]'||l_sources_view||q'[ s - on s.name = o.object_name - and s.type = :a_object_type - and s.owner = :a_object_owner - ]'||l_object_filter||q'[ - and o.cache_stale = 'Y' - ), - obj_info_source_grouped - as (select o.object_owner, o.object_name, o.object_type, o.cache_stale, o.cache_id, - cast(collect(o.text order by o.line) as ]'||l_current_schema||q'[.ut_varchar2_rows) as texts - from obj_info_with_source o - group by o.object_owner, o.object_type, o.object_name, o.cache_stale, cache_id - order by o.object_owner, o.object_type, o.object_name - ) - select obj - from ( - select ]'||l_current_schema||q'[.ut_annotated_object(o.object_owner, o.object_name, o.object_type, - cast(collect( - ]'||l_current_schema||q'[.ut_annotation(c.annotation_position, c.annotation_name, c.annotation_text, c.subobject_name) order by c.annotation_position ) - as ]'||l_current_schema||q'[.ut_annotations) - ) as obj - from obj_info_source_grouped o - join ]'||l_current_schema||q'[.ut_annotation_cache c - on o.cache_id = c.cache_id - where o.cache_stale = 'N' - group by o.object_owner, o.object_name, o.object_type - union all - -- this query needs to be executed as last part of the union - -- as it is updating the cache_stale flag in an autonomous transaction - select value(c) as obj - from table( - ]'||l_current_schema||q'[.ut_annotation_parser.parse_annotations( - cursor(select o.object_owner, o.object_name, o.object_type, o.cache_id, o.texts from obj_info_source_grouped o where o.cache_stale = 'Y') - ) - ) c - ) a - order by a.obj.object_owner, a.obj.object_type, a.obj.object_name]'; - open l_cursor for l_cursor_sql using a_object_type, a_object_owner, a_object_name, a_object_type, a_object_owner, a_object_name; - loop - fetch l_cursor into l_obj; - exit when l_cursor%notfound; - pipe row (l_obj); - end loop; - close l_cursor; - return; - end; + loop + fetch l_cursor bulk collect into l_names, l_lines limit c_lines_fetch_limit; - function get_package_annotations(a_owner_name varchar2, a_name varchar2) return ut_annotations is - l_source clob; - ex_package_is_wrapped exception; - pragma exception_init(ex_package_is_wrapped, -24241); - begin - return parse_package_annotations(ut_metadata.get_package_spec_source(a_owner_name, a_name)); - exception - when ex_package_is_wrapped then - return ut_annotations(); - end; + for i in 1 .. l_names.count loop + if l_names(i) != l_name then + l_result := ut_annotated_object(a_object_owner, l_name, a_object_type, parse_object_annotations(l_object_lines)); + ut_annotation_cache_manager.update_cache(l_result); + if l_result.annotations.count > 0 then + pipe row (l_result); + end if; + end if; - -- parse the annotation parameters and return as key-value pair array - function parse_annotation_params(a_annotation_text varchar2) return tt_annotation_params is - l_annotation_params tt_annotation_params; - l_param_str varchar2(32767); - l_param_item typ_annotation_param; - l_param_item_empty typ_annotation_param; - c_annot_param_pattern constant varchar2(50) := '(.+?)(,|$)'; - begin - if a_annotation_text is not null then - for param_ind in 1 .. regexp_count(a_annotation_text, c_annot_param_pattern) loop - l_param_str := regexp_substr(srcstr => a_annotation_text - ,pattern => c_annot_param_pattern - ,occurrence => param_ind - ,subexpression => 1); - l_param_item := l_param_item_empty; - l_param_item.key := regexp_substr(srcstr => l_param_str - ,pattern => '(' || c_regexp_identifier || ')\s*=' - ,modifier => 'i' - ,subexpression => 1); - l_param_item.val := trim(regexp_substr(l_param_str, '(.+?=)?(.*$)', subexpression => 2)); + l_name := l_names(i); + l_object_lines(l_object_lines.count+1) := l_lines(i); + + end loop; + exit when l_cursor%notfound; - l_annotation_params(l_annotation_params.count + 1) := l_param_item; end loop; + + if l_name is not null then + l_result := ut_annotated_object(a_object_owner, l_name, a_object_type, parse_object_annotations(l_object_lines)); + ut_annotation_cache_manager.update_cache(l_result); + if l_result.annotations.count > 0 then + pipe row (l_result); + end if; + end if; + + close l_cursor; end if; - return l_annotation_params; + end; end ut_annotation_parser; diff --git a/source/core/annotations/ut_annotation_parser.pks b/source/core/annotations/ut_annotation_parser.pks index cb8fa6ae7..09f93cc70 100644 --- a/source/core/annotations/ut_annotation_parser.pks +++ b/source/core/annotations/ut_annotation_parser.pks @@ -16,71 +16,27 @@ create or replace package ut_annotation_parser authid current_user as limitations under the License. */ - /* - package: ut_annotation_parser - - Responsible for parsing and accessing utplsql annotations. - - */ - - subtype t_annotation_name is varchar2(1000); - subtype t_procedure_name is varchar2(250); - - /* - type: typ_annotation_param - - a key/value pair of annotation parameters - - example: - --%test(name=A name of the test) - will be stored as: - typ_annotation_param( key=> 'name', value=>'A name of the test' ) - */ - type typ_annotation_param is record( - key varchar2(255) - ,val varchar2(4000)); - - /* - type: typ_annotation_param - a list of typ_annotation_param - */ - type tt_annotation_params is table of typ_annotation_param index by pls_integer; - - type t_object_source is record( - owner varchar2(250), - name varchar2(250), - type varchar2(50), - cache_id integer, - lines ut_varchar2_rows - ); - - type t_object_sources_cur is ref cursor return t_object_source; - - /* - INTERNAL USE ONLY - */ - function parse_package_annotations(a_source clob) return ut_annotations; - - function parse_annotations(a_cursor t_object_sources_cur) return ut_annotated_objects pipelined; - + /** + * Reads database source code, parses it and returns annotations + */ + + /** + * Parses source code and converts it to annotations + * + * @param a_source clob containing source code to be parsed + * @return array containing annotations + */ + function parse_object_annotations(a_source clob) return ut_annotations; + + /** + * Parses an object or all objects of a specified type for database schema. + * Pesults are returned in a form of a pipelined function. + * @param a_object_owner schema name to be parsed + * @param a_object_type type of object to be parsed + * @param a_object_name name of object to be parsed - optional + * @return array containing annotated objects along with annotations for each object (nested) + */ function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2 := null) return ut_annotated_objects pipelined; - /* - function: get_package_annotations - - get annotations for specified package specification and return its annotated schema - */ - function get_package_annotations(a_owner_name varchar2, a_name varchar2) return ut_annotations; - - function get_post_processed_source(a_source_lines ut_varchar2_rows) return clob; - - - /* - function: parse_annotation_params - - parses annotation parameters from annotation text string - */ - function parse_annotation_params(a_annotation_text varchar2) return tt_annotation_params; - end ut_annotation_parser; / diff --git a/source/core/annotations/ut_annotations.tps b/source/core/annotations/ut_annotations.tps index 55f71e1de..d19f4943d 100644 --- a/source/core/annotations/ut_annotations.tps +++ b/source/core/annotations/ut_annotations.tps @@ -1,2 +1,19 @@ -create type ut_annotations as table of ut_annotation +create type ut_annotations + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ +as table of ut_annotation / diff --git a/source/core/ut_output_buffer_tmp.sql b/source/core/ut_output_buffer_tmp.sql index 3b9905f24..3c3275c6e 100644 --- a/source/core/ut_output_buffer_tmp.sql +++ b/source/core/ut_output_buffer_tmp.sql @@ -22,7 +22,7 @@ create table ut_output_buffer_tmp$( text varchar2(4000), is_finished number(1,0) default 0 not null, start_date date not null, - constraint ut_output_buffer_tmp_pk primary key(start_date, reporter_id, message_id), + constraint ut_output_buffer_tmp_pk primary key(reporter_id, message_id), constraint ut_output_buffer_tmp_ck check(is_finished = 0 and text is not null or is_finished = 1 and text is null) ) nologging nomonitoring initrans 100 ; diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index 0581608c1..5c4487875 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -52,13 +52,13 @@ create or replace package body ut_suite_manager is return l_info; end; - function config_package(a_object ut_annotated_object) return ut_logical_suite is + function create_suite(a_object ut_annotated_object) return ut_logical_suite is l_is_suite boolean := false; l_is_test boolean := false; l_suite_disabled boolean := false; l_test_disabled boolean := false; l_suite_items ut_suite_items := ut_suite_items(); - l_suite_name ut_annotation_parser.t_annotation_name; + l_suite_name varchar2(4000); l_default_setup_proc varchar2(250 char); l_default_teardown_proc varchar2(250 char); @@ -66,7 +66,7 @@ create or replace package body ut_suite_manager is l_suite_teardown_proc varchar2(250 char); l_suite_path varchar2(4000 char); - l_proc_name ut_annotation_parser.t_procedure_name; + l_proc_name varchar2(250 char); l_suite ut_logical_suite; l_test ut_test; @@ -181,43 +181,7 @@ create or replace package body ut_suite_manager is return l_suite; - end config_package; - - function config_package(a_owner_name varchar2, a_object_name varchar2) return ut_logical_suite is - l_owner_name varchar2(250 char); - l_object_name varchar2(250 char); - l_cursor sys_refcursor; - l_annotated_object ut_annotated_object; - e_insufficient_priv exception; - pragma exception_init(e_insufficient_priv,-01031); - begin - l_owner_name := a_owner_name; - l_object_name := a_object_name; - begin - ut_metadata.do_resolve(a_owner => l_owner_name, a_object => l_object_name); - exception - when e_insufficient_priv then - return null; - end; - - open l_cursor for select value(x) from table (ut3.ut_annotation_parser.get_annotated_objects(l_owner_name, 'PACKAGE', l_object_name))x; - fetch l_cursor into l_annotated_object; - close l_cursor; - return config_package(l_annotated_object); - - end; - - function build_suites(a_cursor sys_refcursor) return ut_suite_items pipelined is - l_object ut_annotated_object; - begin - loop - fetch a_cursor into l_object; - exit when a_cursor%notfound; - pipe row (config_package(l_object)); - end loop; - close a_cursor; - return; - end; + end create_suite; procedure update_cache(a_owner_name varchar2, a_schema_suites tt_schema_suites, a_total_obj_cnt integer) is begin @@ -231,13 +195,13 @@ create or replace package body ut_suite_manager is end; procedure config_schema(a_owner_name varchar2) is - l_suite ut_logical_suite; - - l_all_suites tt_schema_suites; - l_ind varchar2(4000 char); - l_path varchar2(4000 char); - l_root varchar2(4000 char); - l_root_suite ut_logical_suite; + l_suite ut_logical_suite; + l_annotated_objects ut_annotated_objects; + l_all_suites tt_schema_suites; + l_ind varchar2(4000 char); + l_path varchar2(4000 char); + l_root varchar2(4000 char); + l_root_suite ut_logical_suite; type t_object_name is record( owner all_objects.owner%type, @@ -292,38 +256,38 @@ create or replace package body ut_suite_manager is end if; end; - $if $$ut_trace $then - procedure print(a_item ut_suite_item, a_pad pls_integer) is - l_suite ut_logical_suite; - l_pad varchar2(1000) := lpad(' ', a_pad, ' '); - begin - if a_item is of (ut_logical_suite) then - dbms_output.put_line(l_pad || 'Suite: ' || a_item.name || '(' || a_item.path || ')'); - dbms_output.put_line(l_pad || 'Items: '); - l_suite := treat(a_item as ut_logical_suite); - for i in 1 .. l_suite.items.count loop - print(l_suite.items(i), a_pad + 2); - end loop; - else - dbms_output.put_line(l_pad || 'Test: ' || a_item.name || '(' || a_item.path || ')' ); - end if; - end print; - $end +-- $if $$ut_trace $then +-- procedure print(a_item ut_suite_item, a_pad pls_integer) is +-- l_suite ut_logical_suite; +-- l_pad varchar2(1000) := lpad(' ', a_pad, ' '); +-- begin +-- if a_item is of (ut_logical_suite) then +-- dbms_output.put_line(l_pad || 'Suite: ' || a_item.name || '(' || a_item.path || ')'); +-- dbms_output.put_line(l_pad || 'Items: '); +-- l_suite := treat(a_item as ut_logical_suite); +-- for i in 1 .. l_suite.items.count loop +-- print(l_suite.items(i), a_pad + 2); +-- end loop; +-- else +-- dbms_output.put_line(l_pad || 'Test: ' || a_item.name || '(' || a_item.path || ')' ); +-- end if; +-- end print; +-- $end begin g_schema_object_path_map.delete(a_owner_name); -- form the single-dimension list of suites constructed from parsed packages - for i in ( - select treat(value(t) as ut3.ut_logical_suite) suite - from table( - ut3.ut_suite_manager.build_suites( - cursor( select value(x) from table (ut3.ut_annotation_parser.get_annotated_objects(a_owner_name, 'PACKAGE'))x ) - ) - ) t - ) loop - if i.suite is not null then - l_all_suites(i.suite.path) := i.suite; - g_schema_object_path_map(a_owner_name)(i.suite.object_name) := i.suite.path; + execute immediate + q'[select value(x) + from table( + ]'||ut_utils.ut_owner||q'[.ut_annotation_parser.get_annotated_objects(:a_owner_name, 'PACKAGE') + )x ]' + bulk collect into l_annotated_objects using a_owner_name; + for i in 1 .. l_annotated_objects.count loop + l_suite := create_suite(l_annotated_objects(i)); + if l_suite is not null then + l_all_suites(l_suite.path) := l_suite; + g_schema_object_path_map(a_owner_name)(l_suite.object_name) := l_suite.path; end if; end loop; @@ -561,7 +525,7 @@ create or replace package body ut_suite_manager is l_package_name := regexp_substr(l_path, '^[A-Za-z0-9$#_]+\.([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?$', subexpression => 1); l_procedure_name := regexp_substr(l_path, '^[A-Za-z0-9$#_]+\.([A-Za-z0-9$#_]+)(\.([A-Za-z0-9$#_]+))?$', subexpression => 3); - if not g_schema_object_path_map(l_schema).exists(l_package_name) then + if g_schema_object_path_map.exists(l_schema) and not g_schema_object_path_map(l_schema).exists(l_package_name) then raise_application_error(ut_utils.gc_suite_package_not_found,'Suite package '||l_schema||'.'||l_package_name|| ' not found'); end if; l_path := rtrim(l_schema || ':' || g_schema_object_path_map(l_schema)(l_package_name) || '.' || l_procedure_name, '.'); diff --git a/source/core/ut_suite_manager.pks b/source/core/ut_suite_manager.pks index 50f2a5774..112c0287e 100644 --- a/source/core/ut_suite_manager.pks +++ b/source/core/ut_suite_manager.pks @@ -16,15 +16,27 @@ create or replace package ut_suite_manager authid current_user is limitations under the License. */ - --builds individual suites from a cursor of objects and their annotations - function build_suites(a_cursor sys_refcursor) return ut_suite_items pipelined; - - --for testing purposes only - function config_package(a_owner_name varchar2, a_object_name varchar2) return ut_logical_suite; - + /** + * Reads database source code, parses it and returns annotations + */ + + /** + * @private + * + * Returns a list of Unit Test packages that exist in a given database schema + * + * @param a_schema_names list of schemas to return the information for + * @return array containing unit test schema and object names + */ function get_schema_ut_packages(a_schema_names ut_varchar2_rows) return ut_object_names; - --INTERNAL USE + /** + * Builds a hierarchical suites based on given suite-paths + * + * @param a_paths list of suite-paths or procedure names or package names or schema names + * @return array containing root suites-ready to be executed + * + */ function configure_execution_by_path(a_paths in ut_varchar2_list) return ut_suite_items; end ut_suite_manager; diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index bd2355235..9b935ddb8 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -378,8 +378,10 @@ create or replace package body ut_utils is end; procedure cleanup_temp_tables is + pragma autonomous_transaction; begin execute immediate 'delete from ut_cursor_data'; + commit; end; function to_version(a_version_no varchar2) return t_version is @@ -403,5 +405,10 @@ create or replace package body ut_utils is return sys_context('userenv','current_schema'); end; + function scale_cardinality(a_cardinality natural) return natural is + begin + return nvl(trunc(power(10,(floor(log(10,a_cardinality))+1))/3),0); + end; + end ut_utils; / diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 49b326fc0..6f28f43c2 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -16,11 +16,10 @@ create or replace package ut_utils authid definer is limitations under the License. */ - /* - Package: ut_utils - a collection of tools used throught utplsql along with helper functions. - - */ + /** + * Common utilities and constants used throughout utPLSQL framework + * + */ gc_version constant varchar2(50) := 'X.X.X.X'; @@ -109,17 +108,13 @@ create or replace package ut_utils authid definer is ); - /* - Function: test_result_to_char - returns a string representation of a test_result. - - Parameters: - a_test_result - . - - Returns: - a_test_result as string. - - */ + /** + * Converts test results into strings + * + * @param a_test_result numeric representation of test result + * + * @return a string representation of a test_result. + */ function test_result_to_char(a_test_result integer) return varchar2; function to_test_result(a_test boolean) return integer; @@ -127,6 +122,7 @@ create or replace package ut_utils authid definer is function gen_savepoint_name return varchar2; procedure debug_log(a_message varchar2); + procedure debug_log(a_message clob); function to_string(a_value varchar2, a_qoute_char varchar2 := '''') return varchar2; @@ -155,76 +151,66 @@ create or replace package ut_utils authid definer is function int_to_boolean(a_value integer) return boolean; - /* - Procedure: validate_rollback_type - - Validates passed value against supported rollback types - */ + /** + * Validates passed value against supported rollback types + */ procedure validate_rollback_type(a_rollback_type number); - /* - Function: string_to_table - - Parameters: - a_string - the text to be split. - a_delimiter - the delimiter character or string - a_skip_leading_delimiter - determines if the leading delimiter should be ignored, used by clob_to_table - - Returns: - ut_varchar2_list - table of string - - Splits a given string into table of string by delimiter. - The delimiter gets removed. - If null passed as any of the parameters, empty table is returned. - If no occurence of a_delimiter found in a_text then text is returned as a single row of the table. - If no text between delimiters found then an empty row is returned, example: - string_to_table( 'a,,b', ',' ) gives table ut_varchar2_list( 'a', null, 'b' ); - */ + /** + * + * Splits a given string into table of string by delimiter. + * The delimiter gets removed. + * If null passed as any of the parameters, empty table is returned. + * If no occurence of a_delimiter found in a_text then text is returned as a single row of the table. + * If no text between delimiters found then an empty row is returned, example: + * string_to_table( 'a,,b', ',' ) gives table ut_varchar2_list( 'a', null, 'b' ); + * + * @param a_string the text to be split. + * @param a_delimiter the delimiter character or string + * @param a_skip_leading_delimiter determines if the leading delimiter should be ignored, used by clob_to_table + * + * @return table of varchar2 values + */ function string_to_table(a_string varchar2, a_delimiter varchar2:= chr(10), a_skip_leading_delimiter varchar2 := 'N') return ut_varchar2_list; - /* - Function: clob_to_table - - Parameters: - a_clob - the text to be split. - a_delimiter - the delimiter character or string (default chr(10) ) - a_max_amount - the maximum length of returned string (default 8191) - - Returns: - ut_varchar2_list - table of string - - Splits a given string into table of string by delimiter. - Default value of a_max_amount is 8191 because of code can contains multibyte character. - The delimiter gets removed. - If null passed as any of the parameters, empty table is returned. - If split text is longer than a_max_amount it gets split into pieces of a_max_amount. - If no text between delimiters found then an empty row is returned, example: - string_to_table( 'a,,b', ',' ) gives table ut_varchar2_list( 'a', null, 'b' ); - */ + /** + * Splits a given string into table of string by delimiter. + * Default value of a_max_amount is 8191 because of code can contains multibyte character. + * The delimiter gets removed. + * If null passed as any of the parameters, empty table is returned. + * If split text is longer than a_max_amount it gets split into pieces of a_max_amount. + * If no text between delimiters found then an empty row is returned, example: + * string_to_table( 'a,,b', ',' ) gives table ut_varchar2_list( 'a', null, 'b' ); + * + * @param a_clob the text to be split. + * @param a_delimiter the delimiter character or string (default chr(10) ) + * @param a_max_amount the maximum length of returned string (default 8191) + * @return table of varchar2 values + */ 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; - /* - Returns time difference in seconds (with miliseconds) between given timestamps - */ + /** + * Returns time difference in seconds (with miliseconds) between given timestamps + */ function time_diff(a_start_time timestamp with time zone, a_end_time timestamp with time zone) return number; - /* - * Returns a text indented with spaces except the first line. - */ + /** + * Returns a text indented with spaces except the first line. + */ function indent_lines(a_text varchar2, a_indent_size integer := 4, a_include_first_line boolean := false) return varchar2; - /* - * Returns a list of object that are part of utPLSQL framework - */ + /** + * Returns a list of object that are part of utPLSQL framework + */ function get_utplsql_objects_list return ut_object_names; - /* - * Append a line to the end of ut_varchar2_lst - */ + /** + * Append a line to the end of ut_varchar2_lst + */ procedure append_to_varchar2_list(a_list in out nocopy ut_varchar2_list, a_line varchar2); procedure append_to_clob(a_src_clob in out nocopy clob, a_new_data clob); @@ -233,13 +219,13 @@ create or replace package ut_utils authid definer is function convert_collection(a_collection ut_varchar2_list) return ut_varchar2_rows; /** - * Set session's action and module using dbms_application_info - */ + * Set session's action and module using dbms_application_info + */ procedure set_action(a_text in varchar2); /** - * Set session's client info using dbms_application_info - */ + * Set session's client info using dbms_application_info + */ procedure set_client_info(a_text in varchar2); function to_xpath(a_list varchar2, a_ancestors varchar2 := '/*/') return varchar2; @@ -249,15 +235,31 @@ create or replace package ut_utils authid definer is procedure cleanup_temp_tables; /** - * Converts version string into version record - * - * @param a_version_no string representation of version in format vX.X.X.X where X is a positive integer - * @return t_version record with up to four positive numbers containing version - * @throws 20214 if passed version string is not matching version pattern - */ + * Converts version string into version record + * + * @param a_version_no string representation of version in format vX.X.X.X where X is a positive integer + * @return t_version record with up to four positive numbers containing version + * @throws 20214 if passed version string is not matching version pattern + */ function to_version(a_version_no varchar2) return t_version; + + /** + * Function is used to reference to utPLSQL owned objects in dynamic sql statements executed from packages with invoker rights + * + * @return the name of the utPSQL schema owner + */ function ut_owner return varchar2; + + /** + * Used in dynamic sql select statements to maintain balance between + * number of hard-parses and optimiser accurancy for cardinality of collections + * + * + * @return 3, for inputs of: 1-9; 33 for input of 10 - 99; 333 for (100 - 999) + */ + function scale_cardinality(a_cardinality natural) return natural; + end ut_utils; / diff --git a/source/create_synonyms_and_grants_for_public.sql b/source/create_synonyms_and_grants_for_public.sql index 8353f60d5..93c3c0e69 100644 --- a/source/create_synonyms_and_grants_for_public.sql +++ b/source/create_synonyms_and_grants_for_public.sql @@ -75,6 +75,8 @@ grant select on &&ut3_owner..ut_annotation_cache_info to public; grant select on &&ut3_owner..ut_annotation_cache to public; grant execute on &&ut3_owner..ut_annotation_cache_manager to public; grant execute on &&ut3_owner..ut_annotation_parser to public; +grant execute on &&ut3_owner..ut_annotation_cached_objects to public; +grant execute on &&ut3_owner..ut_annotation_cached_object to public; prompt Creating synonyms for UTPLSQL objects in &&ut3_owner schema to PUBLIC diff --git a/source/create_synonyms_and_grants_for_user.sql b/source/create_synonyms_and_grants_for_user.sql index 76dd3c0a9..9938652cd 100644 --- a/source/create_synonyms_and_grants_for_user.sql +++ b/source/create_synonyms_and_grants_for_user.sql @@ -95,6 +95,8 @@ grant select on &&ut3_owner..ut_annotation_cache_info to &ut3_user; grant select on &&ut3_owner..ut_annotation_cache to &ut3_user; grant execute on &&ut3_owner..ut_annotation_cache_manager to &ut3_user; grant execute on &&ut3_owner..ut_annotation_parser to &ut3_user; +grant execute on &&ut3_owner..ut_annotation_cached_objects to &ut3_user; +grant execute on &&ut3_owner..ut_annotation_cached_object to &ut3_user; prompt Creating synonyms for UTPLSQL objects in &&ut3_owner schema to user &&ut3_user diff --git a/source/install.sql b/source/install.sql index 5b3e6493f..960e4d991 100644 --- a/source/install.sql +++ b/source/install.sql @@ -79,6 +79,8 @@ alter session set plsql_warnings = 'ENABLE:ALL', 'DISABLE:(5004,5018,6000,6001,6 @@install_component.sql 'core/annotations/ut_annotations.tps' @@install_component.sql 'core/annotations/ut_annotated_object.tps' @@install_component.sql 'core/annotations/ut_annotated_objects.tps' +@@install_component.sql 'core/annotations/ut_annotation_cached_object.tps' +@@install_component.sql 'core/annotations/ut_annotation_cached_objects.tps' @@install_component.sql 'core/annotations/ut_annotation_cache_seq.sql' @@install_component.sql 'core/annotations/ut_annotation_cache_info.sql' @@install_component.sql 'core/annotations/ut_annotation_cache.sql' diff --git a/source/uninstall.sql b/source/uninstall.sql index 6a3ac600c..a8f567150 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -182,6 +182,10 @@ drop table ut_annotation_cache_info cascade constraints; drop sequence ut_annotation_cache_seq; +drop type ut_annotation_cached_objects force; + +drop type ut_annotation_cached_object force; + drop type ut_annotated_objects force; drop type ut_annotated_object force; diff --git a/test/ut_annotation_parser/test_annotation_parser.pkb b/test/ut_annotation_parser/test_annotation_parser.pkb index 9af022b3d..8b6dc5028 100644 --- a/test/ut_annotation_parser/test_annotation_parser.pkb +++ b/test/ut_annotation_parser/test_annotation_parser.pkb @@ -18,7 +18,7 @@ create or replace package body test_annotation_parser is END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert @@ -49,7 +49,7 @@ create or replace package body test_annotation_parser is END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( @@ -95,7 +95,7 @@ create or replace package body test_annotation_parser is END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( @@ -127,7 +127,7 @@ create or replace package body test_annotation_parser is END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( @@ -155,7 +155,7 @@ create or replace package body test_annotation_parser is END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( @@ -183,7 +183,7 @@ create or replace package body test_annotation_parser is END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( @@ -214,7 +214,7 @@ create or replace package body test_annotation_parser is END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( @@ -242,7 +242,7 @@ create or replace package body test_annotation_parser is END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( @@ -274,7 +274,7 @@ create or replace package body test_annotation_parser is END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( @@ -288,14 +288,16 @@ create or replace package body test_annotation_parser is end; procedure ignore_wrapped_package is - l_actual ut3.ut_annotations; - pragma autonomous_transaction; + l_cursor sys_refcursor; begin - - l_actual := ut3.ut_annotation_parser.get_package_annotations(user, 'TST_WRAPPED_PCK'); - - ut.expect(l_actual.count).to_equal(0); - + --Act + open l_cursor for + select * + from table( + ut3.ut_annotation_parser.get_annotated_objects(user, 'PACKAGE','TST_WRAPPED_PCK') + ); + --Assert + ut.expect(l_cursor).to_be_empty(); end; procedure cre_wrapped_pck is @@ -324,15 +326,13 @@ END; l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param := null; - --l_results ut_expectation_results; begin l_source := 'PACKAGE test_tt AS -- %suite(Name of suite (including some brackets) and some more text) END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( @@ -346,7 +346,6 @@ END;'; l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS @@ -361,7 +360,7 @@ END;'; END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( @@ -385,7 +384,7 @@ END;'; END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( @@ -402,7 +401,6 @@ END;'; l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; - l_ann_param ut3.ut_annotation_parser.typ_annotation_param; begin l_source := 'PACKAGE test_tt AS -- %suite @@ -414,7 +412,7 @@ END;'; END;'; --Act - l_actual := ut3.ut_annotation_parser.parse_package_annotations(l_source); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert l_expected := ut3.ut_annotations( diff --git a/test/ut_reporters/test_coverage.pkb b/test/ut_reporters/test_coverage.pkb index 7feba8b46..bb953bd40 100644 --- a/test/ut_reporters/test_coverage.pkb +++ b/test/ut_reporters/test_coverage.pkb @@ -12,6 +12,7 @@ create or replace package body test_coverage is procedure create_dummy_coverage_package is begin + dbms_output.put_line('creating DUMMY_COVERAGE'); execute immediate q'[create or replace package DUMMY_COVERAGE is procedure do_stuff; end;]'; @@ -29,6 +30,7 @@ create or replace package body test_coverage is procedure create_dummy_coverage_test is begin + dbms_output.put_line('creating TEST_DUMMY_COVERAGE'); execute immediate q'[create or replace package TEST_DUMMY_COVERAGE is --%suite(dummy coverage test) @@ -72,7 +74,9 @@ create or replace package body test_coverage is procedure cleanup_dummy_coverage is pragma autonomous_transaction; begin + dbms_output.put_line('dopping TEST_DUMMY_COVERAGE'); execute immediate q'[drop package test_dummy_coverage]'; + dbms_output.put_line('dopping DUMMY_COVERAGE'); execute immediate q'[drop package dummy_coverage]'; delete from ut3.plsql_profiler_data where runid = g_run_id; delete from ut3.plsql_profiler_units where runid = g_run_id; From dbf25380b3f5dd4df0964fb0db27ad3dd4cf824a Mon Sep 17 00:00:00 2001 From: Jacek Date: Sat, 14 Oct 2017 21:07:08 +0100 Subject: [PATCH 10/19] Fixed failing test and test for Wrapped package. To test wrapped package we need to have an annotation-like text in it. This way the exception handler `when ex_package_is_wrapped` gets tested after adding source filters --- ...eTestAnnotationsParsingTimeHugePackage.sql | 2 +- old_tests/RunAll.sql | 2 + .../core/annotations/ut_annotation_parser.pkb | 213 ++---------------- .../core/annotations/ut_annotation_parser.pks | 24 +- source/core/ut_suite_manager.pkb | 2 +- source/install.sql | 2 + source/uninstall.sql | 2 + .../test_annotation_parser.pkb | 130 +++++------ .../test_annotation_parser.pks | 55 ++--- 9 files changed, 126 insertions(+), 306 deletions(-) diff --git a/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql b/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql index 53afcda16..8d9ecad0c 100644 --- a/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql +++ b/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql @@ -8,7 +8,7 @@ set echo off declare l_suites ut_suite_items; begin - l_suites := ut_suite_manager.configure_execution_by_path(USER||'.TST_PKG_HUGE'); + l_suites := ut_suite_manager.configure_execution_by_path(ut_varchar2_list(USER||'.TST_PKG_HUGE')); end; / diff --git a/old_tests/RunAll.sql b/old_tests/RunAll.sql index 06b62c4b8..e99dcd302 100644 --- a/old_tests/RunAll.sql +++ b/old_tests/RunAll.sql @@ -326,6 +326,8 @@ begin 'source/api/ut_runner.pks', 'source/core/coverage', 'source/core/types', + 'source/core/annotations/ut_annotation_manager.pkb', + 'source/core/annotations/ut_annotation_manager.pks', 'source/core/annotations/ut_annotation_parser.pkb', 'source/core/annotations/ut_annotation_parser.pks', 'source/core/annotations/ut_annotation_cache_manager.pkb', diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index e4d1219bf..34402e71b 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -194,113 +194,6 @@ create or replace package body ut_annotation_parser as return l_comments; end extract_and_replace_comments; - function parse_object_annotations(a_source_lines in out nocopy dbms_preprocessor.source_lines_t) return ut_annotations is - l_processed_lines dbms_preprocessor.source_lines_t; - l_source clob; - l_annotations ut_annotations := ut_annotations(); - ex_package_is_wrapped exception; - pragma exception_init(ex_package_is_wrapped, -24241); - - begin - if a_source_lines.count > 0 then - --convert to post-processed source clob - begin - --get post-processed source - l_processed_lines := sys.dbms_preprocessor.get_post_processed_source(a_source_lines); - --convert to clob - for i in 1..l_processed_lines.count loop - ut_utils.append_to_clob(l_source, replace(l_processed_lines(i), chr(13)||chr(10), chr(10))); - end loop; - --parse annotations - l_annotations := parse_object_annotations(l_source); - dbms_lob.freetemporary(l_source); - exception - when ex_package_is_wrapped then - null; - end; - end if; - a_source_lines.delete; - return l_annotations; - end; - - function get_annotated_objects_cursor(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2) return sys_refcursor is - l_result sys_refcursor; - l_ut_owner varchar2(250) := ut_utils.ut_owner; - l_objects_view varchar2(200) := ut_metadata.get_dba_view('dba_objects'); - l_cursor_text long; - begin - l_cursor_text := - q'[select ]'||l_ut_owner||q'[.ut_annotation_cached_object( - object_owner => o.owner, - object_name => o.object_name, - object_type => o.object_type, - needs_refresh => case when o.last_ddl_time < i.parse_time then 'N' else 'Y' end, - cache_id => i.cache_id - ) - from ]'||l_objects_view||q'[ o - left join ut3.ut_annotation_cache_info i - on o.owner = i.object_owner and o.object_name = i.object_name and o.object_type = i.object_type - where o.owner = :a_object_owner - and o.object_type = :a_object_type - and o.status = 'VALID' - and :a_object_name ]'|| case when a_object_name is not null then '= o.object_name' else 'is null' end; - open l_result for l_cursor_text using a_object_owner, a_object_type, a_object_name; - return l_result; - end; - - function get_sources_for_annotations(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2) return sys_refcursor is - l_result sys_refcursor; - l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source'); - begin - open l_result for - q'[select s.name, s.text - from ]'||l_sources_view||q'[ s - where s.type = :a_object_type - and s.owner = :a_object_owner - and s.name - in (select x.name - from ]'||l_sources_view||q'[ x - where x.type = :a_object_type - and x.owner = :a_object_owner - and x.text like '%--%\%%' escape '\' - and :a_object_name ]'|| case when a_object_name is not null then '= x.name' else 'is null' end || q'[ - ) - order by name, line]' - using a_object_type, a_object_owner, a_object_type, a_object_owner, a_object_name; - - return l_result; - end; - - function get_sources_for_annotations(a_object_owner varchar2, a_object_type varchar2, a_objects_to_refresh ut_annotation_cached_objects) return sys_refcursor is - l_result sys_refcursor; - l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source'); - l_card natural; - begin - l_card := ut_utils.scale_cardinality(cardinality(a_objects_to_refresh)); - open l_result for - q'[select /*+ cardinality( r ]'||l_card||q'[ )*/ - s.name, s.text - from table(:a_objects_to_refresh) r - join ]'||l_sources_view||q'[ s - on s.name = r.object_name - where s.type = :a_object_type - and s.owner = :a_object_owner - and s.name - in (select /*+ cardinality( x ]'||l_card||q'[ )*/ - x.name - from table(:a_objects_to_refresh) t - join ]'||l_sources_view||q'[ x - on x.name = t.object_name - where x.type = :a_object_type - and x.owner = :a_object_owner - and x.text like '%--%\%%' escape '\' - ) - order by name, line]' - using a_objects_to_refresh, a_object_type, a_object_owner, a_objects_to_refresh, a_object_type, a_object_owner; - - return l_result; - end; - ------------------------------------------------------------ --public definitions ------------------------------------------------------------ @@ -339,91 +232,33 @@ create or replace package body ut_annotation_parser as return l_result; end parse_object_annotations; - function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2 := null) return ut_annotated_objects pipelined is - l_info_rows ut_annotation_cached_objects; - l_in_cache ut_annotation_cached_objects; - l_to_parse ut_annotation_cached_objects := ut_annotation_cached_objects(); - l_cursor sys_refcursor; - l_results ut_annotated_objects; - l_result ut_annotated_object; - c_object_fetch_limit constant integer := 10; - c_lines_fetch_limit constant integer := 1000; - l_lines dbms_preprocessor.source_lines_t; - l_names dbms_preprocessor.source_lines_t; - l_name varchar2(250) := ''''; - l_object_lines dbms_preprocessor.source_lines_t; - begin - --get information about cached objects - l_cursor := get_annotated_objects_cursor(a_object_owner, a_object_type, a_object_name); - fetch l_cursor bulk collect into l_info_rows; - close l_cursor; - - --get list of objects in cache - select value(x) bulk collect into l_in_cache from table(l_info_rows) x where x.needs_refresh = 'N'; - - --if not all in cache, get list of objects to refresh - if l_in_cache.count <= l_info_rows.count then - select value(x) bulk collect into l_to_parse from table(l_info_rows) x where x.needs_refresh = 'Y'; - end if; - - --pipe annotations from cache - if l_in_cache.count > 0 then - l_cursor := ut_annotation_cache_manager.get_annotations_for_objects(l_in_cache); - loop - fetch l_cursor bulk collect into l_results limit c_object_fetch_limit; - for i in 1 .. l_results.count loop - pipe row (l_results(i)); - end loop; - exit when l_cursor%notfound; - end loop; - close l_cursor; - end if; - - --if some source needs parsing - if l_to_parse.count > 0 then - --do we need to parse all of sources - if l_in_cache.count = 0 then - l_cursor := get_sources_for_annotations(a_object_owner, a_object_type, a_object_name); - else - -- sources need to be filtered by objects to parse - l_cursor := get_sources_for_annotations(a_object_owner, a_object_type, l_to_parse); - end if; - - --remove cached annotations data for objects that will be refreshed - ut_annotation_cache_manager.cleanup_cache(l_to_parse); - - loop - fetch l_cursor bulk collect into l_names, l_lines limit c_lines_fetch_limit; - - for i in 1 .. l_names.count loop - - if l_names(i) != l_name then - l_result := ut_annotated_object(a_object_owner, l_name, a_object_type, parse_object_annotations(l_object_lines)); - ut_annotation_cache_manager.update_cache(l_result); - if l_result.annotations.count > 0 then - pipe row (l_result); - end if; - end if; - - l_name := l_names(i); - l_object_lines(l_object_lines.count+1) := l_lines(i); + function parse_object_annotations(a_source_lines dbms_preprocessor.source_lines_t) return ut_annotations is + l_processed_lines dbms_preprocessor.source_lines_t; + l_source clob; + l_annotations ut_annotations := ut_annotations(); + ex_package_is_wrapped exception; + pragma exception_init(ex_package_is_wrapped, -24241); + begin + if a_source_lines.count > 0 then + --convert to post-processed source clob + begin + --get post-processed source + l_processed_lines := sys.dbms_preprocessor.get_post_processed_source(a_source_lines); + --convert to clob + for i in 1..l_processed_lines.count loop + ut_utils.append_to_clob(l_source, replace(l_processed_lines(i), chr(13)||chr(10), chr(10))); end loop; - exit when l_cursor%notfound; - - end loop; - - if l_name is not null then - l_result := ut_annotated_object(a_object_owner, l_name, a_object_type, parse_object_annotations(l_object_lines)); - ut_annotation_cache_manager.update_cache(l_result); - if l_result.annotations.count > 0 then - pipe row (l_result); - end if; - end if; - - close l_cursor; + dbms_output.put_line(l_source); + --parse annotations + l_annotations := parse_object_annotations(l_source); + dbms_lob.freetemporary(l_source); + exception + when ex_package_is_wrapped then + null; + end; end if; - + return l_annotations; end; end ut_annotation_parser; diff --git a/source/core/annotations/ut_annotation_parser.pks b/source/core/annotations/ut_annotation_parser.pks index 09f93cc70..ae16d3484 100644 --- a/source/core/annotations/ut_annotation_parser.pks +++ b/source/core/annotations/ut_annotation_parser.pks @@ -17,26 +17,28 @@ create or replace package ut_annotation_parser authid current_user as */ /** - * Reads database source code, parses it and returns annotations + * Parses the source passed as input parameter and returns annotations */ /** - * Parses source code and converts it to annotations + * Runs the source lines through dbms_preprocessor to remove lines that were not compiled (conditional compilation) + * Parses the processed source code and converts it to annotations * - * @param a_source clob containing source code to be parsed + * @param a_source_lines ordered lines of source code to be parsed * @return array containing annotations */ - function parse_object_annotations(a_source clob) return ut_annotations; + function parse_object_annotations(a_source_lines dbms_preprocessor.source_lines_t) return ut_annotations; + /** - * Parses an object or all objects of a specified type for database schema. - * Pesults are returned in a form of a pipelined function. - * @param a_object_owner schema name to be parsed - * @param a_object_type type of object to be parsed - * @param a_object_name name of object to be parsed - optional - * @return array containing annotated objects along with annotations for each object (nested) + * + * @private + * Parses source code and converts it to annotations + * + * @param a_source_lines ordered lines of source code to be parsed + * @return array containing annotations */ - function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2 := null) return ut_annotated_objects pipelined; + function parse_object_annotations(a_source clob) return ut_annotations; end ut_annotation_parser; / diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index 5c4487875..17af64d58 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -280,7 +280,7 @@ create or replace package body ut_suite_manager is execute immediate q'[select value(x) from table( - ]'||ut_utils.ut_owner||q'[.ut_annotation_parser.get_annotated_objects(:a_owner_name, 'PACKAGE') + ]'||ut_utils.ut_owner||q'[.ut_annotation_manager.get_annotated_objects(:a_owner_name, 'PACKAGE') )x ]' bulk collect into l_annotated_objects using a_owner_name; for i in 1 .. l_annotated_objects.count loop diff --git a/source/install.sql b/source/install.sql index 960e4d991..3e90daea6 100644 --- a/source/install.sql +++ b/source/install.sql @@ -88,6 +88,8 @@ alter session set plsql_warnings = 'ENABLE:ALL', 'DISABLE:(5004,5018,6000,6001,6 @@install_component.sql 'core/annotations/ut_annotation_cache_manager.pkb' @@install_component.sql 'core/annotations/ut_annotation_parser.pks' @@install_component.sql 'core/annotations/ut_annotation_parser.pkb' +@@install_component.sql 'core/annotations/ut_annotation_manager.pks' +@@install_component.sql 'core/annotations/ut_annotation_manager.pkb' --suite manager @@install_component.sql 'core/ut_suite_manager.pks' diff --git a/source/uninstall.sql b/source/uninstall.sql index a8f567150..969537254 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -172,6 +172,8 @@ drop type ut_data_value force; drop table ut_cursor_data; +drop package ut_annotation_manager; + drop package ut_annotation_parser; drop package ut_annotation_cache_manager; diff --git a/test/ut_annotation_parser/test_annotation_parser.pkb b/test/ut_annotation_parser/test_annotation_parser.pkb index 8b6dc5028..903e4f542 100644 --- a/test/ut_annotation_parser/test_annotation_parser.pkb +++ b/test/ut_annotation_parser/test_annotation_parser.pkb @@ -1,6 +1,6 @@ create or replace package body test_annotation_parser is - procedure test1 is + procedure test_proc_comments is l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; @@ -32,7 +32,7 @@ create or replace package body test_annotation_parser is ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; - procedure test2 is + procedure ignore_floating_annotations is l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; @@ -45,7 +45,14 @@ create or replace package body test_annotation_parser is -- %ann1(Name of suite) -- %ann2(all.globaltests) + --%test procedure foo; + + -- %ann3(Name of suite) + -- %ann4(all.globaltests) + + --%test + procedure bar; END;'; --Act @@ -55,14 +62,16 @@ create or replace package body test_annotation_parser is l_expected := ut3.ut_annotations( ut3.ut_annotation( 1, 'suite', null, null ), ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), - ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ) + ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ), + ut3.ut_annotation( 6, 'test', null, 'foo'), + ut3.ut_annotation( 9, 'test', null, 'bar') ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; - procedure test3 is + procedure parse_complex_with_functions is l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; @@ -90,6 +99,7 @@ create or replace package body test_annotation_parser is --%beforeeach(key=testval) PROCEDURE foo3(a_value number default null); + --%all function foo4(a_val number default null , a_par varchar2 default := ''asdf''); END;'; @@ -104,43 +114,15 @@ create or replace package body test_annotation_parser is ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ), ut3.ut_annotation( 4, 'test', null, 'foo' ), ut3.ut_annotation( 5, 'beforeeach', null,'foo2' ), - ut3.ut_annotation( 6, 'beforeeach', 'key=testval','foo3' ) + ut3.ut_annotation( 6, 'beforeeach', 'key=testval','foo3' ), + ut3.ut_annotation( 7, 'all', null,'foo4' ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; - procedure test4 is - l_source clob; - l_actual ut3.ut_annotations; - l_expected ut3.ut_annotations; - - begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite) - -- %suitepath(all.globaltests) - - --%test - procedure foo; - END;'; - - --Act - l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); - - --Assert - l_expected := ut3.ut_annotations( - ut3.ut_annotation( 1, 'suite', null, null ), - ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), - ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ), - ut3.ut_annotation( 4, 'test', null, 'foo' ) - ); - - ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); - end; - - procedure test5 is + procedure no_procedure_annotation is l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; @@ -168,7 +150,7 @@ create or replace package body test_annotation_parser is end; - procedure test6 is + procedure parse_accessible_by is l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; @@ -196,7 +178,7 @@ create or replace package body test_annotation_parser is end; - procedure test7 is + procedure complex_package_declaration is l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; @@ -227,7 +209,7 @@ create or replace package body test_annotation_parser is end; - procedure test8 is + procedure complex_text is l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; @@ -236,7 +218,7 @@ create or replace package body test_annotation_parser is l_source := 'PACKAGE test_tt AS -- %suite --%displayname(name = Name of suite) - -- %suitepath(key=all.globaltests,key2=foo) + -- %suitepath(key=all.globaltests,key2=foo,"--%some text") procedure foo; END;'; @@ -248,14 +230,14 @@ create or replace package body test_annotation_parser is l_expected := ut3.ut_annotations( ut3.ut_annotation( 1, 'suite', null, null ), ut3.ut_annotation( 2, 'displayname', 'name = Name of suite', null ), - ut3.ut_annotation( 3, 'suitepath', 'key=all.globaltests,key2=foo', null ) + ut3.ut_annotation( 3, 'suitepath', 'key=all.globaltests,key2=foo,"--%some text"', null ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; - procedure test9 is + procedure ignore_annotations_in_comments is l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; @@ -265,6 +247,7 @@ create or replace package body test_annotation_parser is /* Some comment -- inlined + -- %ignored */ -- %suite --%displayname(Name of suite) @@ -288,37 +271,38 @@ create or replace package body test_annotation_parser is end; procedure ignore_wrapped_package is - l_cursor sys_refcursor; + l_source dbms_preprocessor.source_lines_t; + l_actual ut3.ut_annotations; begin + --Arrange + l_source(1) := 'create or replace PACKAGE tst_wrapped_pck wrapped +a000000 +369 +abcd +abcd +abcd +abcd +abcd +abcd +abcd +abcd +abcd +abcd +abcd +abcd +abcd +abcd +abcd +9 +34 6d +bg9Jaf2KguofrwaqloE8yvbggKcwg5m49TOf9b9cFj7R9JaW8lYWWi70llr/K6V0iwlp5+eb +v58yvbLAXLi9gYHwoIvAgccti+Cmpg0DKLY= +-- %some_annotation_like_text +'; --Act - open l_cursor for - select * - from table( - ut3.ut_annotation_parser.get_annotated_objects(user, 'PACKAGE','TST_WRAPPED_PCK') - ); + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); --Assert - ut.expect(l_cursor).to_be_empty(); - end; - - procedure cre_wrapped_pck is - pragma autonomous_transaction; - begin - dbms_ddl.create_wrapped(q'[ -CREATE OR REPLACE PACKAGE tst_wrapped_pck IS - PROCEDURE dummy; -END; -]'); - end; - - procedure drop_wrapped_pck is - ex_pck_doesnt_exist exception; - pragma autonomous_transaction; - pragma exception_init(ex_pck_doesnt_exist, -04043); - begin - execute immediate 'drop package tst_wrapped_pck'; - exception - when ex_pck_doesnt_exist then - null; + ut.expect(anydata.convertCollection(l_actual)).to_be_empty(); end; procedure brackets_in_desc is @@ -342,7 +326,7 @@ END;'; ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); end; - procedure test_space_Before_Annot_Params is + procedure test_space_before_annot_params is l_source clob; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; @@ -402,13 +386,13 @@ END;'; l_actual ut3.ut_annotations; l_expected ut3.ut_annotations; begin - l_source := 'PACKAGE test_tt AS + l_source := 'PACKAGE very_long_procedure_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_did_it AS -- %suite -- %displayname(Name of suite) -- %suitepath(all.globaltests) --%test - procedure very_long_procedure_name_valid_for_oracle_12_so_utplsql_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_ditit; + procedure very_long_procedure_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_dit_it; END;'; --Act @@ -419,7 +403,7 @@ END;'; ut3.ut_annotation( 1, 'suite', null, null ), ut3.ut_annotation( 2, 'displayname', 'Name of suite', null ), ut3.ut_annotation( 3, 'suitepath', 'all.globaltests', null ), - ut3.ut_annotation( 4, 'test', null, 'very_long_procedure_name_valid_for_oracle_12_so_utplsql_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_ditit' ) + ut3.ut_annotation( 4, 'test', null, 'very_long_procedure_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_dit_it' ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); diff --git a/test/ut_annotation_parser/test_annotation_parser.pks b/test/ut_annotation_parser/test_annotation_parser.pks index 95182fbc2..b5ef9b5ed 100644 --- a/test/ut_annotation_parser/test_annotation_parser.pks +++ b/test/ut_annotation_parser/test_annotation_parser.pks @@ -1,45 +1,38 @@ create or replace package test_annotation_parser is - --%suite(annotations) + --%suite(ut_annotation_parser) --%suitepath(utplsql.core) - --%test(Parse procedure level annotations with annotations mixed with comments) - procedure test1; - --%test(Parse package level annotations with annotations "in the air") - procedure test2; - --%test(Parse complex package) - procedure test3; - --%test(Parse package level annotations) - procedure test4; - --%test(Parse package level annotations) - procedure test5; - --%test(Parse package level annotations Accessible by) - procedure test6; - --%test(Parse package level annotations with multiline declaration) - procedure test7; - --%test(Parse package level annotations) - procedure test8; - --%test(Parse package level annotations with multiline comment) - procedure test9; - - --%test(Ignore Wrapped Package And Does Not Raise Exception) - --%beforetest(cre_wrapped_pck) - --%aftertest(drop_wrapped_pck) + --%test(Ignores procedure level annotations if mixed with comments) + procedure test_proc_comments; + --%test(Ignores floating annotations between procedures and package) + procedure ignore_floating_annotations; + --%test(Parses complex annotations on procedures and functions) + procedure parse_complex_with_functions; + --%test(Parses package annotations without any procedure annotations) + procedure no_procedure_annotation; + --%test(Parses package level annotations with Accessible by) + procedure parse_accessible_by; + --%test(Parses package level annotations with multiline declaration) + procedure complex_package_declaration; + --%test(Parses complex text in annotation) + procedure complex_text; + --%test(Ignores content of multi-line comments) + procedure ignore_annotations_in_comments; + + --%test(Ignores wrapped package and does not raise exception) procedure ignore_wrapped_package; - procedure cre_wrapped_pck; - procedure drop_wrapped_pck; - - --%test(Parse package level annotations with annotation params containing brackets) + --%test(Parses package level annotations with annotation params containing brackets) procedure brackets_in_desc; - --%test(Test space before annotation params) - procedure test_space_Before_Annot_Params; + --%test(Parses annotation text even with spaces before brackets) + procedure test_space_before_annot_params; - -- %test(Test annotations with windows newline) + -- %test(Parses source-code with Windows-style newline) procedure test_windows_newline; - -- %test(Test annotation function with very long name) + -- %test(Parses annotations with very long object names) procedure test_annot_very_long_name; end test_annotation_parser; From ca31e98091c5e82d540d7b0650bb7be38587df4b Mon Sep 17 00:00:00 2001 From: Jacek Date: Sat, 14 Oct 2017 22:59:02 +0100 Subject: [PATCH 11/19] Added missing annotation manager. Removed unused code from ut_meta_data. Improved code comments. --- .../annotations/ut_annotation_manager.pkb | 199 ++++++++++++++++++ .../annotations/ut_annotation_manager.pks | 34 +++ source/core/ut_metadata.pkb | 22 +- source/core/ut_metadata.pks | 92 ++++---- 4 files changed, 271 insertions(+), 76 deletions(-) create mode 100644 source/core/annotations/ut_annotation_manager.pkb create mode 100644 source/core/annotations/ut_annotation_manager.pks diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb new file mode 100644 index 000000000..51801cd6b --- /dev/null +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -0,0 +1,199 @@ +create or replace package body ut_annotation_manager as + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ + + ------------------------------ + --private definitions + + function get_annotated_objects_cursor(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2) return sys_refcursor is + l_result sys_refcursor; + l_ut_owner varchar2(250) := ut_utils.ut_owner; + l_objects_view varchar2(200) := ut_metadata.get_dba_view('dba_objects'); + l_cursor_text long; + begin + l_cursor_text := + q'[select ]'||l_ut_owner||q'[.ut_annotation_cached_object( + object_owner => o.owner, + object_name => o.object_name, + object_type => o.object_type, + needs_refresh => case when o.last_ddl_time < i.parse_time then 'N' else 'Y' end, + cache_id => i.cache_id + ) + from ]'||l_objects_view||q'[ o + left join ut3.ut_annotation_cache_info i + on o.owner = i.object_owner and o.object_name = i.object_name and o.object_type = i.object_type + where o.owner = :a_object_owner + and o.object_type = :a_object_type + and o.status = 'VALID' + and :a_object_name ]'|| case when a_object_name is not null then '= o.object_name' else 'is null' end; + open l_result for l_cursor_text using a_object_owner, a_object_type, a_object_name; + return l_result; + end; + + function get_sources_for_annotations(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2) return sys_refcursor is + l_result sys_refcursor; + l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source'); + begin + open l_result for + q'[select s.name, s.text + from ]'||l_sources_view||q'[ s + where s.type = :a_object_type + and s.owner = :a_object_owner + and s.name + in (select x.name + from ]'||l_sources_view||q'[ x + where x.type = :a_object_type + and x.owner = :a_object_owner + and x.text like '%--%\%%' escape '\' + and :a_object_name ]'|| case when a_object_name is not null then '= x.name' else 'is null' end || q'[ + ) + order by name, line]' + using a_object_type, a_object_owner, a_object_type, a_object_owner, a_object_name; + + return l_result; + end; + + function get_sources_for_annotations(a_object_owner varchar2, a_object_type varchar2, a_objects_to_refresh ut_annotation_cached_objects) return sys_refcursor is + l_result sys_refcursor; + l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source'); + l_card natural; + begin + l_card := ut_utils.scale_cardinality(cardinality(a_objects_to_refresh)); + open l_result for + q'[select /*+ cardinality( r ]'||l_card||q'[ )*/ + s.name, s.text + from table(:a_objects_to_refresh) r + join ]'||l_sources_view||q'[ s + on s.name = r.object_name + where s.type = :a_object_type + and s.owner = :a_object_owner + and s.name + in (select /*+ cardinality( x ]'||l_card||q'[ )*/ + x.name + from table(:a_objects_to_refresh) t + join ]'||l_sources_view||q'[ x + on x.name = t.object_name + where x.type = :a_object_type + and x.owner = :a_object_owner + and x.text like '%--%\%%' escape '\' + ) + order by name, line]' + using a_objects_to_refresh, a_object_type, a_object_owner, a_objects_to_refresh, a_object_type, a_object_owner; + + return l_result; + end; + + ------------------------------------------------------------ + --public definitions + ------------------------------------------------------------ + function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2 := null) return ut_annotated_objects pipelined is + l_info_rows ut_annotation_cached_objects; + l_in_cache ut_annotation_cached_objects; + l_to_parse ut_annotation_cached_objects := ut_annotation_cached_objects(); + l_cursor sys_refcursor; + l_results ut_annotated_objects; + l_result ut_annotated_object; + c_object_fetch_limit constant integer := 10; + c_lines_fetch_limit constant integer := 1000; + l_lines dbms_preprocessor.source_lines_t; + l_names dbms_preprocessor.source_lines_t; + l_name varchar2(250) := ''''; + l_object_lines dbms_preprocessor.source_lines_t; + begin + --get information about cached objects + l_cursor := get_annotated_objects_cursor(a_object_owner, a_object_type, a_object_name); + fetch l_cursor bulk collect into l_info_rows; + close l_cursor; + + --get list of objects in cache + select value(x) bulk collect into l_in_cache from table(l_info_rows) x where x.needs_refresh = 'N'; + + --if not all in cache, get list of objects to refresh + if l_in_cache.count <= l_info_rows.count then + select value(x) bulk collect into l_to_parse from table(l_info_rows) x where x.needs_refresh = 'Y'; + end if; + + --pipe annotations from cache + if l_in_cache.count > 0 then + l_cursor := ut_annotation_cache_manager.get_annotations_for_objects(l_in_cache); + loop + fetch l_cursor bulk collect into l_results limit c_object_fetch_limit; + for i in 1 .. l_results.count loop + pipe row (l_results(i)); + end loop; + exit when l_cursor%notfound; + end loop; + close l_cursor; + end if; + + --if some source needs parsing + if l_to_parse.count > 0 then + --do we need to parse all of sources + if l_in_cache.count = 0 then + l_cursor := get_sources_for_annotations(a_object_owner, a_object_type, a_object_name); + else + -- sources need to be filtered by objects to parse + l_cursor := get_sources_for_annotations(a_object_owner, a_object_type, l_to_parse); + end if; + + --remove cached annotations data for objects that will be refreshed + ut_annotation_cache_manager.cleanup_cache(l_to_parse); + + loop + fetch l_cursor bulk collect into l_names, l_lines limit c_lines_fetch_limit; + + for i in 1 .. l_names.count loop + + if l_names(i) != l_name then + l_result := ut_annotated_object( + a_object_owner, l_name, a_object_type, + ut_annotation_parser.parse_object_annotations(l_object_lines) + ); + ut_annotation_cache_manager.update_cache(l_result); + if l_result.annotations.count > 0 then + pipe row (l_result); + end if; + l_object_lines.delete; + end if; + + l_name := l_names(i); + l_object_lines(l_object_lines.count+1) := l_lines(i); + + end loop; + exit when l_cursor%notfound; + + end loop; + + if l_name is not null then + l_result := ut_annotated_object( + a_object_owner, l_name, a_object_type, + ut_annotation_parser.parse_object_annotations(l_object_lines) + ); + ut_annotation_cache_manager.update_cache(l_result); + if l_result.annotations.count > 0 then + pipe row (l_result); + end if; + l_object_lines.delete; + end if; + + close l_cursor; + end if; + + end; + +end ut_annotation_manager; +/ diff --git a/source/core/annotations/ut_annotation_manager.pks b/source/core/annotations/ut_annotation_manager.pks new file mode 100644 index 000000000..b043da641 --- /dev/null +++ b/source/core/annotations/ut_annotation_manager.pks @@ -0,0 +1,34 @@ +create or replace package ut_annotation_manager authid current_user as + /* + utPLSQL - Version X.X.X.X + Copyright 2016 - 2017 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. + */ + + /** + * Builds annotations out of database source code by reading it from cache + */ + + /** + * Parses an object or all objects of a specified type for database schema. + * Results are returned in as a pipelined function. + * @param a_object_owner schema name to be parsed + * @param a_object_type type of object to be parsed + * @param a_object_name name of object to be parsed - optional + * @return array containing annotated objects along with annotations for each object (nested) + */ + function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2 := null) return ut_annotated_objects pipelined; + +end ut_annotation_manager; +/ diff --git a/source/core/ut_metadata.pkb b/source/core/ut_metadata.pkb index 15566ebd1..d70781aee 100644 --- a/source/core/ut_metadata.pkb +++ b/source/core/ut_metadata.pkb @@ -115,24 +115,6 @@ create or replace package body ut_metadata as return false; end; - function get_package_spec_source(a_owner varchar2, a_object_name varchar2) return clob is - l_lines sys.dbms_preprocessor.source_lines_t; - l_cursor sys_refcursor; - l_source clob; - l_view_name varchar2(128) := get_dba_view('dba_source'); - begin - open l_cursor for 'select text from '||l_view_name||q'[ s - where s.owner = :a_owner and s.name = :a_object_name and s.type = 'PACKAGE' - order by s.line]' using upper(a_owner), upper(a_object_name); - fetch l_cursor bulk collect into l_lines; - -- we fetch the source explicitly as dbms_preprocessor is very sow on 12.1 and 12.2 when grabbing the sources. - l_lines := sys.dbms_preprocessor.get_post_processed_source(l_lines); - for i in 1..l_lines.count loop - ut_utils.append_to_clob(l_source, replace(l_lines(i), chr(13)||chr(10), chr(10))); - end loop; - return l_source; - end; - function get_source_definition_line(a_owner varchar2, a_object_name varchar2, a_line_no integer) return varchar2 is l_cursor sys_refcursor; l_view_name varchar2(128) := get_dba_view('dba_source'); @@ -165,9 +147,9 @@ create or replace package body ut_metadata as g_cached_object := null; end; - function get_dba_view(a_view_name varchar2) return varchar2 is + function get_dba_view(a_dba_view_name varchar2) return varchar2 is l_invalid_object_name exception; - l_result varchar2(128) := lower(a_view_name); + l_result varchar2(128) := lower(a_dba_view_name); pragma exception_init(l_invalid_object_name,-44002); begin l_result := dbms_assert.sql_object_name(l_result); diff --git a/source/core/ut_metadata.pks b/source/core/ut_metadata.pks index fcd4a4a20..33c5636d3 100644 --- a/source/core/ut_metadata.pks +++ b/source/core/ut_metadata.pks @@ -16,78 +16,58 @@ create or replace package ut_metadata authid current_user as limitations under the License. */ - /* - package: ut_metadata - - Common place for all code that reads from the system tables. - - */ - - /* - function: form_name - - forms correct object/subprogram name to call as owner.object[.subprogram] - - */ + /** + * Common package for all code that reads from the system tables. + */ + + /** + * Forms correct object/subprogram name to call as owner.object[.subprogram] + * + */ function form_name(a_owner_name varchar2, a_object varchar2, a_subprogram varchar2 default null) return varchar2; - /* - function: package_valid - - check if package exists and is VALID. - - */ + /** + * Check if package exists and is in a VALID state + * + */ function package_valid(a_owner_name varchar2, a_package_name in varchar2) return boolean; - /* - function: procedure_exists - - check if package exists and is VALID and contains the given procedure. - - */ + /** + * Check if package exists and is VALID and contains the given procedure. + * + */ function procedure_exists(a_owner_name varchar2, a_package_name in varchar2, a_procedure_name in varchar2) return boolean; - /* - procedure: do_resolve - - resolves [owner.]object using dbms_utility.name_resolve and returnes resolved parts - - */ + /** + * Resolves [owner.]object using dbms_utility.name_resolve and returns resolved parts + * + */ procedure do_resolve(a_owner in out nocopy varchar2, a_object in out nocopy varchar2); - /* - procedure: do_resolve - - resolves [owner.]object[.procedure] using dbms_utility.name_resolve and returnes resolved parts - - */ + /** + * Resolves [owner.]object[.procedure] using dbms_utility.name_resolve and returns resolved parts + * + */ procedure do_resolve(a_owner in out nocopy varchar2, a_object in out nocopy varchar2, a_procedure_name in out nocopy varchar2); - /* - function: get_package_spec_source - - return the text of the package specification for a given package - */ - function get_package_spec_source(a_owner varchar2, a_object_name varchar2) return clob; - - - /* - function: get_source_definition_line - - return the text of the source line for a given object, excludes package spec and type spec - */ + /** + * Return the text of the source line for a given object (body). It excludes package spec and type spec + */ function get_source_definition_line(a_owner varchar2, a_object_name varchar2, a_line_no integer) return varchar2; + /** + * Invalidates package-level cache for source. + * Caching is used to improve performance of function get_source_definition_line + */ procedure reset_source_definition_cache; - /* - function: get_dba_view - - return the dba_xxx view name if it is accessible or all_xxx view otherwise - */ - function get_dba_view(a_view_name varchar2) return varchar2; + /** + * Returns dba_... view name if it is accessible, otherwise it returns all_xxx view + * @param a_dba_view_name the name of dba view requested + */ + function get_dba_view(a_dba_view_name varchar2) return varchar2; end ut_metadata; / From 289f0061b4ec1916b839dff7e1c5f0071d3aeb46 Mon Sep 17 00:00:00 2001 From: Jacek Date: Sat, 14 Oct 2017 23:34:29 +0100 Subject: [PATCH 12/19] Removed dbms_output from code. --- source/core/annotations/ut_annotation_parser.pkb | 1 - 1 file changed, 1 deletion(-) diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index 34402e71b..f35e6a0a7 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -249,7 +249,6 @@ create or replace package body ut_annotation_parser as for i in 1..l_processed_lines.count loop ut_utils.append_to_clob(l_source, replace(l_processed_lines(i), chr(13)||chr(10), chr(10))); end loop; - dbms_output.put_line(l_source); --parse annotations l_annotations := parse_object_annotations(l_source); dbms_lob.freetemporary(l_source); From 239abde974452a15c258ef64ad0cee4525485dfc Mon Sep 17 00:00:00 2001 From: Samuel Nitsche Date: Mon, 16 Oct 2017 17:14:01 +0200 Subject: [PATCH 13/19] Add grant ut_annotation_manager to public --- source/create_synonyms_and_grants_for_public.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/source/create_synonyms_and_grants_for_public.sql b/source/create_synonyms_and_grants_for_public.sql index 93c3c0e69..f3d77c942 100644 --- a/source/create_synonyms_and_grants_for_public.sql +++ b/source/create_synonyms_and_grants_for_public.sql @@ -69,6 +69,7 @@ grant select, insert, delete on &&ut3_owner..ut_cursor_data to public; grant execute on &&ut3_owner..ut_sonar_test_reporter to public; grant execute on &&ut3_owner..ut_annotations to public; grant execute on &&ut3_owner..ut_annotation to public; +grant execute on &&ut3_owner..ut_annotation_manager to public; grant execute on &&ut3_owner..ut_annotated_object to public; grant execute on &&ut3_owner..ut_annotated_objects to public; grant select on &&ut3_owner..ut_annotation_cache_info to public; From 6acd2ad23293891ebf1456f4549cc04a8bd9b7bd Mon Sep 17 00:00:00 2001 From: Samuel Nitsche Date: Mon, 16 Oct 2017 17:15:28 +0200 Subject: [PATCH 14/19] Add grant ut_annotation_manager to ut3_user --- source/create_synonyms_and_grants_for_user.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/source/create_synonyms_and_grants_for_user.sql b/source/create_synonyms_and_grants_for_user.sql index 9938652cd..4785aae32 100644 --- a/source/create_synonyms_and_grants_for_user.sql +++ b/source/create_synonyms_and_grants_for_user.sql @@ -89,6 +89,7 @@ grant select, insert, delete on &&ut3_owner..ut_cursor_data to &ut3_user; grant execute on &&ut3_owner..ut_sonar_test_reporter to &ut3_user; grant execute on &&ut3_owner..ut_annotations to &ut3_user; grant execute on &&ut3_owner..ut_annotation to &ut3_user; +grant execute on &&ut3_owner..ut_annotation_manager to &ut3_user; grant execute on &&ut3_owner..ut_annotated_object to &ut3_user; grant execute on &&ut3_owner..ut_annotated_objects to &ut3_user; grant select on &&ut3_owner..ut_annotation_cache_info to &ut3_user; From b83e142196b7e2e81b917c7f3e16112364c7b65c Mon Sep 17 00:00:00 2001 From: Jacek Date: Wed, 18 Oct 2017 11:01:03 +0100 Subject: [PATCH 15/19] Fixed hardcoded schema-name --- source/core/annotations/ut_annotation_manager.pkb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb index 51801cd6b..aef0989b3 100644 --- a/source/core/annotations/ut_annotation_manager.pkb +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -34,7 +34,7 @@ create or replace package body ut_annotation_manager as cache_id => i.cache_id ) from ]'||l_objects_view||q'[ o - left join ut3.ut_annotation_cache_info i + left join ]'||l_ut_owner||q'[.ut_annotation_cache_info i on o.owner = i.object_owner and o.object_name = i.object_name and o.object_type = i.object_type where o.owner = :a_object_owner and o.object_type = :a_object_type From aafca847eb6694db12c9377be67b0b7547bf8161 Mon Sep 17 00:00:00 2001 From: Jacek Date: Sun, 22 Oct 2017 23:54:16 +0100 Subject: [PATCH 16/19] Refactored annotation cache. * Added ability to build cache in `ut_annotation_manager.rebuild_annotation_cache` * Added ability to purge cache in `ut_annotation_cache_manager.purge_cache` --- .../ut_annotation_cache_manager.pkb | 31 ++- .../ut_annotation_cache_manager.pks | 31 ++- .../annotations/ut_annotation_manager.pkb | 187 +++++++------ .../annotations/ut_annotation_manager.pks | 24 +- ...t.tps => ut_annotation_obj_cache_info.tps} | 5 +- ....tps => ut_annotation_objs_cache_info.tps} | 4 +- .../create_synonyms_and_grants_for_public.sql | 4 +- .../create_synonyms_and_grants_for_user.sql | 4 +- source/install.sql | 4 +- source/uninstall.sql | 4 +- .../test_annotation_cache_manager.pkb | 74 +++++ .../test_annotation_cache_manager.pks | 17 ++ .../test_annotation_manager.pkb | 253 ++++++++++++++++++ .../test_annotation_manager.pks | 46 ++++ test/install_tests.sql | 4 + .../test_expectations_cursor.pkb | 18 +- 16 files changed, 590 insertions(+), 120 deletions(-) rename source/core/annotations/{ut_annotation_cached_object.tps => ut_annotation_obj_cache_info.tps} (86%) rename source/core/annotations/{ut_annotation_cached_objects.tps => ut_annotation_objs_cache_info.tps} (88%) create mode 100644 test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pkb create mode 100644 test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pks create mode 100644 test/annotations/ut_annotation_manager/test_annotation_manager.pkb create mode 100644 test/annotations/ut_annotation_manager/test_annotation_manager.pks diff --git a/source/core/annotations/ut_annotation_cache_manager.pkb b/source/core/annotations/ut_annotation_cache_manager.pkb index a9c5e9916..3adff4aab 100644 --- a/source/core/annotations/ut_annotation_cache_manager.pkb +++ b/source/core/annotations/ut_annotation_cache_manager.pkb @@ -53,7 +53,7 @@ create or replace package body ut_annotation_cache_manager as end; - procedure cleanup_cache(a_objects ut_annotation_cached_objects) is + procedure cleanup_cache(a_objects ut_annotation_objs_cache_info) is pragma autonomous_transaction; begin @@ -79,7 +79,7 @@ create or replace package body ut_annotation_cache_manager as commit; end; - function get_annotations_for_objects(a_cached_objects ut_annotation_cached_objects) return sys_refcursor is + function get_annotations_for_objects(a_cached_objects ut_annotation_objs_cache_info) return sys_refcursor is l_results sys_refcursor; begin open l_results for @@ -94,9 +94,34 @@ create or replace package body ut_annotation_cache_manager as ) ) from table(a_cached_objects) o - join ut_annotation_cache c on o.cache_id = c.cache_id + join ut_annotation_cache_info i + on o.object_owner = i.object_owner and o.object_name = i.object_name and o.object_type = i.object_type + join ut_annotation_cache c on i.cache_id = c.cache_id group by o.object_owner, o.object_name, o.object_type; return l_results; end; + + procedure purge_cache(a_object_owner varchar2, a_object_type varchar2) is + pragma autonomous_transaction; + begin + execute immediate ' + delete from ut_annotation_cache c + where c.cache_id + in (select i.cache_id + from ut_annotation_cache_info i + where 1 = 1 + and '||case when a_object_owner is null then ':a_object_owner is null' else 'object_owner = :a_object_owner' end || ' + and '||case when a_object_type is null then ':a_object_type is null' else 'object_type = :a_object_type' end || ' + )' + using a_object_owner, a_object_type; + execute immediate ' + delete from ut_annotation_cache_info i + where 1 = 1 + and '||case when a_object_owner is null then ':a_object_owner is null' else 'object_owner = :a_object_owner' end || ' + and '||case when a_object_type is null then ':a_object_type is null' else 'object_type = :a_object_type' end + using a_object_owner, a_object_type; + commit; + end; + end ut_annotation_cache_manager; / diff --git a/source/core/annotations/ut_annotation_cache_manager.pks b/source/core/annotations/ut_annotation_cache_manager.pks index ce7d1bd7f..eb332572d 100644 --- a/source/core/annotations/ut_annotation_cache_manager.pks +++ b/source/core/annotations/ut_annotation_cache_manager.pks @@ -16,11 +16,38 @@ create or replace package ut_annotation_cache_manager authid definer as limitations under the License. */ + /** + * Populates cache with information about object and it's annotations + * Cache information for individual object is modified by this code + * We do not pass a collection here to avoid excessive memory usage + * when dealing with large number of objects + * + * @param a_object a `ut_annotated_object` containing object name, type, owner and `ut_annotations` + */ procedure update_cache(a_object ut_annotated_object); - function get_annotations_for_objects(a_cached_objects ut_annotation_cached_objects) return sys_refcursor; + /** + * Returns a ref_cursor containing `ut_annotated_object` as result + * Range of data returned is limited by the input collection o cache object info + * + * @param a_cached_objects a `ut_annotation_objs_cache_info` list with information about objects to get from cache + */ + function get_annotations_for_objects(a_cached_objects ut_annotation_objs_cache_info) return sys_refcursor; - procedure cleanup_cache(a_objects ut_annotation_cached_objects); + /** + * Removes cached information about annotations for objects on the list and updates parse_time in cache info table. + * + * @param a_objects a `ut_annotation_objs_cache_info` list with information about objects to remove from cache + */ + procedure cleanup_cache(a_objects ut_annotation_objs_cache_info); + + /** + * Removes cached information about annotations for objects of specified type and specified owner + * + * @param a_object_owner owner of objects to purge annotations for + * @param a_object_type type of objects to purge annotations for + */ + procedure purge_cache(a_object_owner varchar2, a_object_type varchar2); end; / diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb index aef0989b3..11e16f243 100644 --- a/source/core/annotations/ut_annotation_manager.pkb +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -19,32 +19,30 @@ create or replace package body ut_annotation_manager as ------------------------------ --private definitions - function get_annotated_objects_cursor(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2) return sys_refcursor is + function get_annotation_objs_info_cur(a_object_owner varchar2, a_object_type varchar2) return sys_refcursor is l_result sys_refcursor; l_ut_owner varchar2(250) := ut_utils.ut_owner; l_objects_view varchar2(200) := ut_metadata.get_dba_view('dba_objects'); l_cursor_text long; begin l_cursor_text := - q'[select ]'||l_ut_owner||q'[.ut_annotation_cached_object( + q'[select ]'||l_ut_owner||q'[.ut_annotation_obj_cache_info( object_owner => o.owner, object_name => o.object_name, object_type => o.object_type, - needs_refresh => case when o.last_ddl_time < i.parse_time then 'N' else 'Y' end, - cache_id => i.cache_id + needs_refresh => case when o.last_ddl_time < i.parse_time then 'N' else 'Y' end ) from ]'||l_objects_view||q'[ o left join ]'||l_ut_owner||q'[.ut_annotation_cache_info i on o.owner = i.object_owner and o.object_name = i.object_name and o.object_type = i.object_type where o.owner = :a_object_owner and o.object_type = :a_object_type - and o.status = 'VALID' - and :a_object_name ]'|| case when a_object_name is not null then '= o.object_name' else 'is null' end; - open l_result for l_cursor_text using a_object_owner, a_object_type, a_object_name; + and o.status = 'VALID' ]'; + open l_result for l_cursor_text using a_object_owner, a_object_type; return l_result; end; - function get_sources_for_annotations(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2) return sys_refcursor is + function get_sources_to_annotate(a_object_owner varchar2, a_object_type varchar2) return sys_refcursor is l_result sys_refcursor; l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source'); begin @@ -59,15 +57,14 @@ create or replace package body ut_annotation_manager as where x.type = :a_object_type and x.owner = :a_object_owner and x.text like '%--%\%%' escape '\' - and :a_object_name ]'|| case when a_object_name is not null then '= x.name' else 'is null' end || q'[ ) order by name, line]' - using a_object_type, a_object_owner, a_object_type, a_object_owner, a_object_name; + using a_object_type, a_object_owner, a_object_type, a_object_owner; return l_result; end; - function get_sources_for_annotations(a_object_owner varchar2, a_object_type varchar2, a_objects_to_refresh ut_annotation_cached_objects) return sys_refcursor is + function get_sources_to_annotate(a_object_owner varchar2, a_object_type varchar2, a_objects_to_refresh ut_annotation_objs_cache_info) return sys_refcursor is l_result sys_refcursor; l_sources_view varchar2(200) := ut_metadata.get_dba_view('dba_source'); l_card natural; @@ -97,101 +94,117 @@ create or replace package body ut_annotation_manager as return l_result; end; - ------------------------------------------------------------ - --public definitions - ------------------------------------------------------------ - function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2 := null) return ut_annotated_objects pipelined is - l_info_rows ut_annotation_cached_objects; - l_in_cache ut_annotation_cached_objects; - l_to_parse ut_annotation_cached_objects := ut_annotation_cached_objects(); - l_cursor sys_refcursor; - l_results ut_annotated_objects; - l_result ut_annotated_object; - c_object_fetch_limit constant integer := 10; + procedure build_annot_cache_for_sources( + a_object_owner varchar2, a_object_type varchar2, a_sources_cursor sys_refcursor, + a_schema_objects ut_annotation_objs_cache_info + ) is + l_annotations ut_annotations; c_lines_fetch_limit constant integer := 1000; l_lines dbms_preprocessor.source_lines_t; l_names dbms_preprocessor.source_lines_t; - l_name varchar2(250) := ''''; + l_name varchar2(250); l_object_lines dbms_preprocessor.source_lines_t; + pragma autonomous_transaction; begin - --get information about cached objects - l_cursor := get_annotated_objects_cursor(a_object_owner, a_object_type, a_object_name); - fetch l_cursor bulk collect into l_info_rows; - close l_cursor; - - --get list of objects in cache - select value(x) bulk collect into l_in_cache from table(l_info_rows) x where x.needs_refresh = 'N'; - - --if not all in cache, get list of objects to refresh - if l_in_cache.count <= l_info_rows.count then - select value(x) bulk collect into l_to_parse from table(l_info_rows) x where x.needs_refresh = 'Y'; - end if; + ut_annotation_cache_manager.cleanup_cache(a_schema_objects); + loop + fetch a_sources_cursor bulk collect into l_names, l_lines limit c_lines_fetch_limit; + for i in 1 .. l_names.count loop + if l_names(i) != l_name then + l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines); + ut_annotation_cache_manager.update_cache( + ut_annotated_object(a_object_owner, l_name, a_object_type, l_annotations) + ); + l_object_lines.delete; + end if; - --pipe annotations from cache - if l_in_cache.count > 0 then - l_cursor := ut_annotation_cache_manager.get_annotations_for_objects(l_in_cache); - loop - fetch l_cursor bulk collect into l_results limit c_object_fetch_limit; - for i in 1 .. l_results.count loop - pipe row (l_results(i)); - end loop; - exit when l_cursor%notfound; + l_name := l_names(i); + l_object_lines(l_object_lines.count+1) := l_lines(i); end loop; - close l_cursor; + exit when a_sources_cursor%notfound; + + end loop; + if a_sources_cursor%rowcount > 0 then + l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines); + ut_annotation_cache_manager.update_cache( + ut_annotated_object(a_object_owner, l_name, a_object_type, l_annotations) + ); + l_object_lines.delete; end if; + close a_sources_cursor; + commit; + end; - --if some source needs parsing - if l_to_parse.count > 0 then - --do we need to parse all of sources - if l_in_cache.count = 0 then - l_cursor := get_sources_for_annotations(a_object_owner, a_object_type, a_object_name); - else - -- sources need to be filtered by objects to parse - l_cursor := get_sources_for_annotations(a_object_owner, a_object_type, l_to_parse); - end if; - - --remove cached annotations data for objects that will be refreshed - ut_annotation_cache_manager.cleanup_cache(l_to_parse); - loop - fetch l_cursor bulk collect into l_names, l_lines limit c_lines_fetch_limit; + procedure rebuild_annotation_cache( a_object_owner varchar2, a_object_type varchar2, a_info_rows ut_annotation_objs_cache_info) is + l_objects_in_cache_count integer; + l_objects_to_parse ut_annotation_objs_cache_info := ut_annotation_objs_cache_info(); + begin + --get list of objects in cache + select count( 1)into l_objects_in_cache_count from table(a_info_rows) x where x.needs_refresh = 'N'; - for i in 1 .. l_names.count loop + --if cache is empty and there are objects to parse + if l_objects_in_cache_count = 0 and a_info_rows.count > 0 then - if l_names(i) != l_name then - l_result := ut_annotated_object( - a_object_owner, l_name, a_object_type, - ut_annotation_parser.parse_object_annotations(l_object_lines) - ); - ut_annotation_cache_manager.update_cache(l_result); - if l_result.annotations.count > 0 then - pipe row (l_result); - end if; - l_object_lines.delete; - end if; + build_annot_cache_for_sources( + a_object_owner, a_object_type, + --all schema objects + get_sources_to_annotate(a_object_owner, a_object_type), + a_info_rows + ); - l_name := l_names(i); - l_object_lines(l_object_lines.count+1) := l_lines(i); + --if not all in cache, get list of objects to rebuild cache for + elsif l_objects_in_cache_count < a_info_rows.count then - end loop; - exit when l_cursor%notfound; + select value(x)bulk collect into l_objects_to_parse from table(a_info_rows) x where x.needs_refresh = 'Y'; - end loop; - - if l_name is not null then - l_result := ut_annotated_object( - a_object_owner, l_name, a_object_type, - ut_annotation_parser.parse_object_annotations(l_object_lines) + --if some source needs parsing and putting into cache + if l_objects_to_parse.count > 0 then + build_annot_cache_for_sources( + a_object_owner, a_object_type, + get_sources_to_annotate(a_object_owner, a_object_type, l_objects_to_parse), + l_objects_to_parse ); - ut_annotation_cache_manager.update_cache(l_result); - if l_result.annotations.count > 0 then - pipe row (l_result); - end if; - l_object_lines.delete; end if; - close l_cursor; end if; + end; + + ------------------------------------------------------------ + --public definitions + ------------------------------------------------------------ + procedure rebuild_annotation_cache(a_object_owner varchar2, a_object_type varchar2) is + l_info_cursor sys_refcursor; + l_info_rows ut_annotation_objs_cache_info; + begin + l_info_cursor := get_annotation_objs_info_cur(a_object_owner, a_object_type); + fetch l_info_cursor bulk collect into l_info_rows; + close l_info_cursor; + rebuild_annotation_cache(a_object_owner, a_object_type, l_info_rows); + end; + + function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2) return ut_annotated_objects pipelined is + l_info_cursor sys_refcursor; + l_info_rows ut_annotation_objs_cache_info; + l_cursor sys_refcursor; + l_results ut_annotated_objects; + c_object_fetch_limit constant integer := 10; + begin + l_info_cursor := get_annotation_objs_info_cur(a_object_owner, a_object_type); + fetch l_info_cursor bulk collect into l_info_rows; + close l_info_cursor; + rebuild_annotation_cache(a_object_owner, a_object_type, l_info_rows); + + --pipe annotations from cache + l_cursor := ut_annotation_cache_manager.get_annotations_for_objects(l_info_rows); + loop + fetch l_cursor bulk collect into l_results limit c_object_fetch_limit; + for i in 1 .. l_results.count loop + pipe row (l_results(i)); + end loop; + exit when l_cursor%notfound; + end loop; + close l_cursor; end; diff --git a/source/core/annotations/ut_annotation_manager.pks b/source/core/annotations/ut_annotation_manager.pks index b043da641..a316d2460 100644 --- a/source/core/annotations/ut_annotation_manager.pks +++ b/source/core/annotations/ut_annotation_manager.pks @@ -21,14 +21,26 @@ create or replace package ut_annotation_manager authid current_user as */ /** - * Parses an object or all objects of a specified type for database schema. - * Results are returned in as a pipelined function. - * @param a_object_owner schema name to be parsed - * @param a_object_type type of object to be parsed - * @param a_object_name name of object to be parsed - optional + * Gets annotations for all objects of a specified type for database schema. + * Annotations that are stale or missing are parsed and placed in persistent cache. + * After placing in cache, annotation data is returned as pipelined table data. + * + * @param a_object_owner owner of objects to get annotations for + * @param a_object_type type of objects to get annotations for * @return array containing annotated objects along with annotations for each object (nested) */ - function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2, a_object_name varchar2 := null) return ut_annotated_objects pipelined; + function get_annotated_objects(a_object_owner varchar2, a_object_type varchar2) return ut_annotated_objects pipelined; + + /** + * Rebuilds annotation cache for a specified schema and object type. + * The procedure is called internally by `get_annotated_objects` function. + * It can be used to speedup initial execution of utPLSQL on a given schema + * if it is executed before any call is made to `ut.run` or `ut_runner.run` procedure. + * + * @param a_object_owner owner of objects to get annotations for + * @param a_object_type type of objects to get annotations for + */ + procedure rebuild_annotation_cache(a_object_owner varchar2, a_object_type varchar2); end ut_annotation_manager; / diff --git a/source/core/annotations/ut_annotation_cached_object.tps b/source/core/annotations/ut_annotation_obj_cache_info.tps similarity index 86% rename from source/core/annotations/ut_annotation_cached_object.tps rename to source/core/annotations/ut_annotation_obj_cache_info.tps index 24fd30e4a..43cf111ff 100644 --- a/source/core/annotations/ut_annotation_cached_object.tps +++ b/source/core/annotations/ut_annotation_obj_cache_info.tps @@ -1,4 +1,4 @@ -create type ut_annotation_cached_object as object( +create type ut_annotation_obj_cache_info as object( /* utPLSQL - Version X.X.X.X Copyright 2016 - 2017 utPLSQL Project @@ -18,7 +18,6 @@ create type ut_annotation_cached_object as object( object_owner varchar2(250), object_name varchar2(250), object_type varchar2(250), - needs_refresh varchar2(1), - cache_id number(20,0) + needs_refresh varchar2(1) ) / diff --git a/source/core/annotations/ut_annotation_cached_objects.tps b/source/core/annotations/ut_annotation_objs_cache_info.tps similarity index 88% rename from source/core/annotations/ut_annotation_cached_objects.tps rename to source/core/annotations/ut_annotation_objs_cache_info.tps index b1f64711c..4a99283e4 100644 --- a/source/core/annotations/ut_annotation_cached_objects.tps +++ b/source/core/annotations/ut_annotation_objs_cache_info.tps @@ -1,4 +1,4 @@ -create type ut_annotation_cached_objects as +create type ut_annotation_objs_cache_info as /* utPLSQL - Version X.X.X.X Copyright 2016 - 2017 utPLSQL Project @@ -15,5 +15,5 @@ create type ut_annotation_cached_objects as See the License for the specific language governing permissions and limitations under the License. */ -table of ut_annotation_cached_object +table of ut_annotation_obj_cache_info / diff --git a/source/create_synonyms_and_grants_for_public.sql b/source/create_synonyms_and_grants_for_public.sql index f3d77c942..14dfbcc54 100644 --- a/source/create_synonyms_and_grants_for_public.sql +++ b/source/create_synonyms_and_grants_for_public.sql @@ -76,8 +76,8 @@ grant select on &&ut3_owner..ut_annotation_cache_info to public; grant select on &&ut3_owner..ut_annotation_cache to public; grant execute on &&ut3_owner..ut_annotation_cache_manager to public; grant execute on &&ut3_owner..ut_annotation_parser to public; -grant execute on &&ut3_owner..ut_annotation_cached_objects to public; -grant execute on &&ut3_owner..ut_annotation_cached_object to public; +grant execute on &&ut3_owner..ut_annotation_objs_cache_info to public; +grant execute on &&ut3_owner..ut_annotation_obj_cache_info to public; prompt Creating synonyms for UTPLSQL objects in &&ut3_owner schema to PUBLIC diff --git a/source/create_synonyms_and_grants_for_user.sql b/source/create_synonyms_and_grants_for_user.sql index 4785aae32..3294898ed 100644 --- a/source/create_synonyms_and_grants_for_user.sql +++ b/source/create_synonyms_and_grants_for_user.sql @@ -96,8 +96,8 @@ grant select on &&ut3_owner..ut_annotation_cache_info to &ut3_user; grant select on &&ut3_owner..ut_annotation_cache to &ut3_user; grant execute on &&ut3_owner..ut_annotation_cache_manager to &ut3_user; grant execute on &&ut3_owner..ut_annotation_parser to &ut3_user; -grant execute on &&ut3_owner..ut_annotation_cached_objects to &ut3_user; -grant execute on &&ut3_owner..ut_annotation_cached_object to &ut3_user; +grant execute on &&ut3_owner..ut_annotation_objs_cache_info to &ut3_user; +grant execute on &&ut3_owner..ut_annotation_obj_cache_info to &ut3_user; prompt Creating synonyms for UTPLSQL objects in &&ut3_owner schema to user &&ut3_user diff --git a/source/install.sql b/source/install.sql index f6551bf26..aff99338f 100644 --- a/source/install.sql +++ b/source/install.sql @@ -82,8 +82,8 @@ alter session set plsql_warnings = 'ENABLE:ALL', 'DISABLE:(5004,5018,6000,6001,6 @@install_component.sql 'core/annotations/ut_annotations.tps' @@install_component.sql 'core/annotations/ut_annotated_object.tps' @@install_component.sql 'core/annotations/ut_annotated_objects.tps' -@@install_component.sql 'core/annotations/ut_annotation_cached_object.tps' -@@install_component.sql 'core/annotations/ut_annotation_cached_objects.tps' +@@install_component.sql 'core/annotations/ut_annotation_obj_cache_info.tps' +@@install_component.sql 'core/annotations/ut_annotation_objs_cache_info.tps' @@install_component.sql 'core/annotations/ut_annotation_cache_seq.sql' @@install_component.sql 'core/annotations/ut_annotation_cache_info.sql' @@install_component.sql 'core/annotations/ut_annotation_cache.sql' diff --git a/source/uninstall.sql b/source/uninstall.sql index 85318e73c..c6453ee54 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -160,9 +160,9 @@ drop table ut_annotation_cache_info cascade constraints; drop sequence ut_annotation_cache_seq; -drop type ut_annotation_cached_objects force; +drop type ut_annotation_objs_cache_info force; -drop type ut_annotation_cached_object force; +drop type ut_annotation_obj_cache_info force; drop type ut_annotated_objects force; diff --git a/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pkb b/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pkb new file mode 100644 index 000000000..dd03e44f4 --- /dev/null +++ b/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pkb @@ -0,0 +1,74 @@ +create or replace package body test_annotation_cache_manager is + + procedure setup_cache is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package dummy_test_package as + --%suite(dummy_test_suite) + --%rollback(manual) + + --%test(dummy_test) + --%beforetest(some_procedure) + procedure some_dummy_test_procedure; + end;]'; + execute immediate q'[create or replace procedure dummy_test_procedure as + --%some_annotation(some_text) + --%rollback(manual) + begin + null; + end;]'; + execute immediate q'[create or replace procedure ut3.dummy_test_procedure as + --%some_annotation(some_text) + --%rollback(manual) + begin + null; + end;]'; + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PROCEDURE'); + ut3.ut_annotation_manager.rebuild_annotation_cache('UT3','PROCEDURE'); + end; + + procedure cleanup_cache is + pragma autonomous_transaction; + begin + delete from ut3.ut_annotation_cache_info + where object_type = 'PROCEDURE' and object_owner in ('UT3',user) + or object_type = 'PACKAGE' and object_owner = user and object_name = 'DUMMY_TEST_PACKAGE'; + execute immediate q'[drop package dummy_test_package]'; + execute immediate q'[drop procedure dummy_test_procedure]'; + execute immediate q'[drop procedure ut3.dummy_test_procedure]'; + end; + + procedure test_purge_schema_type is + l_actual sys_refcursor; + begin + + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PROCEDURE'; + ut.expect(l_actual).not_to_be_empty(); + + --Act + ut3.ut_annotation_cache_manager.purge_cache(user,'PROCEDURE'); + + --Assert + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PROCEDURE'; + --Cache purged for object owner/type + ut.expect(l_actual).to_be_empty(); + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PACKAGE'; + --Cache not purged for other types + ut.expect(l_actual).not_to_be_empty(); + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner != user and object_type = 'PROCEDURE'; + --Cache not purged for other owners + ut.expect(l_actual).not_to_be_empty(); + + end; + +end test_annotation_cache_manager; +/ diff --git a/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pks b/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pks new file mode 100644 index 000000000..ff818914e --- /dev/null +++ b/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pks @@ -0,0 +1,17 @@ +create or replace package test_annotation_cache_manager is + + --%suite(ut_annotation_cache_manager) + --%suitepath(utplsql.core.annoations) + + + --%beforeeach + procedure setup_cache; + + --%aftereach + procedure cleanup_cache; + + --%test(Purges cache for a given schema and object type) + procedure test_purge_schema_type; + +end test_annotation_cache_manager; +/ diff --git a/test/annotations/ut_annotation_manager/test_annotation_manager.pkb b/test/annotations/ut_annotation_manager/test_annotation_manager.pkb new file mode 100644 index 000000000..066a6eade --- /dev/null +++ b/test/annotations/ut_annotation_manager/test_annotation_manager.pkb @@ -0,0 +1,253 @@ +create or replace package body test_annotation_manager is + + procedure create_dummy_package is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package dummy_package as + procedure some_dummy_procedure; + end;]'; + end; + + procedure drop_dummy_package is + pragma autonomous_transaction; + begin + execute immediate q'[drop package dummy_package]'; + end; + + procedure recompile_dummy_package is + pragma autonomous_transaction; + begin + execute immediate q'[alter package dummy_package compile]'; + end; + + procedure create_dummy_test_package is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package dummy_test_package as + --%suite(dummy_test_suite) + --%rollback(manual) + + --%test(dummy_test) + --%beforetest(some_procedure) + procedure some_dummy_test_procedure; + end;]'; + end; + + procedure modify_dummy_test_package is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package dummy_test_package as + --%suite(dummy_test_suite) + + --%test(dummy_test) + procedure some_dummy_test_procedure; + end;]'; + end; + + procedure drop_dummy_test_package is + pragma autonomous_transaction; + begin + execute immediate q'[drop package dummy_test_package]'; + end; + + procedure recompile_dummy_test_package is + pragma autonomous_transaction; + begin + execute immediate q'[alter package dummy_test_package compile]'; + end; + + procedure cleanup_annotation_cache is + pragma autonomous_transaction; + begin + delete from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PACKAGE' and object_name in ('DUMMY_PACKAGE','DUMMY_TEST_PACKAGE'); + commit; + end; + + + procedure add_new_package is + l_actual_cache_id integer; + l_actual integer; + l_start_date date; + begin + --Act + l_start_date := sysdate; + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + --Assert + select max(cache_id) + into l_actual_cache_id + from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PACKAGE' and object_name = 'DUMMY_PACKAGE' + and parse_time >= l_start_date; + + ut.expect(l_actual_cache_id).to_be_not_null; + + select count(1) + into l_actual + from ut3.ut_annotation_cache + where cache_id = l_actual_cache_id; + + ut.expect(l_actual).to_equal(0); + + end; + + procedure update_modified_package is + l_actual_cache_id integer; + l_actual integer; + l_start_date date; + begin + --Arrange + l_start_date := sysdate; + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + recompile_dummy_package(); + l_start_date := sysdate; + dbms_lock.sleep(1); + --Act + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + --Assert + select max(cache_id) + into l_actual_cache_id + from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PACKAGE' and object_name = 'DUMMY_PACKAGE' + and parse_time >= l_start_date; + + ut.expect(l_actual_cache_id).to_be_not_null; + + select count(1) + into l_actual + from ut3.ut_annotation_cache + where cache_id = l_actual_cache_id; + + ut.expect(l_actual).to_equal(0); + end; + + + procedure add_new_test_package is + l_actual_cache_id integer; + l_actual sys_refcursor; + l_expected sys_refcursor; + l_start_date date; + begin + --Arrange + l_start_date := sysdate; + --Act + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + --Assert + select max(cache_id) + into l_actual_cache_id + from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PACKAGE' and object_name = 'DUMMY_TEST_PACKAGE' + and parse_time >= l_start_date; + + ut.expect(l_actual_cache_id).to_be_not_null; + + open l_actual for + select annotation_position, annotation_name, annotation_text, subobject_name + from ut3.ut_annotation_cache where cache_id = l_actual_cache_id; + + open l_expected for + select 1 as annotation_position, 'suite' as annotation_name, + 'dummy_test_suite' as annotation_text, '' as subobject_name + from dual union all + select 2, 'rollback' , 'manual', '' as subobject_name + from dual union all + select 3, 'test' , 'dummy_test', 'some_dummy_test_procedure' as subobject_name + from dual union all + select 4, 'beforetest' , 'some_procedure', 'some_dummy_test_procedure' as subobject_name + from dual; + + ut.expect(l_actual).to_equal(l_expected); + end; + + + procedure update_modified_test_package is + l_actual_cache_id integer; + l_actual sys_refcursor; + l_expected sys_refcursor; + l_start_date date; + begin + --Arrange + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + l_start_date := sysdate; + modify_dummy_test_package(); + --Act + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + --Assert + select max(cache_id) + into l_actual_cache_id + from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PACKAGE' and object_name = 'DUMMY_TEST_PACKAGE' + and parse_time >= l_start_date; + + ut.expect(l_actual_cache_id).to_be_not_null; + + open l_actual for + select annotation_position, annotation_name, annotation_text, subobject_name + from ut3.ut_annotation_cache where cache_id = l_actual_cache_id; + + open l_expected for + select 1 as annotation_position, 'suite' as annotation_name, + 'dummy_test_suite' as annotation_text, to_char(null) as subobject_name + from dual union all + select 2, 'test' , 'dummy_test', 'some_dummy_test_procedure' as subobject_name + from dual; + + ut.expect(l_actual).to_equal(l_expected); + end; + + + procedure keep_dropped_data_in_cache is + l_actual_cache_id integer; + l_actual sys_refcursor; + l_expected sys_refcursor; + l_start_date date; + begin + --Arrange + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + l_start_date := sysdate; + drop_dummy_test_package(); + --Act + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + --Assert + select max(cache_id) + into l_actual_cache_id + from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PACKAGE' and object_name = 'DUMMY_TEST_PACKAGE' + and parse_time >= l_start_date; + + ut.expect(l_actual_cache_id).to_be_not_null; + + open l_actual for + select annotation_position, annotation_name, annotation_text, subobject_name + from ut3.ut_annotation_cache where cache_id = l_actual_cache_id; + + open l_expected for + select 1 as annotation_position, 'suite' as annotation_name, + 'dummy_test_suite' as annotation_text, '' as subobject_name + from dual union all + select 2, 'rollback' , 'manual', '' as subobject_name + from dual union all + select 3, 'test' , 'dummy_test', 'some_dummy_test_procedure' as subobject_name + from dual union all + select 4, 'beforetest' , 'some_procedure', 'some_dummy_test_procedure' as subobject_name + from dual; + + ut.expect(l_actual).to_equal(l_expected); + end; + + procedure no_data_for_dropped_object is + l_actual sys_refcursor; + begin + --Arrange + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + drop_dummy_test_package(); + --Act + open l_actual for + select * from table(ut3.ut_annotation_manager.get_annotated_objects(user,'PACKAGE')) + where object_name = 'DUMMY_TEST_PACKAGE'; + --Assert + ut.expect(l_actual).to_be_empty(); + end; + +end test_annotation_manager; +/ diff --git a/test/annotations/ut_annotation_manager/test_annotation_manager.pks b/test/annotations/ut_annotation_manager/test_annotation_manager.pks new file mode 100644 index 000000000..75575db01 --- /dev/null +++ b/test/annotations/ut_annotation_manager/test_annotation_manager.pks @@ -0,0 +1,46 @@ +create or replace package test_annotation_manager is + + --%suite(ut_annotation_manager) + --%suitepath(utplsql.core.annoations) + + --%aftereach + procedure cleanup_annotation_cache; + + procedure create_dummy_package; + + procedure drop_dummy_package; + + procedure create_dummy_test_package; + + procedure drop_dummy_test_package; + + --%test(Adds new package to annotation cache info) + --%beforetest(create_dummy_package) + --%aftertest(drop_dummy_package) + procedure add_new_package; + + --%test(Updates annotation cache info for modified package) + --%beforetest(create_dummy_package) + --%aftertest(drop_dummy_package) + procedure update_modified_package; + + --%test(Adds annotations to cache for unit test package) + --%beforetest(create_dummy_test_package) + --%aftertest(drop_dummy_test_package) + procedure add_new_test_package; + + --%test(Updates annotations in cache for modified test package) + --%beforetest(create_dummy_test_package) + --%aftertest(drop_dummy_test_package) + procedure update_modified_test_package; + + --%test(Keeps annotations in cache after object was removed) + --%beforetest(create_dummy_test_package) + procedure keep_dropped_data_in_cache; + + --%test(Does not return data for dropped object) + --%beforetest(create_dummy_test_package) + procedure no_data_for_dropped_object; + +end test_annotation_manager; +/ diff --git a/test/install_tests.sql b/test/install_tests.sql index 4b5d4aa8a..28f05b45f 100644 --- a/test/install_tests.sql +++ b/test/install_tests.sql @@ -12,6 +12,8 @@ whenever oserror exit failure rollback @@ut_reporters/test_coveralls_reporter.pks @ut_expectations/test_expectations_cursor.pks @@ut_runner/test_ut_runner.pks +@@annotations/ut_annotation_manager/test_annotation_manager.pks +@@annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pks @core.pkb @ut_utils/test_ut_utils.pkb @@ -24,6 +26,8 @@ whenever oserror exit failure rollback @@ut_reporters/test_coveralls_reporter.pkb @ut_expectations/test_expectations_cursor.pkb @@ut_runner/test_ut_runner.pkb +@@annotations/ut_annotation_manager/test_annotation_manager.pkb +@@annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pkb set linesize 200 set define on diff --git a/test/ut_expectations/test_expectations_cursor.pkb b/test/ut_expectations/test_expectations_cursor.pkb index a41ea48ad..4da3cd0f0 100644 --- a/test/ut_expectations/test_expectations_cursor.pkb +++ b/test/ut_expectations/test_expectations_cursor.pkb @@ -20,26 +20,26 @@ create or replace package body test_expectations_cursor is procedure test_cursor_w_temp_table as pragma autonomous_transaction; - c_expected sys_refcursor; - c_actual sys_refcursor; + l_expected sys_refcursor; + l_actual sys_refcursor; begin -- Arrange execute immediate 'insert into gtt_test_table ( value ) values ( ''Test-entry'' )'; - open c_expected for select 'Test-entry' as value from dual; - open c_actual for 'select * from gtt_test_table'; - + open l_expected for select 'Test-entry' as value from dual; + open l_actual for 'select * from gtt_test_table'; + --Act - execute the expectation on cursor opened on GTT - ut3.ut.expect( c_actual ).to_equal( a_expected => c_expected ); - + ut3.ut.expect( l_actual ).to_equal( l_expected ); + --Assert - check that expectation was executed successfully ut.expect(ut3.ut_expectation_processor.get_status()).to_equal(ut3.ut_utils.tr_success); - + --Cleanup ut3.ut_expectation_processor.clear_expectations(); rollback; end; end; -/ \ No newline at end of file +/ From cd394c7d8cf827821ff3fc3dad5fcc4e6fd88fb7 Mon Sep 17 00:00:00 2001 From: Jacek Date: Mon, 23 Oct 2017 07:19:44 +0100 Subject: [PATCH 17/19] Fixed test execution on 12.1. Added wrapper procedure `purge_cache` in `ut_annotation_manager` Changed tests to use wrapper procedure. --- source/api/ut.pks | 20 ++--- .../ut_annotation_cache_manager.pkb | 4 +- .../annotations/ut_annotation_manager.pkb | 5 ++ .../annotations/ut_annotation_manager.pks | 8 ++ .../test_annotation_cache_manager.pkb | 74 ------------------- .../test_annotation_cache_manager.pks | 17 ----- test/install_tests.sql | 6 +- .../test_annotation_manager.pkb | 73 +++++++++++++++++- .../test_annotation_manager.pks | 8 ++ 9 files changed, 106 insertions(+), 109 deletions(-) delete mode 100644 test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pkb delete mode 100644 test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pks rename test/{annotations => }/ut_annotation_manager/test_annotation_manager.pkb (77%) rename test/{annotations => }/ut_annotation_manager/test_annotation_manager.pks (86%) diff --git a/source/api/ut.pks b/source/api/ut.pks index 58a4ab6a8..61c7d5918 100644 --- a/source/api/ut.pks +++ b/source/api/ut.pks @@ -119,18 +119,18 @@ create or replace package ut authid current_user as a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null ); - /* - Helper procedure to set NLS session parameter for date processing in refcursor. - It needs to be called before refcursor is open in order to have DATE data type data in refcursor - properly transformed into XML format as a date-time element. - If the function is not called before opening a cursor to be compared, the DATE data is compared using default NLS setting for date. - */ + /** + * Helper procedure to set NLS session parameter for date processing in refcursor. + * It needs to be called before refcursor is open in order to have DATE data type data in refcursor + * properly transformed into XML format as a date-time element. + * If the function is not called before opening a cursor to be compared, the DATE data is compared using default NLS setting for date. + */ procedure set_nls; - /* - Helper procedure to reset NLS session parameter to it's original state. - It needs to be called after refcursor is open in order restore the original session state and keep the NLS date setting at default. - */ + /** + * Helper procedure to reset NLS session parameter to it's original state. + * It needs to be called after refcursor is open in order restore the original session state and keep the NLS date setting at default. + */ procedure reset_nls; end ut; diff --git a/source/core/annotations/ut_annotation_cache_manager.pkb b/source/core/annotations/ut_annotation_cache_manager.pkb index 3adff4aab..e6a81a497 100644 --- a/source/core/annotations/ut_annotation_cache_manager.pkb +++ b/source/core/annotations/ut_annotation_cache_manager.pkb @@ -62,7 +62,9 @@ create or replace package body ut_annotation_cache_manager as in (select i.cache_id from ut_annotation_cache_info i join table (a_objects) o - using (object_name, object_type, object_owner) + on o.object_name = i.object_name + and o.object_type = i.object_type + and o.object_owner = i.object_owner ); merge into ut_annotation_cache_info i diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb index 11e16f243..ceb085824 100644 --- a/source/core/annotations/ut_annotation_manager.pkb +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -208,5 +208,10 @@ create or replace package body ut_annotation_manager as end; + procedure purge_cache(a_object_owner varchar2, a_object_type varchar2) is + begin + ut_annotation_cache_manager.purge_cache(a_object_owner, a_object_type); + end; + end ut_annotation_manager; / diff --git a/source/core/annotations/ut_annotation_manager.pks b/source/core/annotations/ut_annotation_manager.pks index a316d2460..d78d02073 100644 --- a/source/core/annotations/ut_annotation_manager.pks +++ b/source/core/annotations/ut_annotation_manager.pks @@ -42,5 +42,13 @@ create or replace package ut_annotation_manager authid current_user as */ procedure rebuild_annotation_cache(a_object_owner varchar2, a_object_type varchar2); + /** + * Removes cached information about annotations for objects of specified type and specified owner + * + * @param a_object_owner owner of objects to purge annotations for + * @param a_object_type type of objects to purge annotations for + */ + procedure purge_cache(a_object_owner varchar2, a_object_type varchar2); + end ut_annotation_manager; / diff --git a/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pkb b/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pkb deleted file mode 100644 index dd03e44f4..000000000 --- a/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pkb +++ /dev/null @@ -1,74 +0,0 @@ -create or replace package body test_annotation_cache_manager is - - procedure setup_cache is - pragma autonomous_transaction; - begin - execute immediate q'[create or replace package dummy_test_package as - --%suite(dummy_test_suite) - --%rollback(manual) - - --%test(dummy_test) - --%beforetest(some_procedure) - procedure some_dummy_test_procedure; - end;]'; - execute immediate q'[create or replace procedure dummy_test_procedure as - --%some_annotation(some_text) - --%rollback(manual) - begin - null; - end;]'; - execute immediate q'[create or replace procedure ut3.dummy_test_procedure as - --%some_annotation(some_text) - --%rollback(manual) - begin - null; - end;]'; - ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); - ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PROCEDURE'); - ut3.ut_annotation_manager.rebuild_annotation_cache('UT3','PROCEDURE'); - end; - - procedure cleanup_cache is - pragma autonomous_transaction; - begin - delete from ut3.ut_annotation_cache_info - where object_type = 'PROCEDURE' and object_owner in ('UT3',user) - or object_type = 'PACKAGE' and object_owner = user and object_name = 'DUMMY_TEST_PACKAGE'; - execute immediate q'[drop package dummy_test_package]'; - execute immediate q'[drop procedure dummy_test_procedure]'; - execute immediate q'[drop procedure ut3.dummy_test_procedure]'; - end; - - procedure test_purge_schema_type is - l_actual sys_refcursor; - begin - - open l_actual for - select * from ut3.ut_annotation_cache_info - where object_owner = user and object_type = 'PROCEDURE'; - ut.expect(l_actual).not_to_be_empty(); - - --Act - ut3.ut_annotation_cache_manager.purge_cache(user,'PROCEDURE'); - - --Assert - open l_actual for - select * from ut3.ut_annotation_cache_info - where object_owner = user and object_type = 'PROCEDURE'; - --Cache purged for object owner/type - ut.expect(l_actual).to_be_empty(); - open l_actual for - select * from ut3.ut_annotation_cache_info - where object_owner = user and object_type = 'PACKAGE'; - --Cache not purged for other types - ut.expect(l_actual).not_to_be_empty(); - open l_actual for - select * from ut3.ut_annotation_cache_info - where object_owner != user and object_type = 'PROCEDURE'; - --Cache not purged for other owners - ut.expect(l_actual).not_to_be_empty(); - - end; - -end test_annotation_cache_manager; -/ diff --git a/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pks b/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pks deleted file mode 100644 index ff818914e..000000000 --- a/test/annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pks +++ /dev/null @@ -1,17 +0,0 @@ -create or replace package test_annotation_cache_manager is - - --%suite(ut_annotation_cache_manager) - --%suitepath(utplsql.core.annoations) - - - --%beforeeach - procedure setup_cache; - - --%aftereach - procedure cleanup_cache; - - --%test(Purges cache for a given schema and object type) - procedure test_purge_schema_type; - -end test_annotation_cache_manager; -/ diff --git a/test/install_tests.sql b/test/install_tests.sql index 28f05b45f..a6340efc3 100644 --- a/test/install_tests.sql +++ b/test/install_tests.sql @@ -12,8 +12,7 @@ whenever oserror exit failure rollback @@ut_reporters/test_coveralls_reporter.pks @ut_expectations/test_expectations_cursor.pks @@ut_runner/test_ut_runner.pks -@@annotations/ut_annotation_manager/test_annotation_manager.pks -@@annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pks +@@ut_annotation_manager/test_annotation_manager.pks @core.pkb @ut_utils/test_ut_utils.pkb @@ -26,8 +25,7 @@ whenever oserror exit failure rollback @@ut_reporters/test_coveralls_reporter.pkb @ut_expectations/test_expectations_cursor.pkb @@ut_runner/test_ut_runner.pkb -@@annotations/ut_annotation_manager/test_annotation_manager.pkb -@@annotations/ut_annotation_cache_manager/test_annotation_cache_manager.pkb +@@ut_annotation_manager/test_annotation_manager.pkb set linesize 200 set define on diff --git a/test/annotations/ut_annotation_manager/test_annotation_manager.pkb b/test/ut_annotation_manager/test_annotation_manager.pkb similarity index 77% rename from test/annotations/ut_annotation_manager/test_annotation_manager.pkb rename to test/ut_annotation_manager/test_annotation_manager.pkb index 066a6eade..4fc1eb8df 100644 --- a/test/annotations/ut_annotation_manager/test_annotation_manager.pkb +++ b/test/ut_annotation_manager/test_annotation_manager.pkb @@ -64,6 +64,39 @@ create or replace package body test_annotation_manager is commit; end; + procedure setup_cache is + pragma autonomous_transaction; + begin + create_dummy_test_package(); + execute immediate q'[create or replace procedure dummy_test_procedure as + --%some_annotation(some_text) + --%rollback(manual) + begin + null; + end;]'; + execute immediate q'[create or replace procedure ut3.dummy_test_procedure as + --%some_annotation(some_text) + --%rollback(manual) + begin + null; + end;]'; + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PROCEDURE'); + ut3.ut_annotation_manager.rebuild_annotation_cache('UT3','PROCEDURE'); + end; + + procedure cleanup_cache is + pragma autonomous_transaction; + begin + delete from ut3.ut_annotation_cache_info + where object_type = 'PROCEDURE' and object_owner in ('UT3',user) + or object_type = 'PACKAGE' and object_owner = user and object_name = 'DUMMY_TEST_PACKAGE'; + drop_dummy_test_package(); + execute immediate q'[drop procedure dummy_test_procedure]'; + execute immediate q'[drop procedure ut3.dummy_test_procedure]'; + end; + + procedure add_new_package is l_actual_cache_id integer; @@ -143,7 +176,8 @@ create or replace package body test_annotation_manager is open l_actual for select annotation_position, annotation_name, annotation_text, subobject_name - from ut3.ut_annotation_cache where cache_id = l_actual_cache_id; + from ut3.ut_annotation_cache where cache_id = l_actual_cache_id + order by annotation_position; open l_expected for select 1 as annotation_position, 'suite' as annotation_name, @@ -183,7 +217,8 @@ create or replace package body test_annotation_manager is open l_actual for select annotation_position, annotation_name, annotation_text, subobject_name - from ut3.ut_annotation_cache where cache_id = l_actual_cache_id; + from ut3.ut_annotation_cache where cache_id = l_actual_cache_id + order by annotation_position; open l_expected for select 1 as annotation_position, 'suite' as annotation_name, @@ -219,7 +254,8 @@ create or replace package body test_annotation_manager is open l_actual for select annotation_position, annotation_name, annotation_text, subobject_name - from ut3.ut_annotation_cache where cache_id = l_actual_cache_id; + from ut3.ut_annotation_cache where cache_id = l_actual_cache_id + order by annotation_position; open l_expected for select 1 as annotation_position, 'suite' as annotation_name, @@ -249,5 +285,36 @@ create or replace package body test_annotation_manager is ut.expect(l_actual).to_be_empty(); end; + procedure test_purge_schema_type is + l_actual sys_refcursor; + begin + + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PROCEDURE'; + ut.expect(l_actual).not_to_be_empty(); + + --Act + ut3.ut_annotation_cache_manager.purge_cache(user,'PROCEDURE'); + + --Assert + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PROCEDURE'; + --Cache purged for object owner/type + ut.expect(l_actual).to_be_empty(); + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PACKAGE'; + --Cache not purged for other types + ut.expect(l_actual).not_to_be_empty(); + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner != user and object_type = 'PROCEDURE'; + --Cache not purged for other owners + ut.expect(l_actual).not_to_be_empty(); + + end; + end test_annotation_manager; / diff --git a/test/annotations/ut_annotation_manager/test_annotation_manager.pks b/test/ut_annotation_manager/test_annotation_manager.pks similarity index 86% rename from test/annotations/ut_annotation_manager/test_annotation_manager.pks rename to test/ut_annotation_manager/test_annotation_manager.pks index 75575db01..66110440e 100644 --- a/test/annotations/ut_annotation_manager/test_annotation_manager.pks +++ b/test/ut_annotation_manager/test_annotation_manager.pks @@ -42,5 +42,13 @@ create or replace package test_annotation_manager is --%beforetest(create_dummy_test_package) procedure no_data_for_dropped_object; + procedure setup_cache; + procedure cleanup_cache; + + --%test(Purges cache for a given schema and object type) + --%beforetest(setup_cache) + --%aftertest(cleanup_cache) + procedure test_purge_schema_type; + end test_annotation_manager; / From 5967a3afbaf6c1e071738b4779af49ddfceba29b Mon Sep 17 00:00:00 2001 From: Jacek Date: Mon, 23 Oct 2017 07:48:45 +0100 Subject: [PATCH 18/19] Fixed test execution on 12.1. Added wrapper procedures `purge_cache`, `rebuild_annotation_cache` in `ut_runner` Added tests for wrappers. Updated documentation for annotation cache calls. --- docs/userguide/annotations.md | 23 +++++ source/api/ut_runner.pkb | 10 ++ source/api/ut_runner.pks | 19 ++++ .../test_annotation_manager.pkb | 64 ------------ .../test_annotation_manager.pks | 8 -- test/ut_runner/test_ut_runner.pkb | 98 +++++++++++++++++++ test/ut_runner/test_ut_runner.pks | 15 +++ 7 files changed, 165 insertions(+), 72 deletions(-) diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index 479b17f6a..cd7ef0d8b 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -214,3 +214,26 @@ When processing the test suite `test_pkg` defined in [Example of annotated test rollback to savepoint 'beforeall' ``` + +# Annotation cache + +utPLSQL needs to scan sources of package specifications to identify and parse annotations. +To improve framework startup time, specially when dealing with database users owning large amount of packages the framework has build-in persistent cache for annotations. + +Cache is checked for staleness and refreshed automatically on every run. +The initial startup of utPLSQL for a schema will take longer than consecutive executions. + +If you're in situation, where your database is controlled via CI/CD server and gets refreshed/wiped before each run of your tests, +consider building upfront and creating the snapshot of our database after the cache was refreshed. + +To build annotation cache without actually invoking any tests, call `ut_runner.rebuild_annotation_cache(a_object_owner, a_object_type)` sql block for every unit test owner that you want to have annotations cache prebuilt. + +Example: +```sql +exec ut_runner.rebuild_annotation_cache('HR', 'PACKAGE'); +``` + +To purge annotations cache call: +```sql +exec ut_runner.purge_cache('HR', 'PACKAGE'); +``` diff --git a/source/api/ut_runner.pkb b/source/api/ut_runner.pkb index e5574c62f..1c276f4ab 100644 --- a/source/api/ut_runner.pkb +++ b/source/api/ut_runner.pkb @@ -119,5 +119,15 @@ create or replace package body ut_runner is ); end; + procedure rebuild_annotation_cache(a_object_owner varchar2, a_object_type varchar2) is + begin + ut_annotation_manager.rebuild_annotation_cache(a_object_owner, a_object_type); + end; + + procedure purge_cache(a_object_owner varchar2, a_object_type varchar2) is + begin + ut_annotation_manager.purge_cache(a_object_owner, a_object_type); + end; + end ut_runner; / diff --git a/source/api/ut_runner.pks b/source/api/ut_runner.pks index ceead4ea6..0794d4356 100644 --- a/source/api/ut_runner.pks +++ b/source/api/ut_runner.pks @@ -58,5 +58,24 @@ create or replace package ut_runner authid current_user is a_include_objects ut_varchar2_list := null, a_exclude_objects ut_varchar2_list := null, a_fail_on_errors boolean default false ); + /** + * Rebuilds annotation cache for a specified schema and object type. + * The procedure is called internally by `get_annotated_objects` function. + * It can be used to speedup initial execution of utPLSQL on a given schema + * if it is executed before any call is made to `ut.run` or `ut_runner.run` procedure. + * + * @param a_object_owner owner of objects to get annotations for + * @param a_object_type type of objects to get annotations for + */ + procedure rebuild_annotation_cache(a_object_owner varchar2, a_object_type varchar2); + + /** + * Removes cached information about annotations for objects of specified type and specified owner + * + * @param a_object_owner owner of objects to purge annotations for + * @param a_object_type type of objects to purge annotations for + */ + procedure purge_cache(a_object_owner varchar2, a_object_type varchar2); + end ut_runner; / diff --git a/test/ut_annotation_manager/test_annotation_manager.pkb b/test/ut_annotation_manager/test_annotation_manager.pkb index 4fc1eb8df..7ac1e0870 100644 --- a/test/ut_annotation_manager/test_annotation_manager.pkb +++ b/test/ut_annotation_manager/test_annotation_manager.pkb @@ -64,39 +64,6 @@ create or replace package body test_annotation_manager is commit; end; - procedure setup_cache is - pragma autonomous_transaction; - begin - create_dummy_test_package(); - execute immediate q'[create or replace procedure dummy_test_procedure as - --%some_annotation(some_text) - --%rollback(manual) - begin - null; - end;]'; - execute immediate q'[create or replace procedure ut3.dummy_test_procedure as - --%some_annotation(some_text) - --%rollback(manual) - begin - null; - end;]'; - ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); - ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PROCEDURE'); - ut3.ut_annotation_manager.rebuild_annotation_cache('UT3','PROCEDURE'); - end; - - procedure cleanup_cache is - pragma autonomous_transaction; - begin - delete from ut3.ut_annotation_cache_info - where object_type = 'PROCEDURE' and object_owner in ('UT3',user) - or object_type = 'PACKAGE' and object_owner = user and object_name = 'DUMMY_TEST_PACKAGE'; - drop_dummy_test_package(); - execute immediate q'[drop procedure dummy_test_procedure]'; - execute immediate q'[drop procedure ut3.dummy_test_procedure]'; - end; - - procedure add_new_package is l_actual_cache_id integer; @@ -285,36 +252,5 @@ create or replace package body test_annotation_manager is ut.expect(l_actual).to_be_empty(); end; - procedure test_purge_schema_type is - l_actual sys_refcursor; - begin - - open l_actual for - select * from ut3.ut_annotation_cache_info - where object_owner = user and object_type = 'PROCEDURE'; - ut.expect(l_actual).not_to_be_empty(); - - --Act - ut3.ut_annotation_cache_manager.purge_cache(user,'PROCEDURE'); - - --Assert - open l_actual for - select * from ut3.ut_annotation_cache_info - where object_owner = user and object_type = 'PROCEDURE'; - --Cache purged for object owner/type - ut.expect(l_actual).to_be_empty(); - open l_actual for - select * from ut3.ut_annotation_cache_info - where object_owner = user and object_type = 'PACKAGE'; - --Cache not purged for other types - ut.expect(l_actual).not_to_be_empty(); - open l_actual for - select * from ut3.ut_annotation_cache_info - where object_owner != user and object_type = 'PROCEDURE'; - --Cache not purged for other owners - ut.expect(l_actual).not_to_be_empty(); - - end; - end test_annotation_manager; / diff --git a/test/ut_annotation_manager/test_annotation_manager.pks b/test/ut_annotation_manager/test_annotation_manager.pks index 66110440e..75575db01 100644 --- a/test/ut_annotation_manager/test_annotation_manager.pks +++ b/test/ut_annotation_manager/test_annotation_manager.pks @@ -42,13 +42,5 @@ create or replace package test_annotation_manager is --%beforetest(create_dummy_test_package) procedure no_data_for_dropped_object; - procedure setup_cache; - procedure cleanup_cache; - - --%test(Purges cache for a given schema and object type) - --%beforetest(setup_cache) - --%aftertest(cleanup_cache) - procedure test_purge_schema_type; - end test_annotation_manager; / diff --git a/test/ut_runner/test_ut_runner.pkb b/test/ut_runner/test_ut_runner.pkb index 1da63965d..06c595618 100644 --- a/test/ut_runner/test_ut_runner.pkb +++ b/test/ut_runner/test_ut_runner.pkb @@ -1,5 +1,50 @@ create or replace package body test_ut_runner is + procedure setup_cache_objects is + pragma autonomous_transaction; + begin + execute immediate q'[create or replace package dummy_test_package as + --%suite(dummy_test_suite) + --%rollback(manual) + + --%test(dummy_test) + --%beforetest(some_procedure) + procedure some_dummy_test_procedure; + end;]'; + execute immediate q'[create or replace procedure dummy_test_procedure as + --%some_annotation(some_text) + --%rollback(manual) + begin + null; + end;]'; + execute immediate q'[create or replace procedure ut3.dummy_test_procedure as + --%some_annotation(some_text) + --%rollback(manual) + begin + null; + end;]'; + end; + + procedure setup_cache is + pragma autonomous_transaction; + begin + setup_cache_objects(); + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PROCEDURE'); + ut3.ut_annotation_manager.rebuild_annotation_cache('UT3','PROCEDURE'); + end; + + procedure cleanup_cache is + pragma autonomous_transaction; + begin + delete from ut3.ut_annotation_cache_info + where object_type = 'PROCEDURE' and object_owner in ('UT3',user) + or object_type = 'PACKAGE' and object_owner = user and object_name = 'DUMMY_TEST_PACKAGE'; + execute immediate q'[drop package dummy_test_package]'; + execute immediate q'[drop procedure dummy_test_procedure]'; + execute immediate q'[drop procedure ut3.dummy_test_procedure]'; + end; + procedure create_test_spec as pragma autonomous_transaction; @@ -160,5 +205,58 @@ end;'; drop_test_package(); end; + procedure test_purge_cache_schema_type is + l_actual sys_refcursor; + begin + + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PROCEDURE'; + ut.expect(l_actual).not_to_be_empty(); + + --Act + ut3.ut_runner.purge_cache(user,'PROCEDURE'); + + --Assert + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PROCEDURE'; + --Cache purged for object owner/type + ut.expect(l_actual).to_be_empty(); + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner = user and object_type = 'PACKAGE'; + --Cache not purged for other types + ut.expect(l_actual).not_to_be_empty(); + open l_actual for + select * from ut3.ut_annotation_cache_info + where object_owner = 'UT3' and object_type = 'PROCEDURE'; + --Cache not purged for other owners + ut.expect(l_actual).not_to_be_empty(); + + end; + + procedure test_rebuild_cache_schema_type is + l_actual integer; + begin + --Act + ut3.ut_annotation_manager.rebuild_annotation_cache(user,'PACKAGE'); + --Assert + select count(1) into l_actual + from ut3.ut_annotation_cache_info i + join ut3.ut_annotation_cache c on c.cache_id = i.cache_id + where object_owner = user and object_type = 'PACKAGE' and object_name = 'DUMMY_TEST_PACKAGE'; + --Rebuild cache for user/packages + ut.expect(l_actual).to_equal(4); + + select count(1) into l_actual + from ut3.ut_annotation_cache_info i + join ut3.ut_annotation_cache c on c.cache_id = i.cache_id + where object_owner = 'UT3' and object_type = 'PROCEDURE'; + + --Did not rebuild cache for ut3/procedures + ut.expect(l_actual).to_equal(0); + end; + end; / diff --git a/test/ut_runner/test_ut_runner.pks b/test/ut_runner/test_ut_runner.pks index 7b4ab3d40..04e5ce6ae 100644 --- a/test/ut_runner/test_ut_runner.pks +++ b/test/ut_runner/test_ut_runner.pks @@ -28,5 +28,20 @@ create or replace package test_ut_runner is --%test(does not consume dbms_output from before the run) procedure run_keep_dbms_output_buffer; + procedure setup_cache; + procedure cleanup_cache; + + --%test(Purges cache for a given schema and object type) + --%beforetest(setup_cache) + --%aftertest(cleanup_cache) + procedure test_purge_cache_schema_type; + + procedure setup_cache_objects; + + --%test(Rebuilds cache for a given schema and object type) + --%beforetest(setup_cache_objects) + --%aftertest(cleanup_cache) + procedure test_rebuild_cache_schema_type; + end; / From 4f5d87052ecbd449af290f3e533b1c6699139ed7 Mon Sep 17 00:00:00 2001 From: Jacek Date: Tue, 24 Oct 2017 22:38:16 +0100 Subject: [PATCH 19/19] Merge conflicts cleanup --- ...arse_package_annotations.ParseAnnotationParamsWithBrackets.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationParamsWithBrackets.sql diff --git a/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationParamsWithBrackets.sql b/old_tests/ut_annotations/ut_annotations.parse_package_annotations.ParseAnnotationParamsWithBrackets.sql deleted file mode 100644 index e69de29bb..000000000