From aa400a4a67cc44cd9862a4cf707ba01568cd2c37 Mon Sep 17 00:00:00 2001 From: Lukasz Wasylow Date: Fri, 4 Apr 2025 19:47:24 +0100 Subject: [PATCH 1/4] Creating a list of annotations allowed. Current issues is that any comment matching annotation --% is pushed into ut cache packages which causes exclusion from coverage reports. --- .../annotations/ut_annotation_manager.pkb | 35 +++++++++++++- .../annotations/ut_annotation_manager.pks | 2 + source/core/ut_suite_builder.pkb | 47 ++++++------------- source/core/ut_utils.pks | 35 ++++++++++++++ 4 files changed, 84 insertions(+), 35 deletions(-) diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb index 65f7b3e40..ac3eac092 100644 --- a/source/core/annotations/ut_annotation_manager.pkb +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -99,18 +99,32 @@ create or replace package body ut_annotation_manager as return l_result; end; + function get_annotations_list return ut_varchar2_list pipelined is + begin + for i in 1 .. ut_utils.gc_supported_annotations.count loop + pipe row( ut_utils.gc_supported_annotations(i) ); + end loop; + end get_annotations_list; + 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_source_view_name(); l_card natural; + l_allowed_annotations varchar2(32767) := 'suite'; begin + + select listagg(column_value,'|') within group (order by column_value) + into l_allowed_annotations + from table(get_annotations_list); + + l_card := ut_utils.scale_cardinality(cardinality(a_objects_to_refresh)); open l_result for q'[select /*+ no_parallel */ x.name, x.text from (select /*+ cardinality( r ]'||l_card||q'[ )*/ s.name, s.text, s.line, max(case when s.text like '%--%\%%' escape '\' - and regexp_like(s.text,'^\s*--\s*%') + and regexp_like(s.text,'^\s*--\s*]'||l_allowed_annotations||q'[%') then 'Y' else 'N' end ) over(partition by s.name) is_annotated @@ -125,7 +139,24 @@ create or replace package body ut_annotation_manager as where x.is_annotated = 'Y' order by x.name, x.line]' using a_objects_to_refresh; - + raise_application_error(-20001, q'[select /*+ no_parallel */ x.name, x.text + from (select /*+ cardinality( r ]'||l_card||q'[ )*/ + s.name, s.text, s.line, + max(case when s.text like '%--%\%%' escape '\' + and regexp_like(s.text,'^\s*--\s*]'||l_allowed_annotations||q'[%') + then 'Y' else 'N' end + ) + over(partition by s.name) is_annotated + from table(:a_objects_to_refresh) r + join ]'||l_sources_view||q'[ s + on s.name = r.object_name + and s.owner = r.object_owner + and s.type = r.object_type + where s.owner = ']'||ut_utils.qualified_sql_name(a_object_owner)||q'[' + and s.type = ']'||ut_utils.qualified_sql_name(a_object_type)||q'[' + ) x + where x.is_annotated = 'Y' + order by x.name, x.line]'); return l_result; end; diff --git a/source/core/annotations/ut_annotation_manager.pks b/source/core/annotations/ut_annotation_manager.pks index 20fcd810f..9816b7265 100644 --- a/source/core/annotations/ut_annotation_manager.pks +++ b/source/core/annotations/ut_annotation_manager.pks @@ -20,6 +20,8 @@ create or replace package ut_annotation_manager authid current_user as * Builds annotations out of database source code by reading it from cache */ + function get_annotations_list return ut_varchar2_list pipelined; + /** * 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. diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index ebb113370..2d71870db 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -21,46 +21,27 @@ create or replace package body ut_suite_builder is subtype t_object_name is varchar2(500); subtype t_annotation_position is binary_integer; - gc_suite constant t_annotation_name := 'suite'; - gc_suitepath constant t_annotation_name := 'suitepath'; - gc_tags constant t_annotation_name := 'tags'; + gc_suite constant t_annotation_name := ut_utils.gc_suite; + gc_suitepath constant t_annotation_name := ut_utils.gc_suitepath; + gc_tags constant t_annotation_name := ut_utils.gc_tags; gc_test constant t_annotation_name := ut_utils.gc_test_execute; - gc_disabled constant t_annotation_name := 'disabled'; - gc_displayname constant t_annotation_name := 'displayname'; + gc_disabled constant t_annotation_name := ut_utils.gc_disabled_ann; + gc_displayname constant t_annotation_name := ut_utils.gc_displayname; gc_beforeall constant t_annotation_name := ut_utils.gc_before_all; gc_beforeeach constant t_annotation_name := ut_utils.gc_before_each; gc_beforetest constant t_annotation_name := ut_utils.gc_before_test; gc_afterall constant t_annotation_name := ut_utils.gc_after_all; gc_aftereach constant t_annotation_name := ut_utils.gc_after_each; gc_aftertest constant t_annotation_name := ut_utils.gc_after_test; - gc_throws constant t_annotation_name := 'throws'; - gc_rollback constant t_annotation_name := 'rollback'; - gc_context constant t_annotation_name := 'context'; - gc_name constant t_annotation_name := 'name'; - gc_endcontext constant t_annotation_name := 'endcontext'; - - type tt_annotations is table of t_annotation_name; - - gc_supported_annotations constant tt_annotations - := tt_annotations( - gc_suite, - gc_suitepath, - gc_tags, - gc_test, - gc_disabled, - gc_displayname, - gc_beforeall, - gc_beforeeach, - gc_beforetest, - gc_afterall, - gc_aftereach, - gc_aftertest, - gc_throws, - gc_rollback, - gc_context, - gc_name, - gc_endcontext - ); + gc_throws constant t_annotation_name := ut_utils.gc_throws; + gc_rollback constant t_annotation_name := ut_utils.gc_rollback; + gc_context constant t_annotation_name := ut_utils.gc_context; + gc_name constant t_annotation_name := ut_utils.gc_name; + gc_endcontext constant t_annotation_name := ut_utils.gc_endcontext; + + + gc_supported_annotations constant ut_utils.tt_annotations + := ut_utils.gc_supported_annotations; type tt_executables is table of ut_executables index by t_annotation_position; diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 0e1e0a851..fb054e934 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -24,6 +24,10 @@ create or replace package ut_utils authid definer is gc_version constant varchar2(50) := 'v3.1.14.4206-develop'; subtype t_executable_type is varchar2(30); + subtype t_annotation_name is varchar2(4000); + gc_suite constant t_annotation_name := 'suite'; + gc_suitepath constant t_annotation_name := 'suitepath'; + gc_tags constant t_annotation_name := 'tags'; gc_before_all constant t_executable_type := 'beforeall'; gc_before_each constant t_executable_type := 'beforeeach'; gc_before_test constant t_executable_type := 'beforetest'; @@ -31,6 +35,37 @@ create or replace package ut_utils authid definer is gc_after_test constant t_executable_type := 'aftertest'; gc_after_each constant t_executable_type := 'aftereach'; gc_after_all constant t_executable_type := 'afterall'; + gc_disabled_ann constant t_annotation_name := 'disabled'; + gc_displayname constant t_annotation_name := 'displayname'; + gc_throws constant t_annotation_name := 'throws'; + gc_rollback constant t_annotation_name := 'rollback'; + gc_context constant t_annotation_name := 'context'; + gc_name constant t_annotation_name := 'name'; + gc_endcontext constant t_annotation_name := 'endcontext'; + + type tt_annotations is table of t_annotation_name; + + + gc_supported_annotations constant tt_annotations + := tt_annotations( + gc_suite, + gc_suitepath, + gc_tags, + gc_before_all, + gc_before_each, + gc_before_test, + gc_test_execute, + gc_after_test, + gc_after_each, + gc_after_all, + gc_disabled_ann, + gc_displayname, + gc_throws, + gc_rollback, + gc_context, + gc_name , + gc_endcontext + ); /* Constants: Test Results */ subtype t_test_result is binary_integer range 0 .. 3; From 0e2efabfd4db206a1be57fb18e2f7c1b409593cb Mon Sep 17 00:00:00 2001 From: Lukasz Wasylow Date: Fri, 4 Apr 2025 19:51:18 +0100 Subject: [PATCH 2/4] Remove debug --- .../core/annotations/ut_annotation_manager.pkb | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb index ac3eac092..198cdfe4a 100644 --- a/source/core/annotations/ut_annotation_manager.pkb +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -139,24 +139,6 @@ create or replace package body ut_annotation_manager as where x.is_annotated = 'Y' order by x.name, x.line]' using a_objects_to_refresh; - raise_application_error(-20001, q'[select /*+ no_parallel */ x.name, x.text - from (select /*+ cardinality( r ]'||l_card||q'[ )*/ - s.name, s.text, s.line, - max(case when s.text like '%--%\%%' escape '\' - and regexp_like(s.text,'^\s*--\s*]'||l_allowed_annotations||q'[%') - then 'Y' else 'N' end - ) - over(partition by s.name) is_annotated - from table(:a_objects_to_refresh) r - join ]'||l_sources_view||q'[ s - on s.name = r.object_name - and s.owner = r.object_owner - and s.type = r.object_type - where s.owner = ']'||ut_utils.qualified_sql_name(a_object_owner)||q'[' - and s.type = ']'||ut_utils.qualified_sql_name(a_object_type)||q'[' - ) x - where x.is_annotated = 'Y' - order by x.name, x.line]'); return l_result; end; From b8e9c31b4545b4f28b2536657fb47ecd598b90b3 Mon Sep 17 00:00:00 2001 From: Lukasz Wasylow Date: Thu, 10 Apr 2025 20:29:07 +0100 Subject: [PATCH 3/4] Updating a regex to treat object as annotated only if it contains valid annotations. --- .../annotations/ut_annotation_manager.pkb | 16 ++------ .../annotations/ut_annotation_manager.pks | 2 - source/core/ut_utils.pkb | 12 ++++++ source/core/ut_utils.pks | 1 + .../annotations/test_annotation_manager.pkb | 37 +++++++++++++++++++ .../annotations/test_annotation_manager.pks | 3 ++ 6 files changed, 56 insertions(+), 15 deletions(-) diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb index 198cdfe4a..36c4a2082 100644 --- a/source/core/annotations/ut_annotation_manager.pkb +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -99,32 +99,22 @@ create or replace package body ut_annotation_manager as return l_result; end; - function get_annotations_list return ut_varchar2_list pipelined is - begin - for i in 1 .. ut_utils.gc_supported_annotations.count loop - pipe row( ut_utils.gc_supported_annotations(i) ); - end loop; - end get_annotations_list; + 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_source_view_name(); l_card natural; - l_allowed_annotations varchar2(32767) := 'suite'; + l_allowed_annotations varchar2(32767) := ut_utils.get_annotations_list_regex; begin - select listagg(column_value,'|') within group (order by column_value) - into l_allowed_annotations - from table(get_annotations_list); - - l_card := ut_utils.scale_cardinality(cardinality(a_objects_to_refresh)); open l_result for q'[select /*+ no_parallel */ x.name, x.text from (select /*+ cardinality( r ]'||l_card||q'[ )*/ s.name, s.text, s.line, max(case when s.text like '%--%\%%' escape '\' - and regexp_like(s.text,'^\s*--\s*]'||l_allowed_annotations||q'[%') + and regexp_like(s.text,'^\s*--\s*%(]'||l_allowed_annotations||q'[)') then 'Y' else 'N' end ) over(partition by s.name) is_annotated diff --git a/source/core/annotations/ut_annotation_manager.pks b/source/core/annotations/ut_annotation_manager.pks index 9816b7265..20fcd810f 100644 --- a/source/core/annotations/ut_annotation_manager.pks +++ b/source/core/annotations/ut_annotation_manager.pks @@ -20,8 +20,6 @@ create or replace package ut_annotation_manager authid current_user as * Builds annotations out of database source code by reading it from cache */ - function get_annotations_list return ut_varchar2_list pipelined; - /** * 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. diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index 81d47221b..f4e248534 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -1024,5 +1024,17 @@ create or replace package body ut_utils is return l_result; end; + function get_annotations_list_regex return varchar2 is + cursor c_get_annotation_regex is + select listagg(column_value,'|') within group (order by column_value) + from table(gc_supported_annotations); + l_result varchar2(4000); + begin + open c_get_annotation_regex; + fetch c_get_annotation_regex into l_result; + close c_get_annotation_regex; + return l_result; + end get_annotations_list_regex; + end ut_utils; / diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index fb054e934..2b4b7695e 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -517,5 +517,6 @@ create or replace package ut_utils authid definer is */ function lengthb_clob( a_clob clob) return integer; + function get_annotations_list_regex return varchar2; end ut_utils; / diff --git a/test/ut3_tester/core/annotations/test_annotation_manager.pkb b/test/ut3_tester/core/annotations/test_annotation_manager.pkb index 8eb3c2f6d..e13ed1d7f 100644 --- a/test/ut3_tester/core/annotations/test_annotation_manager.pkb +++ b/test/ut3_tester/core/annotations/test_annotation_manager.pkb @@ -58,6 +58,17 @@ create or replace package body test_annotation_manager is end;]'); end; + procedure add_badly_annotated_pck is + begin + exec_autonomous(q'[ + create or replace package badly_annot_pkg as + --%sparameter(no parameter) + --%returns(no return) + --%usage(no usage) + procedure some_dummy_test_procedure; + end;]'); + end; + procedure drop_dummy_test_package is begin exec_autonomous(q'[drop package dummy_test_package]'); @@ -66,6 +77,14 @@ create or replace package body test_annotation_manager is null; end; + procedure drop_badly_ann_pkg is + begin + exec_autonomous(q'[drop package badly_annot_pkg]'); + exception + when others then + null; + end; + procedure recompile_dummy_test_package is begin exec_autonomous(q'[alter package dummy_test_package compile]'); @@ -462,5 +481,23 @@ create or replace package body test_annotation_manager is assert_dummy_test_package(l_start_date); end; +procedure issue_1278_correct_annotation is + l_actual sys_refcursor; + begin + --Arrange + add_badly_annotated_pck(); + --Act + ut3_develop.ut_annotation_manager.rebuild_annotation_cache(sys_context('USERENV', 'CURRENT_USER'),'PACKAGE'); + --Assert + open l_actual for + select * + from ut3_develop.ut_suite_cache_package + where object_owner = sys_context('USERENV', 'CURRENT_USER') and object_name = 'BADLY_ANNOT_PKG'; + + drop_badly_ann_pkg; + ut.expect(l_actual).to_be_empty; + end; + + end test_annotation_manager; / diff --git a/test/ut3_tester/core/annotations/test_annotation_manager.pks b/test/ut3_tester/core/annotations/test_annotation_manager.pks index 7207917f3..7e92d4fee 100644 --- a/test/ut3_tester/core/annotations/test_annotation_manager.pks +++ b/test/ut3_tester/core/annotations/test_annotation_manager.pks @@ -100,5 +100,8 @@ create or replace package test_annotation_manager is --%endcontext + --%test(Issue #1278 of marking object as annotated when it is not) + procedure issue_1278_correct_annotation; + end test_annotation_manager; / From 463b84bff05df5c048df4ae681392d965b4bec1c Mon Sep 17 00:00:00 2001 From: Lukasz Wasylow Date: Thu, 10 Apr 2025 20:39:48 +0100 Subject: [PATCH 4/4] Remove empty line --- source/core/annotations/ut_annotation_manager.pkb | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/core/annotations/ut_annotation_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb index 36c4a2082..f49bebe94 100644 --- a/source/core/annotations/ut_annotation_manager.pkb +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -99,8 +99,6 @@ create or replace package body ut_annotation_manager as return l_result; end; - - 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_source_view_name();