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/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/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql b/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql index bb67d374f..8d9ecad0c 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(ut_varchar2_list(USER||'.TST_PKG_HUGE')); end; / diff --git a/old_tests/RunAll.sql b/old_tests/RunAll.sql index 9c76c642a..e99dcd302 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 @@ -38,19 +37,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 @@ -284,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$; @@ -341,8 +326,12 @@ begin 'source/api/ut_runner.pks', 'source/core/coverage', 'source/core/types', - 'source/core/ut_annotations.pkb', - 'source/core/ut_annotations.pks', + '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', + '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/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/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_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 ff8c402de..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_failed_expectations(); - 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; -/ 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/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 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/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/source/core/annotations/ut_annotated_object.tps b/source/core/annotations/ut_annotated_object.tps new file mode 100644 index 000000000..a9e1ae76e --- /dev/null +++ b/source/core/annotations/ut_annotated_object.tps @@ -0,0 +1,24 @@ +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), + 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..7f01f612c --- /dev/null +++ b/source/core/annotations/ut_annotated_objects.tps @@ -0,0 +1,20 @@ +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 new file mode 100644 index 000000000..fdd489dcf --- /dev/null +++ b/source/core/annotations/ut_annotation.tps @@ -0,0 +1,24 @@ +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), + subobject_name varchar2(250) +) +/ + diff --git a/source/core/annotations/ut_annotation_cache.sql b/source/core/annotations/ut_annotation_cache.sql new file mode 100644 index 000000000..f8aff469b --- /dev/null +++ b/source/core/annotations/ut_annotation_cache.sql @@ -0,0 +1,25 @@ +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), + constraint ut_annotation_cache_pk primary key(cache_id, annotation_position), + 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..859c4a79b --- /dev/null +++ b/source/core/annotations/ut_annotation_cache_info.sql @@ -0,0 +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, + constraint ut_annotation_cache_info_pk primary key(cache_id), + 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 new file mode 100644 index 000000000..e6a81a497 --- /dev/null +++ b/source/core/annotations/ut_annotation_cache_manager.pkb @@ -0,0 +1,129 @@ +create or replace package body ut_annotation_cache_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. + */ + + 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 + 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) + 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 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) + 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; + + + procedure cleanup_cache(a_objects ut_annotation_objs_cache_info) 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 + 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 + 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_objs_cache_info) 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_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 new file mode 100644 index 000000000..eb332572d --- /dev/null +++ b/source/core/annotations/ut_annotation_cache_manager.pks @@ -0,0 +1,53 @@ +create or replace package ut_annotation_cache_manager authid definer 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. + */ + + /** + * 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); + + /** + * 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; + + /** + * 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_cache_seq.sql b/source/core/annotations/ut_annotation_cache_seq.sql new file mode 100644 index 000000000..611e97ac0 --- /dev/null +++ b/source/core/annotations/ut_annotation_cache_seq.sql @@ -0,0 +1,16 @@ +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_manager.pkb b/source/core/annotations/ut_annotation_manager.pkb new file mode 100644 index 000000000..ceb085824 --- /dev/null +++ b/source/core/annotations/ut_annotation_manager.pkb @@ -0,0 +1,217 @@ +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_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_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 + ) + 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' ]'; + open l_result for l_cursor_text using a_object_owner, a_object_type; + return l_result; + end; + + 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 + 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 '\' + ) + order by name, line]' + using a_object_type, a_object_owner, a_object_type, a_object_owner; + + 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_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; + + 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_object_lines dbms_preprocessor.source_lines_t; + pragma autonomous_transaction; + begin + 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; + + l_name := l_names(i); + l_object_lines(l_object_lines.count+1) := l_lines(i); + end loop; + 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; + + + 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'; + + --if cache is empty and there are objects to parse + if l_objects_in_cache_count = 0 and a_info_rows.count > 0 then + + 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 + ); + + --if not all in cache, get list of objects to rebuild cache for + elsif l_objects_in_cache_count < a_info_rows.count then + + select value(x)bulk collect into l_objects_to_parse from table(a_info_rows) x where x.needs_refresh = 'Y'; + + --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 + ); + end if; + + 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; + + 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 new file mode 100644 index 000000000..d78d02073 --- /dev/null +++ b/source/core/annotations/ut_annotation_manager.pks @@ -0,0 +1,54 @@ +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 + */ + + /** + * 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) 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); + + /** + * 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/source/core/annotations/ut_annotation_obj_cache_info.tps b/source/core/annotations/ut_annotation_obj_cache_info.tps new file mode 100644 index 000000000..43cf111ff --- /dev/null +++ b/source/core/annotations/ut_annotation_obj_cache_info.tps @@ -0,0 +1,23 @@ +create type ut_annotation_obj_cache_info 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) + ) +/ diff --git a/source/core/annotations/ut_annotation_objs_cache_info.tps b/source/core/annotations/ut_annotation_objs_cache_info.tps new file mode 100644 index 000000000..4a99283e4 --- /dev/null +++ b/source/core/annotations/ut_annotation_objs_cache_info.tps @@ -0,0 +1,19 @@ +create type ut_annotation_objs_cache_info 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_obj_cache_info +/ diff --git a/source/core/ut_annotations.pkb b/source/core/annotations/ut_annotation_parser.pkb similarity index 52% rename from source/core/ut_annotations.pkb rename to source/core/annotations/ut_annotation_parser.pkb index 7ee7906a6..f35e6a0a7 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 @@ -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#_$]*'; @@ -40,16 +39,18 @@ create or replace package body ut_annotations 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_params_str varchar2(32767); - l_annotation_name varchar2(1000); - l_annotation_params tt_annotation_params; - l_annotation t_annotation; - l_annotations_list tt_annotations; + 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); begin -- loop while there are unprocessed comment blocks while 0 != nvl(regexp_instr(srcstr => a_source @@ -59,62 +60,35 @@ create or replace package body ut_annotations 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'); 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_annotations_list(l_annotation_name) := l_annotation; + 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)); + + 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_list; - - end get_annotations; + end add_annotations; - function get_package_annotations(a_source clob, a_comments tt_comment_list) return tt_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); begin l_package_comments := regexp_substr(srcstr => a_source @@ -123,19 +97,16 @@ create or replace package body ut_annotations 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; - end; + if l_package_comments is not null then + add_annotations(a_annotations, l_package_comments, a_comments); + end if; + end add_package_annotations; - function get_procedure_list(a_source clob, a_comments tt_comment_list) return tt_procedure_list 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_procedure_annotations tt_procedure_annotations; - l_procedure_list tt_procedure_list; 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; @@ -167,24 +138,21 @@ create or replace package body ut_annotations 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; + 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_procedure_list; - 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; l_comment_pos pls_integer; l_comment_replacer varchar2(50); + l_source clob := a_source; begin l_comment_pos := 1; loop @@ -209,131 +177,88 @@ 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 + ------------------------------------------------------------ + --public definitions + ------------------------------------------------------------ - function parse_package_annotations(a_source clob) return typ_annotated_package is + function parse_object_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 := ut_annotations(); + l_result ut_annotations; begin 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); + add_package_annotations(l_annotations, l_source, l_comments); + add_procedure_annotations(l_annotations, l_source, l_comments); + - l_annotated_pkg.procedure_annotations := get_procedure_list(l_source, l_comments); + dbms_lob.freetemporary(l_source); + 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_annotated_pkg); + 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_object_annotations; - return l_annotated_pkg; - end parse_package_annotations; - - ------------------------------ - --public definitions - - function get_package_annotations(a_owner_name varchar2, a_name varchar2) return typ_annotated_package is - l_source clob; + 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 - - -- 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); + 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; + return l_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; - -end ut_annotations; +end ut_annotation_parser; / diff --git a/source/core/annotations/ut_annotation_parser.pks b/source/core/annotations/ut_annotation_parser.pks new file mode 100644 index 000000000..ae16d3484 --- /dev/null +++ b/source/core/annotations/ut_annotation_parser.pks @@ -0,0 +1,44 @@ +create or replace package ut_annotation_parser 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. + */ + + /** + * Parses the source passed as input parameter and returns 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_lines ordered lines of source code to be parsed + * @return array containing annotations + */ + function parse_object_annotations(a_source_lines dbms_preprocessor.source_lines_t) return ut_annotations; + + + /** + * + * @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 parse_object_annotations(a_source clob) return ut_annotations; + +end ut_annotation_parser; +/ diff --git a/source/core/annotations/ut_annotations.tps b/source/core/annotations/ut_annotations.tps new file mode 100644 index 000000000..d19f4943d --- /dev/null +++ b/source/core/annotations/ut_annotations.tps @@ -0,0 +1,19 @@ +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/types/ut_test.tpb b/source/core/types/ut_test.tpb index 99d593c89..c5e9fa07b 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 80fd12d5b..785625163 100644 --- a/source/core/types/ut_test.tps +++ b/source/core/types/ut_test.tps @@ -56,6 +56,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_annotations.pks b/source/core/ut_annotations.pks deleted file mode 100644 index 02f0ea646..000000000 --- a/source/core/ut_annotations.pks +++ /dev/null @@ -1,98 +0,0 @@ -create or replace package ut_annotations 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. - */ - - /* - package: ut_annotations - - 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_annotation is record( - text varchar2(4000), - params tt_annotation_params - ); - - /* - 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: 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_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; - -end ut_annotations; -/ 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; / 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 605be5d61..17af64d58 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -27,6 +27,13 @@ create or replace package body ut_suite_manager is 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); @@ -34,7 +41,7 @@ create or replace package body ut_suite_manager is 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'); + 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(*) @@ -45,11 +52,13 @@ create or replace package body ut_suite_manager is 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.typ_annotated_package; - l_suite_name ut_annotations.t_annotation_name; - l_test ut_test; - l_proc_annotations ut_annotations.tt_annotations; + 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 varchar2(4000); l_default_setup_proc varchar2(250 char); l_default_teardown_proc varchar2(250 char); @@ -57,136 +66,122 @@ 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 varchar2(250 char); - 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_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_annotations.get_package_annotations(a_owner_name => l_owner_name, a_name => l_object_name); - if l_annotation_data.package_annotations.exists('suite') then + l_beforetest_procedure varchar2(250 char); + l_aftertest_procedure varchar2(250 char); + l_rollback_type integer; + l_displayname varchar2(4000); - 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; + begin + l_suite_rollback := ut_utils.gc_rollback_auto; + for i in 1 .. a_object.annotations.count loop - 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; + if a_object.annotations(i).subobject_name is null then - 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; + 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 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; - else - l_suite_rollback := ut_utils.gc_rollback_auto; - end if; - 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; + elsif l_is_suite then - if l_proc_annotations.exists('beforeeach') and l_default_setup_proc is null 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 l_proc_annotations.exists('aftereach') and l_default_teardown_proc is null then + elsif a_object.annotations(i).name = '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 + elsif a_object.annotations(i).name = '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 + 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_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; + + 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_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_annotation_data.package_annotations.exists('disabled'), + 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_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; - - if l_proc_annotations.exists('aftertest') then - l_aftertest_procedure := l_proc_annotations('aftertest').text; - end if; - - if l_proc_annotations.exists('displayname') then - l_displayname := l_proc_annotations('displayname').text; - else - l_displayname := l_proc_annotations('test').text; - 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; - 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; - + 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; + end create_suite; procedure update_cache(a_owner_name varchar2, a_schema_suites tt_schema_suites, a_total_obj_cnt integer) is begin @@ -200,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, @@ -261,40 +256,42 @@ 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 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); - + q'[select value(x) + from table( + ]'||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 + 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; + --build hierarchical structure of the suite l_schema_suites.delete; -- Restructure single-dimenstion list into hierarchy of suites by the value of %suitepath attribute value @@ -480,7 +477,6 @@ create or replace package body ut_suite_manager is -- 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]" @@ -529,13 +525,10 @@ 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); - l_temp_suite := config_package(l_schema, l_package_name); - - if l_temp_suite is null 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 || ':' || l_temp_suite.path || '.' || l_procedure_name, '.'); + l_path := rtrim(l_schema || ':' || g_schema_object_path_map(l_schema)(l_package_name) || '.' || l_procedure_name, '.'); end; end if; diff --git a/source/core/ut_suite_manager.pks b/source/core/ut_suite_manager.pks index d13df91f5..112c0287e 100644 --- a/source/core/ut_suite_manager.pks +++ b/source/core/ut_suite_manager.pks @@ -16,13 +16,27 @@ 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; - - procedure config_schema(a_owner_name varchar2); - + /** + * 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 83edb74a4..aeb814fad 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 @@ -448,5 +450,16 @@ create or replace package body ut_utils is commit; end; + + function ut_owner return varchar2 is + begin + 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 fe7b73a69..0ecff8cfc 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,12 +235,12 @@ 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; @@ -272,5 +258,23 @@ create or replace package ut_utils authid definer is */ procedure read_cache_to_dbms_output; + + /** + * 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 b4fedff80..14dfbcc54 100644 --- a/source/create_synonyms_and_grants_for_public.sql +++ b/source/create_synonyms_and_grants_for_public.sql @@ -67,6 +67,18 @@ 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_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; +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_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 e7f72270f..3294898ed 100644 --- a/source/create_synonyms_and_grants_for_user.sql +++ b/source/create_synonyms_and_grants_for_user.sql @@ -87,6 +87,17 @@ 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_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; +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_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 62401a4cc..aff99338f 100644 --- a/source/install.sql +++ b/source/install.sql @@ -78,8 +78,21 @@ 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.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_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' +@@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' +@@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 ae6ac732d..c6453ee54 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -148,7 +148,29 @@ drop type ut_data_value force; drop table ut_cursor_data; -drop package ut_annotations; +drop package ut_annotation_manager; + +drop package ut_annotation_parser; + +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_annotation_objs_cache_info force; + +drop type ut_annotation_obj_cache_info force; + +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; diff --git a/test/install_tests.sql b/test/install_tests.sql index a9e1fa210..80fcad038 100644 --- a/test/install_tests.sql +++ b/test/install_tests.sql @@ -4,7 +4,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 @@ -14,10 +14,11 @@ whenever oserror exit failure rollback @@ut_reporters/test_xunit_reporter.pks @ut_expectations/test_expectations_cursor.pks @@ut_runner/test_ut_runner.pks +@@ut_annotation_manager/test_annotation_manager.pks @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 @@ -27,6 +28,7 @@ whenever oserror exit failure rollback @@ut_reporters/test_xunit_reporter.pkb @ut_expectations/test_expectations_cursor.pkb @@ut_runner/test_ut_runner.pkb +@@ut_annotation_manager/test_annotation_manager.pkb set linesize 200 set define on diff --git a/test/ut_annotation_manager/test_annotation_manager.pkb b/test/ut_annotation_manager/test_annotation_manager.pkb new file mode 100644 index 000000000..7ac1e0870 --- /dev/null +++ b/test/ut_annotation_manager/test_annotation_manager.pkb @@ -0,0 +1,256 @@ +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 + order by annotation_position; + + 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 + order by annotation_position; + + 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 + order by annotation_position; + + 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/ut_annotation_manager/test_annotation_manager.pks b/test/ut_annotation_manager/test_annotation_manager.pks new file mode 100644 index 000000000..75575db01 --- /dev/null +++ b/test/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/ut_annotation_parser/test_annotation_parser.pkb b/test/ut_annotation_parser/test_annotation_parser.pkb new file mode 100644 index 000000000..903e4f542 --- /dev/null +++ b/test/ut_annotation_parser/test_annotation_parser.pkb @@ -0,0 +1,414 @@ +create or replace package body test_annotation_parser is + + procedure test_proc_comments 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) + + -- %ann1(Name of suite) + -- wrong line + -- %ann2(some_value) + 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(5,'ann2','some_value','foo') + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + end; + + procedure ignore_floating_annotations 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) + + -- %ann1(Name of suite) + -- %ann2(all.globaltests) + + --%test + procedure foo; + + -- %ann3(Name of suite) + -- %ann4(all.globaltests) + + --%test + procedure bar; + 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( 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 parse_complex_with_functions 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; + + + --%beforeeach + procedure foo2; + + --test comment + -- wrong comment + + + /* + describtion of the procedure + */ + --%beforeeach(key=testval) + PROCEDURE foo3(a_value number default null); + + --%all + function foo4(a_val number default null + , a_par varchar2 default := ''asdf''); + 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' ), + ut3.ut_annotation( 5, 'beforeeach', null,'foo2' ), + 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 no_procedure_annotation 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) + + 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 ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + + end; + + procedure parse_accessible_by is + l_source clob; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; + + begin + l_source := 'PACKAGE test_tt accessible by (foo) AS + -- %suite + -- %displayname(Name of suite) + -- %suitepath(all.globaltests) + + 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 ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + + end; + + procedure complex_package_declaration is + l_source clob; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; + + 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_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 ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + + end; + + procedure complex_text is + l_source clob; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; + + begin + l_source := 'PACKAGE test_tt AS + -- %suite + --%displayname(name = Name of suite) + -- %suitepath(key=all.globaltests,key2=foo,"--%some text") + + 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 = Name of suite', 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 ignore_annotations_in_comments is + l_source clob; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; + + begin + l_source := 'PACKAGE test_tt AS + /* + Some comment + -- inlined + -- %ignored + */ + -- %suite + --%displayname(Name of suite) + -- %suitepath(all.globaltests) + + 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 ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + + end; + + procedure ignore_wrapped_package is + 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 + l_actual := ut3.ut_annotation_parser.parse_object_annotations(l_source); + --Assert + ut.expect(anydata.convertCollection(l_actual)).to_be_empty(); + end; + + procedure brackets_in_desc is + + l_source clob; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; + 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_object_annotations(l_source); + + --Assert + l_expected := ut3.ut_annotations( + ut3.ut_annotation( 1, 'suite', 'Name of suite (including some brackets) and some more text', null ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + end; + + procedure test_space_before_annot_params is + l_source clob; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; + + begin + l_source := 'PACKAGE test_tt AS + /* + Some comment + -- inlined + */ + -- %suite + -- %suitepath (all.globaltests) + + 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, 'suitepath', 'all.globaltests', null ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + end; + + procedure test_windows_newline + as + 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)' || chr(13) || chr(10) + || ' -- %suitepath(all.globaltests) + 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 ) + ); + + ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); + end; + + procedure test_annot_very_long_name + as + l_source clob; + l_actual ut3.ut_annotations; + l_expected ut3.ut_annotations; + begin + 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_dit_it; + 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, '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)); + end; + + +end test_annotation_parser; +/ diff --git a/test/ut_annotation_parser/test_annotation_parser.pks b/test/ut_annotation_parser/test_annotation_parser.pks new file mode 100644 index 000000000..b5ef9b5ed --- /dev/null +++ b/test/ut_annotation_parser/test_annotation_parser.pks @@ -0,0 +1,39 @@ +create or replace package test_annotation_parser is + + --%suite(ut_annotation_parser) + --%suitepath(utplsql.core) + + --%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; + + --%test(Parses package level annotations with annotation params containing brackets) + procedure brackets_in_desc; + + --%test(Parses annotation text even with spaces before brackets) + procedure test_space_before_annot_params; + + -- %test(Parses source-code with Windows-style newline) + procedure test_windows_newline; + + -- %test(Parses annotations with very long object names) + procedure test_annot_very_long_name; + +end test_annotation_parser; +/ diff --git a/test/ut_annotations/test_annotations.pkb b/test/ut_annotations/test_annotations.pkb deleted file mode 100644 index fc5db3254..000000000 --- a/test/ut_annotations/test_annotations.pkb +++ /dev/null @@ -1,579 +0,0 @@ -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); - 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; - 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_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) - 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.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 - 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; - - 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 := 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; - - check_annotation_parsing(l_expected, l_parsing_result); - - end; - - 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; - - 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.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(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(3).name := 'foo3'; - l_expected.procedure_annotations(3).annotations('beforeeach').params(1) := l_ann_param; - - check_annotation_parsing(l_expected, l_parsing_result); - - end; - - 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; - - 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.procedure_annotations(1).name := 'foo'; - l_expected.procedure_annotations(1).annotations('test').params := cast(null as ut3.ut_annotations.tt_annotation_params); - - check_annotation_parsing(l_expected, l_parsing_result); - - end; - - 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; - - 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; - - check_annotation_parsing(l_expected, l_parsing_result); - - end; - - 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; - - 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; - - check_annotation_parsing(l_expected, l_parsing_result); - - end; - - 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; - - 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 := 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; - - check_annotation_parsing(l_expected, l_parsing_result); - - end; - - 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; - - 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; - - check_annotation_parsing(l_expected, l_parsing_result); - - end; - - 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; - - 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 := 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; - - 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 - 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; - 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_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 := 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; - - 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; - l_expected ut3.ut_annotations.typ_annotated_package; - l_ann_param ut3.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 := 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; - - check_annotation_parsing(l_expected, l_parsing_result); - 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; - begin - l_source := 'PACKAGE test_tt AS - -- %suite - -- %displayname(Name of suite)' || chr(13) || chr(10) - || ' -- %suitepath(all.globaltests) - 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; - - check_annotation_parsing(l_expected, l_parsing_result); - 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; - 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 := 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.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); - - check_annotation_parsing(l_expected, l_parsing_result); - end; - - -end test_annotations; -/ diff --git a/test/ut_annotations/test_annotations.pks b/test/ut_annotations/test_annotations.pks deleted file mode 100644 index acfd442ac..000000000 --- a/test/ut_annotations/test_annotations.pks +++ /dev/null @@ -1,46 +0,0 @@ -create or replace package test_annotations is - - --%suite(annotations) - --%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) - procedure ignore_wrapped_package; - - procedure cre_wrapped_pck; - procedure drop_wrapped_pck; - - --%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; - - -- %test(Test annotations with windows newline) - procedure test_windows_newline; - - -- %test(Test annotation function with very long name) - procedure test_annot_very_long_name; - -end test_annotations; -/ 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 +/ 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; 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; / 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; /