From c787e4793de0d89ff9770320e2e863ce25142be4 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sun, 4 Mar 2018 01:34:51 +0000 Subject: [PATCH 01/28] Added support for multiple occurrences of before and after blocks. Refactored suite_builder to be ready for context. --- source/core/types/ut_executables.tps | 19 + source/core/types/ut_logical_suite.tpb | 14 +- source/core/types/ut_logical_suite.tps | 3 +- source/core/types/ut_suite.tpb | 68 +-- source/core/types/ut_suite.tps | 10 +- source/core/types/ut_suite_item.tpb | 12 +- source/core/types/ut_suite_item.tps | 5 +- source/core/types/ut_test.tpb | 114 +++-- source/core/types/ut_test.tps | 27 +- source/core/ut_suite_builder.pkb | 417 ++++++++++++------ source/core/ut_suite_builder.pks | 3 + source/core/ut_utils.pks | 38 +- source/install.sql | 1 + .../reporters/ut_documentation_reporter.tpb | 16 +- source/reporters/ut_teamcity_reporter.tpb | 36 +- source/reporters/ut_xunit_reporter.tpb | 12 +- source/uninstall.sql | 4 + test/core/test_suite_manager.pkb | 103 +++-- test/core/test_ut_suite.pkb | 127 +++--- test/core/test_ut_test.pkb | 82 ++-- 20 files changed, 681 insertions(+), 430 deletions(-) create mode 100644 source/core/types/ut_executables.tps diff --git a/source/core/types/ut_executables.tps b/source/core/types/ut_executables.tps new file mode 100644 index 000000000..f74d3f7ca --- /dev/null +++ b/source/core/types/ut_executables.tps @@ -0,0 +1,19 @@ +create or replace type ut_executables as + /* + utPLSQL - Version 3 + 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_executable +/ diff --git a/source/core/types/ut_logical_suite.tpb b/source/core/types/ut_logical_suite.tpb index 6ad0ab0ac..f289f5010 100644 --- a/source/core/types/ut_logical_suite.tpb +++ b/source/core/types/ut_logical_suite.tpb @@ -17,11 +17,13 @@ create or replace type body ut_logical_suite as */ constructor function ut_logical_suite( - self in out nocopy ut_logical_suite,a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_description varchar2 := null, a_path varchar2 + self in out nocopy ut_logical_suite,a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_path varchar2 ) return self as result is begin self.self_type := $$plsql_unit; - self.init(a_object_owner, a_object_name, a_name, a_description, a_path, ut_utils.gc_rollback_auto, false); + self.init(a_object_owner, a_object_name, a_name); + self.path := a_path; + self.disabled_flag := ut_utils.boolean_to_int(false); self.items := ut_suite_items(); return; end; @@ -49,6 +51,14 @@ create or replace type body ut_logical_suite as self.calc_execution_result(); end; + overriding member procedure set_default_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer) is + begin + self.rollback_type := coalesce(self.rollback_type, a_rollback_type); + for i in 1 .. self.items.count loop + self.items(i).set_default_rollback_type(a_rollback_type); + end loop; + end; + overriding member function do_execute(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base) return boolean is l_suite_savepoint varchar2(30); l_item_savepoint varchar2(30); diff --git a/source/core/types/ut_logical_suite.tps b/source/core/types/ut_logical_suite.tps index f26e68856..37b5d3ba3 100644 --- a/source/core/types/ut_logical_suite.tps +++ b/source/core/types/ut_logical_suite.tps @@ -22,11 +22,12 @@ create or replace type ut_logical_suite under ut_suite_item ( items ut_suite_items, constructor function ut_logical_suite( - self in out nocopy ut_logical_suite,a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_description varchar2 := null, a_path varchar2 + self in out nocopy ut_logical_suite, a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_path varchar2 ) return self as result, member function is_valid(self in out nocopy ut_logical_suite) return boolean, member procedure add_item(self in out nocopy ut_logical_suite, a_item ut_suite_item), overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base), + overriding member procedure set_default_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer), overriding member function do_execute(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_logical_suite), overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2), diff --git a/source/core/types/ut_suite.tpb b/source/core/types/ut_suite.tpb index ab9d7a94e..1a264eef1 100644 --- a/source/core/types/ut_suite.tpb +++ b/source/core/types/ut_suite.tpb @@ -16,32 +16,31 @@ create or replace type body ut_suite as limitations under the License. */ - constructor function ut_suite ( - self in out nocopy ut_suite , a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_path varchar2, a_description varchar2 := null, - a_rollback_type integer := null, a_disabled_flag boolean := false, a_before_all_proc_name varchar2 := null, - a_after_all_proc_name varchar2 := null - ) return self as result is + constructor function ut_suite (self in out nocopy ut_suite , a_object_owner varchar2, a_object_name varchar2) return self as result is begin self.self_type := $$plsql_unit; - self.init(a_object_owner, a_object_name, a_name, a_description, a_path, a_rollback_type, a_disabled_flag); - self.before_all := ut_executable(self, a_before_all_proc_name, ut_utils.gc_before_all); + self.init(a_object_owner, a_object_name, a_object_name); self.items := ut_suite_items(); - self.after_all := ut_executable(self, a_after_all_proc_name, ut_utils.gc_after_all); + before_all_list := ut_executables(); + after_all_list := ut_executables(); return; end; overriding member function is_valid(self in out nocopy ut_suite) return boolean is - l_is_valid boolean; + l_is_valid boolean := true; begin - l_is_valid := - ( not self.before_all.is_defined() or self.before_all.is_valid() ) and - ( not self.after_all.is_defined() or self.after_all.is_valid() ); + for i in 1 .. before_all_list.count loop + l_is_valid := self.before_all_list(i).is_valid() and l_is_valid; + end loop; + for i in 1 .. after_all_list.count loop + l_is_valid := self.after_all_list(i).is_valid() and l_is_valid; + end loop; return l_is_valid; end; overriding member function do_execute(self in out nocopy ut_suite, a_listener in out nocopy ut_event_listener_base) return boolean is l_suite_savepoint varchar2(30); - l_suite_step_without_errors boolean; + l_no_errors boolean; procedure propagate_error(a_error_stack_trace varchar2) is begin @@ -64,20 +63,27 @@ create or replace type body ut_suite as l_suite_savepoint := self.create_savepoint_if_needed(); --includes listener calls for before and after actions - l_suite_step_without_errors := self.before_all.do_execute(self, a_listener); - - if l_suite_step_without_errors then + l_no_errors := true; + for i in 1 .. self.before_all_list.count loop + l_no_errors := self.before_all_list(i).do_execute(self, a_listener); + if not l_no_errors then + propagate_error(self.before_all_list(i).get_error_stack_trace()); + exit; + end if; + end loop; + + if l_no_errors then for i in 1 .. self.items.count loop self.items(i).do_execute(a_listener); end loop; - else - propagate_error(self.before_all.get_error_stack_trace()); end if; - l_suite_step_without_errors := self.after_all.do_execute(self, a_listener); - if not l_suite_step_without_errors then - self.put_warning(self.after_all.get_error_stack_trace()); - end if; + for i in 1 .. after_all_list.count loop + l_no_errors := self.after_all_list(i).do_execute(self, a_listener); + if not l_no_errors then + self.put_warning(self.after_all_list(i).get_error_stack_trace()); + end if; + end loop; self.rollback_to_savepoint(l_suite_savepoint); @@ -91,22 +97,30 @@ create or replace type body ut_suite as ut_utils.set_action(null); - return l_suite_step_without_errors; + return l_no_errors; end; overriding member function get_error_stack_traces(self ut_suite) return ut_varchar2_list is l_stack_traces ut_varchar2_list := ut_varchar2_list(); begin - ut_utils.append_to_list(l_stack_traces, self.before_all.get_error_stack_trace()); - ut_utils.append_to_list(l_stack_traces, self.after_all.get_error_stack_trace()); + for i in 1 .. before_all_list.count loop + ut_utils.append_to_list(l_stack_traces, self.before_all_list(i).get_error_stack_trace()); + end loop; + for i in 1 .. after_all_list.count loop + ut_utils.append_to_list(l_stack_traces, self.after_all_list(i).get_error_stack_trace()); + end loop; return l_stack_traces; end; overriding member function get_serveroutputs return clob is l_outputs clob; begin - ut_utils.append_to_clob(l_outputs, self.before_all.serveroutput ); - ut_utils.append_to_clob(l_outputs, self.after_all.serveroutput ); + for i in 1 .. before_all_list.count loop + ut_utils.append_to_clob(l_outputs, self.before_all_list(i).serveroutput); + end loop; + for i in 1 .. after_all_list.count loop + ut_utils.append_to_clob(l_outputs, self.after_all_list(i).serveroutput); + end loop; return l_outputs; end; diff --git a/source/core/types/ut_suite.tps b/source/core/types/ut_suite.tps index 37995add9..3b5e77415 100644 --- a/source/core/types/ut_suite.tps +++ b/source/core/types/ut_suite.tps @@ -19,18 +19,14 @@ create or replace type ut_suite under ut_logical_suite ( * The procedure to be invoked before all of the items of the suite (executed once) * Procedure exists within the package of the suite */ - before_all ut_executable, + before_all_list ut_executables, /** * The procedure to be invoked after all of the items of the suite (executed once) * Procedure exists within the package of the suite */ - after_all ut_executable, - constructor function ut_suite ( - self in out nocopy ut_suite , a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_path varchar2, a_description varchar2 := null, - a_rollback_type integer := null, a_disabled_flag boolean := false, a_before_all_proc_name varchar2 := null, - a_after_all_proc_name varchar2 := null - ) return self as result, + after_all_list ut_executables, + constructor function ut_suite (self in out nocopy ut_suite , a_object_owner varchar2, a_object_name varchar2) return self as result, overriding member function is_valid(self in out nocopy ut_suite) return boolean, overriding member function do_execute(self in out nocopy ut_suite , a_listener in out nocopy ut_event_listener_base) return boolean, overriding member function get_error_stack_traces(self ut_suite) return ut_varchar2_list, diff --git a/source/core/types/ut_suite_item.tpb b/source/core/types/ut_suite_item.tpb index 2ddce3abc..1bfc4adff 100644 --- a/source/core/types/ut_suite_item.tpb +++ b/source/core/types/ut_suite_item.tpb @@ -16,18 +16,15 @@ create or replace type body ut_suite_item as limitations under the License. */ - member procedure init(self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2, a_description varchar2, a_path varchar2, a_rollback_type integer, a_disabled_flag boolean) is + member procedure init(self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2) is begin self.object_owner := a_object_owner; self.object_name := lower(trim(a_object_name)); self.name := lower(trim(a_name)); - self.description := a_description; - self.path := nvl(lower(trim(a_path)), self.object_name); - self.rollback_type := a_rollback_type; - self.disabled_flag := ut_utils.boolean_to_int(a_disabled_flag); self.results_count := ut_results_counter(); self.warnings := ut_varchar2_list(); self.transaction_invalidators := ut_varchar2_list(); + self.disabled_flag := ut_utils.boolean_to_int(false); end; member procedure set_disabled_flag(self in out nocopy ut_suite_item, a_disabled_flag boolean) is @@ -40,6 +37,11 @@ create or replace type body ut_suite_item as return ut_utils.int_to_boolean(self.disabled_flag); end; + member procedure set_default_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer) is + begin + self.rollback_type := coalesce(self.rollback_type, a_rollback_type); + end; + final member procedure do_execute(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base) is l_completed_without_errors boolean; begin diff --git a/source/core/types/ut_suite_item.tps b/source/core/types/ut_suite_item.tps index 7ebeb9c5c..35bee4d25 100644 --- a/source/core/types/ut_suite_item.tps +++ b/source/core/types/ut_suite_item.tps @@ -18,12 +18,11 @@ create or replace type ut_suite_item force under ut_suite_item_base ( results_count ut_results_counter, transaction_invalidators ut_varchar2_list, - member procedure init( - self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2, - a_description varchar2, a_path varchar2, a_rollback_type integer, a_disabled_flag boolean), + member procedure init(self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2), member procedure set_disabled_flag(self in out nocopy ut_suite_item, a_disabled_flag boolean), member function get_disabled_flag return boolean, not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base), + member procedure set_default_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer), member function create_savepoint_if_needed return varchar2, member procedure rollback_to_savepoint(self in out nocopy ut_suite_item, a_savepoint varchar2), member function get_transaction_invalidators return ut_varchar2_list, diff --git a/source/core/types/ut_test.tpb b/source/core/types/ut_test.tpb index 2acfe261b..0016230be 100644 --- a/source/core/types/ut_test.tpb +++ b/source/core/types/ut_test.tpb @@ -17,45 +17,38 @@ create or replace type body ut_test as */ constructor function ut_test( - self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_description varchar2 := null, - a_path varchar2 := null, a_rollback_type integer := null, a_disabled_flag boolean := false, - a_before_each_proc_name varchar2 := null, a_before_test_proc_name varchar2 := null, - a_after_test_proc_name varchar2 := null, a_after_each_proc_name varchar2 := null, + self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_expected_error_codes ut_integer_list := null ) return self as result is begin self.self_type := $$plsql_unit; - self.init(a_object_owner, a_object_name, a_name, a_description, a_path, a_rollback_type, a_disabled_flag); - self.before_each := ut_executable(self, a_before_each_proc_name, ut_utils.gc_before_each); - self.before_test := ut_executable(self, a_before_test_proc_name, ut_utils.gc_before_test); + self.init(a_object_owner, a_object_name, a_name); self.item := ut_executable_test(self, a_name, ut_utils.gc_test_execute); - self.after_test := ut_executable(self, a_after_test_proc_name, ut_utils.gc_after_test); - self.after_each := ut_executable(self, a_after_each_proc_name, ut_utils.gc_after_each); - self.all_expectations := ut_expectation_results(); - self.failed_expectations := ut_expectation_results(); + self.before_each_list := ut_executables(); + self.before_test_list := ut_executables(); + self.after_test_list := ut_executables(); + self.after_each_list := ut_executables(); + self.all_expectations := ut_expectation_results(); + self.failed_expectations := ut_expectation_results(); self.expected_error_codes := a_expected_error_codes; 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; + l_is_valid boolean := true; begin - l_is_valid := - ( not self.before_each.is_defined() or self.before_each.is_valid() ) and - ( not self.before_test.is_defined() or self.before_test.is_valid() ) and - ( self.item.is_valid() ) and - ( not self.after_test.is_defined() or self.after_test.is_valid() ) and - ( not self.after_each.is_defined() or self.after_each.is_valid() ); + for i in 1 .. before_each_list.count loop + l_is_valid := self.before_each_list(i).is_valid() and l_is_valid; + end loop; + for i in 1 .. after_each_list.count loop + l_is_valid := self.after_each_list(i).is_valid() and l_is_valid; + end loop; + for i in 1 .. before_test_list.count loop + l_is_valid := self.before_test_list(i).is_valid() and l_is_valid; + end loop; + for i in 1 .. after_test_list.count loop + l_is_valid := self.after_test_list(i).is_valid() and l_is_valid; + end loop; return l_is_valid; end; @@ -71,8 +64,8 @@ create or replace type body ut_test as end; overriding member function do_execute(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base) return boolean is - l_completed_without_errors boolean; - l_savepoint varchar2(30); + l_no_errors boolean; + l_savepoint varchar2(30); begin ut_utils.debug_log('ut_test.execute'); @@ -87,21 +80,32 @@ create or replace type body ut_test as l_savepoint := self.create_savepoint_if_needed(); --includes listener calls for before and after actions - l_completed_without_errors := self.before_each.do_execute(self, a_listener); - - if l_completed_without_errors then - l_completed_without_errors := self.before_test.do_execute(self, a_listener); - - if l_completed_without_errors then + l_no_errors := true; + for i in 1 .. self.before_each_list.count loop + l_no_errors := self.before_each_list(i).do_execute(self, a_listener); + exit when not l_no_errors; + end loop; + + if l_no_errors then + for i in 1 .. self.before_test_list.count loop + l_no_errors := self.before_test_list(i).do_execute(self, a_listener); + exit when not l_no_errors; + end loop; + + if l_no_errors then -- execute the test self.item.do_execute(self, a_listener, self.expected_error_codes); end if; -- perform cleanup regardless of the test or setup failure - self.after_test.do_execute(self, a_listener); + for i in 1 .. self.after_test_list.count loop + self.after_test_list(i).do_execute(self, a_listener); + end loop; end if; - self.after_each.do_execute(self, a_listener); + for i in 1 .. self.after_each_list.count loop + self.after_each_list(i).do_execute(self, a_listener); + end loop; self.rollback_to_savepoint(l_savepoint); end if; @@ -109,7 +113,7 @@ create or replace type body ut_test as self.end_time := current_timestamp; a_listener.fire_after_event(ut_utils.gc_test,self); end if; - return l_completed_without_errors; + return l_no_errors; end; overriding member procedure calc_execution_result(self in out nocopy ut_test) is @@ -145,21 +149,37 @@ create or replace type body ut_test as l_stack_traces ut_varchar2_list := ut_varchar2_list(); begin ut_utils.append_to_list(l_stack_traces, self.parent_error_stack_trace); - ut_utils.append_to_list(l_stack_traces, self.before_each.get_error_stack_trace()); - ut_utils.append_to_list(l_stack_traces, self.before_test.get_error_stack_trace()); + for i in 1 .. before_each_list.count loop + ut_utils.append_to_list(l_stack_traces, self.before_each_list(i).get_error_stack_trace()); + end loop; + for i in 1 .. before_test_list.count loop + ut_utils.append_to_list(l_stack_traces, self.before_test_list(i).get_error_stack_trace()); + end loop; ut_utils.append_to_list(l_stack_traces, self.item.get_error_stack_trace()); - ut_utils.append_to_list(l_stack_traces, self.after_test.get_error_stack_trace()); - ut_utils.append_to_list(l_stack_traces, self.after_each.get_error_stack_trace()); + for i in 1 .. after_test_list.count loop + ut_utils.append_to_list(l_stack_traces, self.after_test_list(i).get_error_stack_trace()); + end loop; + for i in 1 .. after_each_list.count loop + ut_utils.append_to_list(l_stack_traces, self.after_each_list(i).get_error_stack_trace()); + end loop; return l_stack_traces; end; overriding member function get_serveroutputs return clob is l_outputs clob; begin - ut_utils.append_to_clob(l_outputs, self.before_each.serveroutput ); - ut_utils.append_to_clob(l_outputs, self.before_test.serveroutput ); + for i in 1 .. before_each_list.count loop + ut_utils.append_to_clob(l_outputs, self.before_each_list(i).serveroutput); + end loop; + for i in 1 .. before_test_list.count loop + ut_utils.append_to_clob(l_outputs, self.before_test_list(i).serveroutput); + end loop; ut_utils.append_to_clob(l_outputs, self.item.serveroutput ); - ut_utils.append_to_clob(l_outputs, self.after_test.serveroutput ); - ut_utils.append_to_clob(l_outputs, self.after_each.serveroutput ); + for i in 1 .. after_test_list.count loop + ut_utils.append_to_clob(l_outputs, self.after_test_list(i).serveroutput); + end loop; + for i in 1 .. after_each_list.count loop + ut_utils.append_to_clob(l_outputs, self.after_each_list(i).serveroutput); + end loop; return l_outputs; end; end; diff --git a/source/core/types/ut_test.tps b/source/core/types/ut_test.tps index c295f524a..b9074ba34 100644 --- a/source/core/types/ut_test.tps +++ b/source/core/types/ut_test.tps @@ -16,29 +16,25 @@ create or replace type ut_test under ut_suite_item ( limitations under the License. */ /* - * The procedure to be invoked before invoking the test and before_test procedure. - * Procedure exists within the same package as the test itself and is similar for all tests in the suite + * Procedures to be invoked before invoking the test and before_test_list procedures. */ - before_each ut_executable, + before_each_list ut_executables, /** - * The procedure to be invoked before invoking the test - * Procedure exists within the same package as the test itself + * Procedures to be invoked before invoking the test */ - before_test ut_executable, + before_test_list ut_executables, /** * The Test procedure to be executed */ item ut_executable_test, /** - * The procedure to be invoked after invoking the test - * Procedure exists within the same package as the test itself + * Procedures to be invoked after invoking the test */ - after_test ut_executable, + after_test_list ut_executables, /* - * The procedure to be invoked after invoking the test and after_test procedure. - * Procedure exists within the same package as the test itself and is similar for all tests in the suite + * Procedures to be invoked after invoking the test and after_test_list procedures. */ - after_each ut_executable, + after_each_list ut_executables, /** * The list of all expectations results as well as database errors encountered while invoking * the test procedure and the before_test/after_test blocks @@ -60,15 +56,10 @@ create or replace type ut_test under ut_suite_item ( */ expected_error_codes ut_integer_list, constructor function ut_test( - self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_description varchar2 := null, - a_path varchar2 := null, a_rollback_type integer := null, a_disabled_flag boolean := false, - a_before_each_proc_name varchar2 := null, a_before_test_proc_name varchar2 := null, - a_after_test_proc_name varchar2 := null, a_after_each_proc_name varchar2 := null, + self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_expected_error_codes ut_integer_list := 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 procedure mark_as_skipped(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base), 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), diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index 9414845ea..eb34eed82 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -16,157 +16,314 @@ create or replace package body ut_suite_builder is limitations under the License. */ - ------------------ - - 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); + subtype t_annotation_text is varchar2(4000); + subtype t_annotation_name is varchar2(4000); + subtype t_procedure_name is varchar2(500); + subtype t_annotation_position is binary_integer; + + type tt_annotations is table of t_annotation_text index by t_annotation_name; + + type t_package_annotation is record( + text varchar2(4000), + name varchar2(4000), + procedure_name varchar2(500), + procedure_annotations tt_annotations + ); + + type tt_package_annotations is table of t_package_annotation index by t_annotation_position; + type t_package_annotations_info is record( + owner t_procedure_name, + name t_procedure_name, + annotations tt_package_annotations + ); + + type tt_positions is table of boolean index by t_annotation_position; + type tt_annotations_index is table of tt_positions index by t_annotation_name; + + function is_last_annotation_for_proc(a_annotations ut_annotations, a_index binary_integer) return boolean is + begin + return a_index = a_annotations.count or a_annotations(a_index).subobject_name != nvl(a_annotations(a_index+1).subobject_name, ' '); + end; - l_default_setup_proc varchar2(250 char); - l_default_teardown_proc varchar2(250 char); - l_suite_setup_proc varchar2(250 char); - l_suite_teardown_proc varchar2(250 char); - l_suite_path varchar2(4000 char); + function get_procedure_annotations(a_annotations ut_annotations, a_index binary_integer) return tt_annotations is + l_result tt_annotations; + i binary_integer := a_index; + begin + loop + l_result(a_annotations(i).name) := a_annotations(i).text; + exit when is_last_annotation_for_proc(a_annotations, i); + i := a_annotations.next(i); + end loop; + return l_result; + end; - l_proc_name varchar2(250 char); + function convert_object_annotations(a_object ut_annotated_object) return t_package_annotations_info is + l_result t_package_annotations_info; + l_annotation_no binary_integer; + begin + l_result.owner := a_object.object_owner; + l_result.name := a_object.object_name; + l_annotation_no := a_object.annotations.first; + while l_annotation_no is not null loop + if a_object.annotations(l_annotation_no).subobject_name is null then + l_result.annotations(l_annotation_no).name := a_object.annotations(l_annotation_no).name; + l_result.annotations(l_annotation_no).text := a_object.annotations(l_annotation_no).text; + else + l_result.annotations(l_annotation_no).procedure_name := a_object.annotations(l_annotation_no).subobject_name; + l_result.annotations(l_annotation_no).procedure_annotations := get_procedure_annotations(a_object.annotations, l_annotation_no); + l_annotation_no := l_annotation_no + l_result.annotations(l_annotation_no).procedure_annotations.count - 1; + end if; + l_annotation_no := a_object.annotations.next(l_annotation_no); + end loop; + return l_result; + end; - l_suite ut_logical_suite; - l_test ut_test; + function build_annotation_index(a_annotations tt_package_annotations) return tt_annotations_index is + l_result tt_annotations_index; + i binary_integer; + begin + i := a_annotations.first; + while i is not null loop + if a_annotations(i).name is not null then + l_result(a_annotations(i).name)(i) := true; + end if; + i := a_annotations.next(i); + end loop; + return l_result; + end; - l_suite_rollback integer; + procedure delete_from_annotation_index( + a_index in out nocopy tt_annotations_index, + a_start_pos t_annotation_position, + a_end_pos t_annotation_position + ) is + i t_annotation_name; + begin + i := a_index.first; + while i is not null loop + a_index(i).delete(a_start_pos, a_end_pos); + if a_index(i).count = 0 then + a_index.delete(i); + end if; + i := a_index.next(i); + end loop; + end; - l_beforetest_procedure varchar2(250 char); - l_aftertest_procedure varchar2(250 char); + function get_rollback_type(a_rollback_type_name varchar2) return ut_utils.t_rollback_type is + l_rollback_type ut_utils.t_rollback_type; + begin + l_rollback_type := + case lower(a_rollback_type_name) + when 'manual' then ut_utils.gc_rollback_manual + when 'auto' then ut_utils.gc_rollback_auto + --TODO - if invalid or no rollback type text specified - give a warning + else ut_utils.gc_rollback_auto + end; + return l_rollback_type; + end; - l_expected_error_codes ut_integer_list; + function build_exception_numbers_list(a_annotation_text in varchar2) return ut_integer_list is + l_throws_list ut_varchar2_list; + l_exception_number_list ut_integer_list := ut_integer_list(); + l_regexp_for_excep_nums varchar2(30) := '^-?[[:digit:]]{1,5}$'; + begin + /*the a_expected_error_codes is converted to a ut_varchar2_list after that is trimmed and filtered to left only valid exception numbers*/ + l_throws_list := ut_utils.string_to_table(a_annotation_text, ',', 'Y'); + l_throws_list := ut_utils.filter_list( ut_utils.trim_list_elements(l_throws_list), l_regexp_for_excep_nums); + l_exception_number_list.extend(l_throws_list.count); + for i in 1 .. l_throws_list.count loop + l_exception_number_list(i) := l_throws_list(i); + end loop; + return l_exception_number_list; + end; - l_rollback_type integer; - l_displayname varchar2(4000); - function is_last_annotation_for_proc(a_annotations ut_annotations, a_index binary_integer) return boolean is + procedure add_to_list( + a_executables in out nocopy ut_executables, + a_procedure_name varchar2, + a_event_name ut_utils.t_event_name, + a_suite_item ut_suite_item + ) is begin - return a_index = a_annotations.count or a_annotations(a_index).subobject_name != nvl(a_annotations(a_index+1).subobject_name, ' '); - end; + if a_executables is null then + a_executables := ut_executables(); + end if; + a_executables.extend; + a_executables(a_executables.last) := ut_executable(a_suite_item, a_procedure_name, a_event_name); + end; - function build_exception_numbers_list(a_annotation_text in varchar2) return ut_integer_list is - l_throws_list ut_varchar2_list; - l_exception_number_list ut_integer_list := ut_integer_list(); - l_regexp_for_excep_nums varchar2(30) := '^-?[[:digit:]]{1,5}$'; - begin - /*the a_expected_error_codes is converted to a ut_varchar2_list after that is trimmed and filtered to left only valid exception numbers*/ - l_throws_list := ut_utils.string_to_table(a_annotation_text, ',', 'Y'); - l_throws_list := ut_utils.filter_list( ut_utils.trim_list_elements(l_throws_list), l_regexp_for_excep_nums); - l_exception_number_list.extend(l_throws_list.count); - for i in 1 .. l_throws_list.count loop - l_exception_number_list(i) := l_throws_list(i); - end loop; - return l_exception_number_list; - end; - begin - l_suite_rollback := ut_utils.gc_rollback_auto; - for i in 1 .. a_object.annotations.count loop - - if a_object.annotations(i).subobject_name is null then - - if a_object.annotations(i).name in ('suite','displayname') then - l_suite_name := a_object.annotations(i).text; - if a_object.annotations(i).name = 'suite' then - l_is_suite := true; - end if; - elsif 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; + procedure warning_on_extra_annotations( + a_suite in out nocopy ut_suite_item, + a_procedure_info t_package_annotation, + a_for_annotation varchar2 + ) is + l_annotation_name t_annotation_name; + l_warning varchar2(32767); + begin + l_annotation_name := a_procedure_info.procedure_annotations.first; + while l_annotation_name is not null loop + l_annotation_name := a_procedure_info.procedure_annotations.next(l_annotation_name); + if l_annotation_name != a_for_annotation then + l_warning := l_warning ||'"--%'|| l_annotation_name || '", '; + end if; + end loop; + if l_warning is not null then + a_suite.put_warning( + 'Annotations: '||rtrim(l_warning,', ')||' were ignored for procedure "'||a_procedure_info.procedure_name||'".' || + ' Those annotations cannot be used with annotation:"'||a_for_annotation||'"'); + end if; + end; + + procedure add_test( + a_suite in out nocopy ut_suite, + a_procedure_name varchar2, + a_annotations tt_annotations + ) is + l_test ut_test; + begin + l_test := ut_test(a_suite.object_owner, a_suite.object_name, a_procedure_name); + + if a_annotations.exists('displayname') then + l_test.description := a_annotations('displayname'); + end if; + l_test.description := coalesce(l_test.description,a_annotations('test')); + l_test.path := a_suite.path ||'.'||a_procedure_name; + + if a_annotations.exists('rollback') then + l_test.rollback_type := get_rollback_type(a_annotations('rollback')); + end if; + + if a_annotations.exists('beforetest') then + add_to_list( l_test.before_test_list, a_annotations('beforetest'), ut_utils.gc_before_test, l_test ); + end if; + if a_annotations.exists('aftertest') then + add_to_list( l_test.after_test_list, a_annotations('aftertest'), ut_utils.gc_after_test, l_test ); + end if; + if a_annotations.exists('throws') then + l_test.expected_error_codes := build_exception_numbers_list(a_annotations('throws')); + end if; + l_test.disabled_flag := ut_utils.boolean_to_int(a_annotations.exists('disabled')); + + a_suite.add_item(l_test); + end; + + procedure update_before_after_list( + a_suite in out nocopy ut_logical_suite, + a_before_each_list ut_executables, + a_after_each_list ut_executables + ) is + l_test ut_test; + begin + for i in 1 .. a_suite.items.count loop + if a_suite.items(i) is of (ut_test) then + l_test := treat( a_suite.items(i) as ut_test); + l_test.before_each_list := a_before_each_list; + l_test.after_each_list := a_after_each_list; + a_suite.items(i) := l_test; + end if; + end loop; + end; + + procedure add_procedures_from_annot( + l_annotations tt_package_annotations, + l_suite in out nocopy ut_suite, + l_before_each_list out ut_executables, + l_after_each_list out ut_executables + ) is + l_position t_annotation_position; + begin + l_before_each_list := ut_executables(); + l_after_each_list := ut_executables(); + l_position := l_annotations.first; + while l_position is not null loop + if l_annotations(l_position).procedure_name is not null then + if l_annotations(l_position).procedure_annotations.exists('beforeeach') then + add_to_list( l_before_each_list, l_annotations(l_position).procedure_name, ut_utils.gc_before_each, l_suite ); + warning_on_extra_annotations(l_suite, l_annotations(l_position), 'beforeeach'); + elsif l_annotations(l_position).procedure_annotations.exists('aftereach') then + add_to_list( l_after_each_list, l_annotations(l_position).procedure_name, ut_utils.gc_after_each, l_suite ); + warning_on_extra_annotations(l_suite, l_annotations(l_position), 'aftereach'); + elsif l_annotations(l_position).procedure_annotations.exists('beforeall') then + add_to_list( l_suite.before_all_list, l_annotations(l_position).procedure_name, ut_utils.gc_before_all, l_suite ); + warning_on_extra_annotations(l_suite, l_annotations(l_position), 'beforeall'); + elsif l_annotations(l_position).procedure_annotations.exists('afterall') then + add_to_list( l_suite.after_all_list, l_annotations(l_position).procedure_name, ut_utils.gc_after_all, l_suite ); + warning_on_extra_annotations(l_suite, l_annotations(l_position), 'afterall'); + elsif l_annotations(l_position).procedure_annotations.exists('test') then + add_test( l_suite, l_annotations(l_position).procedure_name, l_annotations(l_position).procedure_annotations); end if; + end if; + l_position := l_annotations.next(l_position); + end loop; + end; - elsif l_is_suite then - l_proc_name := a_object.annotations(i).subobject_name; - - if a_object.annotations(i).name = 'beforeeach' and l_default_setup_proc is null then - l_default_setup_proc := l_proc_name; - elsif a_object.annotations(i).name = 'aftereach' and l_default_teardown_proc is null then - l_default_teardown_proc := l_proc_name; - elsif a_object.annotations(i).name = 'beforeall' and l_suite_setup_proc is null then - l_suite_setup_proc := l_proc_name; - elsif a_object.annotations(i).name = 'afterall' and l_suite_teardown_proc is null then - l_suite_teardown_proc := l_proc_name; - elsif a_object.annotations(i).name = 'disabled' then - l_test_disabled := true; - elsif a_object.annotations(i).name = 'beforetest' then - l_beforetest_procedure := a_object.annotations(i).text; - elsif a_object.annotations(i).name = 'aftertest' then - l_aftertest_procedure := a_object.annotations(i).text; - elsif a_object.annotations(i).name = 'throws' then - l_expected_error_codes := build_exception_numbers_list(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; + function build_suite(a_package t_package_annotations_info) return ut_logical_suite is + l_package_ann_index tt_annotations_index; + l_annotations tt_package_annotations; + l_suite ut_suite; + l_before_each_list ut_executables; + l_after_each_list ut_executables; + l_rollback_type ut_utils.t_rollback_type; + l_annotation_text varchar2(32767); + begin + l_annotations := a_package.annotations; + l_package_ann_index := build_annotation_index(l_annotations); + if l_package_ann_index.exists('suite') then + --create an incomplete suite + l_suite := ut_suite(a_package.owner, a_package.name); + + l_suite.description := l_annotations(l_package_ann_index('suite').first).text; + --TODO - check that there is only one `suite` annotation defined - if not -> warning + --if l_package_ann_index('suite').count > 1 then ... end if; + + if l_package_ann_index.exists('context') then + if l_package_ann_index.exists('endcontext') then +-- add_suite_context( +-- l_suite, +-- l_package_ann_index('context').first, +-- l_package_ann_index('endcontext').first +-- ); + l_annotations.delete(l_package_ann_index('context').first, l_package_ann_index('endcontext').first); + delete_from_annotation_index(l_package_ann_index, l_package_ann_index('context').first, l_package_ann_index('endcontext').first); + -- else TODO - add warning about context without endcontext end if; + end if; - if l_is_test and is_last_annotation_for_proc(a_object.annotations, i) 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_test_disabled - ,a_before_test_proc_name => l_beforetest_procedure - ,a_after_test_proc_name => l_aftertest_procedure - ,a_expected_error_codes => l_expected_error_codes); - - l_is_test := false; - l_test_disabled := false; - l_aftertest_procedure := null; - l_beforetest_procedure := null; - l_rollback_type := null; - l_expected_error_codes := null; + if l_package_ann_index.exists('suitepath') then + --TODO - check that there is only one `suitepath` annotation defined - if not -> warning + --TODO - check that the `suitepath` annotation has text in it - if not -> warning + --TODO - check that text of `suitepath` annotation is of valid format - if not -> warning + l_annotation_text := l_annotations(l_package_ann_index('suitepath').first).text; + if l_annotation_text is not null then + l_suite.path := trim(l_annotations(l_package_ann_index('suitepath').first).text)||'.'||a_package.name; end if; + end if; + l_suite.path := lower(coalesce(l_suite.path, a_package.name)); + if l_package_ann_index.exists('displayname') then + --TODO - check that there is only one `displayname` annotation defined - if not -> warning + l_suite.description := l_annotations(l_package_ann_index('displayname').first).text; end if; - end loop; - if l_is_suite then - l_suite := ut_suite ( - a_object_owner => a_object.object_owner, - a_object_name => a_object.object_name, - a_name => a_object.object_name, --this could be different for sub-suite (context) - a_path => l_suite_path, --a patch for this suite (excluding the package name of current suite) - a_description => l_suite_name, - a_rollback_type => l_suite_rollback, - a_disabled_flag => l_suite_disabled, - a_before_all_proc_name => l_suite_setup_proc, - a_after_all_proc_name => l_suite_teardown_proc - ); - 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; + if l_package_ann_index.exists('rollback') then + l_rollback_type := get_rollback_type(l_annotations(l_package_ann_index('rollback').first).text); + end if; + + l_suite.disabled_flag := ut_utils.boolean_to_int(l_package_ann_index.exists('disabled')); + + --process procedure annotations for suite + add_procedures_from_annot(l_annotations, l_suite, l_before_each_list, l_after_each_list); + + l_suite.set_default_rollback_type(coalesce(l_rollback_type, ut_utils.gc_rollback_auto)); end if; + update_before_after_list(l_suite, l_before_each_list, l_after_each_list); + return l_suite; + end; + function create_suite(a_object ut_annotated_object) return ut_logical_suite is + begin + return build_suite(convert_object_annotations(a_object)); end create_suite; function build_suites_hierarchy(a_suites_by_path tt_schema_suites) return tt_schema_suites is diff --git a/source/core/ut_suite_builder.pks b/source/core/ut_suite_builder.pks index 4de69a584..43ea29c85 100644 --- a/source/core/ut_suite_builder.pks +++ b/source/core/ut_suite_builder.pks @@ -20,7 +20,10 @@ create or replace package ut_suite_builder authid current_user is * Responsible for converting annotations into unit test suites */ + --table of ut_suites indexed by object name ( suite_paths('object_name') = ut_logical_suite type tt_schema_suites is table of ut_logical_suite index by varchar2(4000 char); + + --table of suite paths indexed by object name ( suite_paths('object_name') = 'suitepath.to.object' type t_object_suite_path is table of varchar2(4000) index by varchar2(4000 char); type t_schema_suites_info is record ( diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index fe8586ba0..7afd3b932 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -24,23 +24,25 @@ create or replace package ut_utils authid definer is gc_version constant varchar2(50) := 'v3.1.0.1688-develop'; /* Constants: Event names */ - gc_run constant varchar2(12) := 'run'; - gc_suite constant varchar2(12) := 'suite'; - gc_before_all constant varchar2(12) := 'before_all'; - gc_before_each constant varchar2(12) := 'before_each'; - gc_before_test constant varchar2(12) := 'before_test'; - gc_test constant varchar2(12) := 'test'; - gc_test_execute constant varchar2(12) := 'test_execute'; - gc_after_test constant varchar2(10) := 'after_test'; - gc_after_each constant varchar2(12) := 'after_each'; - gc_after_all constant varchar2(12) := 'after_all'; - gc_finalize constant varchar2(12) := 'finalize'; + subtype t_event_name is varchar2(12); + gc_run constant t_event_name := 'run'; + gc_suite constant t_event_name := 'suite'; + gc_before_all constant t_event_name := 'before_all'; + gc_before_each constant t_event_name := 'before_each'; + gc_before_test constant t_event_name := 'before_test'; + gc_test constant t_event_name := 'test'; + gc_test_execute constant t_event_name := 'test_execute'; + gc_after_test constant t_event_name := 'after_test'; + gc_after_each constant t_event_name := 'after_each'; + gc_after_all constant t_event_name := 'after_all'; + gc_finalize constant t_event_name := 'finalize'; /* Constants: Test Results */ - tr_disabled constant number(1) := 0; -- test/suite was disabled - tr_success constant number(1) := 1; -- test passed - tr_failure constant number(1) := 2; -- one or more expectations failed - tr_error constant number(1) := 3; -- exception was raised + subtype t_test_result is binary_integer range 0 .. 3; + tr_disabled constant t_test_result := 0; -- test/suite was disabled + tr_success constant t_test_result := 1; -- test passed + tr_failure constant t_test_result := 2; -- one or more expectations failed + tr_error constant t_test_result := 3; -- exception was raised tr_disabled_char constant varchar2(8) := 'Disabled'; -- test/suite was disabled tr_success_char constant varchar2(7) := 'Success'; -- test passed @@ -50,9 +52,9 @@ create or replace package ut_utils authid definer is /* Constants: Rollback type for ut_test_object */ - gc_rollback_auto constant number(1) := 0; -- rollback after each test and suite - gc_rollback_manual constant number(1) := 1; -- leave transaction control manual - --gc_rollback_on_error constant number(1) := 2; -- rollback tests only on error + subtype t_rollback_type is binary_integer range 0 .. 1; + gc_rollback_auto constant t_rollback_type := 0; -- rollback after each test and suite + gc_rollback_manual constant t_rollback_type := 1; -- leave transaction control manual ex_unsupported_rollback_type exception; gc_unsupported_rollback_type constant pls_integer := -20200; diff --git a/source/install.sql b/source/install.sql index 69be9317c..4bc6a907e 100644 --- a/source/install.sql +++ b/source/install.sql @@ -59,6 +59,7 @@ alter session set current_schema = &&ut3_owner; @@install_component.sql 'core/types/ut_suite_item.tps' @@install_component.sql 'core/types/ut_suite_items.tps' @@install_component.sql 'core/types/ut_executable.tps' +@@install_component.sql 'core/types/ut_executables.tps' @@install_component.sql 'core/types/ut_executable_test.tps' @@install_component.sql 'core/types/ut_test.tps' @@install_component.sql 'core/types/ut_logical_suite.tps' diff --git a/source/reporters/ut_documentation_reporter.tpb b/source/reporters/ut_documentation_reporter.tpb index d5420c8f3..96545dd53 100644 --- a/source/reporters/ut_documentation_reporter.tpb +++ b/source/reporters/ut_documentation_reporter.tpb @@ -66,13 +66,25 @@ create or replace type body ut_documentation_reporter is end; overriding member procedure after_calling_before_all(self in out nocopy ut_documentation_reporter, a_suite in ut_logical_suite) is + l_list ut_executables; begin - self.print_clob(treat(a_suite as ut_suite).before_all.serveroutput); + l_list := treat(a_suite as ut_suite).before_all_list; + for i in 1 .. l_list.count loop + if l_list(i).serveroutput is not null and l_list(i).serveroutput != empty_clob() then + self.print_clob(l_list(i).serveroutput); + end if; + end loop; end; overriding member procedure after_calling_after_all(self in out nocopy ut_documentation_reporter, a_suite in ut_logical_suite) is + l_list ut_executables; begin - self.print_clob(treat(a_suite as ut_suite).after_all.serveroutput); + l_list := treat(a_suite as ut_suite).after_all_list; + for i in 1 .. l_list.count loop + if l_list(i).serveroutput is not null and l_list(i).serveroutput != empty_clob() then + self.print_clob(l_list(i).serveroutput); + end if; + end loop; end; overriding member procedure after_calling_suite(self in out nocopy ut_documentation_reporter, a_suite ut_logical_suite) as diff --git a/source/reporters/ut_teamcity_reporter.tpb b/source/reporters/ut_teamcity_reporter.tpb index f19626de9..1c1c05b79 100644 --- a/source/reporters/ut_teamcity_reporter.tpb +++ b/source/reporters/ut_teamcity_reporter.tpb @@ -70,25 +70,33 @@ create or replace type body ut_teamcity_reporter is self.print_clob(a_test.get_serveroutputs()); if a_test.result = ut_utils.tr_error then - if a_test.before_each.error_backtrace is not null then - l_std_err_msg := l_std_err_msg || 'Before each exception:' || chr(10) || a_test.before_each.error_backtrace || chr(10); - end if; - - if a_test.before_test.error_backtrace is not null then - l_std_err_msg := l_std_err_msg || 'Before test exception:' || chr(10) || a_test.before_test.error_backtrace || chr(10); - end if; + for i in 1 .. a_test.before_each_list.count loop + if a_test.before_each_list(i).error_backtrace is not null then + l_std_err_msg := l_std_err_msg || 'Before each exception:' || chr(10) || a_test.before_each_list(i).error_backtrace || chr(10); + end if; + end loop; + + for i in 1 .. a_test.before_test_list.count loop + if a_test.before_test_list(i).error_backtrace is not null then + l_std_err_msg := l_std_err_msg || 'Before test exception:' || chr(10) || a_test.before_test_list(i).error_backtrace || chr(10); + end if; + end loop; if a_test.item.error_backtrace is not null then l_std_err_msg := l_std_err_msg || 'Test exception:' || chr(10) || a_test.item.error_backtrace || chr(10); end if; - if a_test.after_test.error_backtrace is not null then - l_std_err_msg := l_std_err_msg || 'After test exception:' || chr(10) || a_test.after_test.error_backtrace || chr(10); - end if; - - if a_test.after_each.error_backtrace is not null then - l_std_err_msg := l_std_err_msg || 'After each exception:' || chr(10) || a_test.after_each.error_backtrace || chr(10); - end if; + for i in 1 .. a_test.after_test_list.count loop + if a_test.after_test_list(i).error_backtrace is not null then + l_std_err_msg := l_std_err_msg || 'After test exception:' || chr(10) || a_test.after_test_list(i).error_backtrace || chr(10); + end if; + end loop; + + for i in 1 .. a_test.after_each_list.count loop + if a_test.after_each_list(i).error_backtrace is not null then + l_std_err_msg := l_std_err_msg || 'After each exception:' || chr(10) || a_test.after_each_list(i).error_backtrace || chr(10); + end if; + end loop; self.print_text(ut_teamcity_reporter_helper.test_std_err(a_test_name => l_test_full_name ,a_out => trim(l_std_err_msg))); diff --git a/source/reporters/ut_xunit_reporter.tpb b/source/reporters/ut_xunit_reporter.tpb index f50180bba..047f8346b 100644 --- a/source/reporters/ut_xunit_reporter.tpb +++ b/source/reporters/ut_xunit_reporter.tpb @@ -78,6 +78,8 @@ create or replace type body ut_xunit_reporter is l_tests_count integer := a_suite.results_count.disabled_count + a_suite.results_count.success_count + a_suite.results_count.failure_count + a_suite.results_count.errored_count; l_suite ut_suite; + l_data clob; + l_errors ut_varchar2_list; begin a_suite_id := a_suite_id + 1; self.print_text(' self.owner_name, a_object => self.object_name, a_procedure_name => self.procedure_name); - - l_statement := - 'declare' || chr(10) || - ' l_error_stack varchar2(32767);' || chr(10) || - ' l_error_backtrace varchar2(32767);' || chr(10) || - 'begin' || chr(10) || - ' begin' || chr(10) || - ' ' || ut_metadata.form_name(self.owner_name, self.object_name, self.procedure_name) || ';' || chr(10) || - ' exception' || chr(10) || - ' when others then ' || chr(10) || - ' l_error_stack := dbms_utility.format_error_stack;' || chr(10) || - ' l_error_backtrace := dbms_utility.format_error_backtrace;' || chr(10) || - ' --raise on ORA-04068, ORA-04061: existing state of packages has been discarded to avoid unrecoverable session exception' || chr(10) || - ' end;' || chr(10) || - ' :a_error_stack := l_error_stack;' || chr(10) || - ' :a_error_backtrace := l_error_backtrace;' || chr(10) || - 'end;'; - - ut_utils.debug_log('ut_executable.do_execute l_statement: ' || l_statement); - - l_cursor_number := dbms_sql.open_cursor; - dbms_sql.parse(l_cursor_number, statement => l_statement, language_flag => dbms_sql.native); - dbms_sql.bind_variable(l_cursor_number, 'a_error_stack', to_char(null), 32767); - dbms_sql.bind_variable(l_cursor_number, 'a_error_backtrace', to_char(null), 32767); - - l_status := dbms_sql.execute(l_cursor_number); - dbms_sql.variable_value(l_cursor_number, 'a_error_stack', self.error_stack); - dbms_sql.variable_value(l_cursor_number, 'a_error_backtrace', self.error_backtrace); - dbms_sql.close_cursor(l_cursor_number); - - save_dbms_output; - - l_completed_without_errors := (self.error_stack||self.error_backtrace) is null; - if self.error_stack like '%ORA-04068%' or self.error_stack like '%ORA-04061%' then - ut_expectation_processor.set_invalidation_exception(); - end if; - --listener - after call to executable - a_listener.fire_after_event(self.associated_event_name, a_item); + ut_event_manager.trigger_event('before_'||self.associated_event_name, self); + + begin + ut_metadata.do_resolve(a_owner => self.owner_name, a_object => self.object_name, a_procedure_name => self.procedure_name); + exception + when others then + self.error_stack := dbms_utility.format_error_stack; + self.error_backtrace := dbms_utility.format_error_backtrace; + l_completed_without_errors := false; + end; + if l_completed_without_errors then + l_statement := + 'declare' || chr(10) || + ' l_error_stack varchar2(32767);' || chr(10) || + ' l_error_backtrace varchar2(32767);' || chr(10) || + 'begin' || chr(10) || + ' begin' || chr(10) || + ' ' || ut_metadata.form_name(self.owner_name, self.object_name, self.procedure_name) || ';' || chr(10) || + ' exception' || chr(10) || + ' when others then ' || chr(10) || + ' l_error_stack := dbms_utility.format_error_stack;' || chr(10) || + ' l_error_backtrace := dbms_utility.format_error_backtrace;' || chr(10) || + ' --raise on ORA-04068, ORA-04061: existing state of packages has been discarded to avoid unrecoverable session exception' || chr(10) || + ' end;' || chr(10) || + ' :a_error_stack := l_error_stack;' || chr(10) || + ' :a_error_backtrace := l_error_backtrace;' || chr(10) || + 'end;'; + + ut_utils.debug_log('ut_executable.do_execute l_statement: ' || l_statement); + + l_cursor_number := dbms_sql.open_cursor; + dbms_sql.parse(l_cursor_number, statement => l_statement, language_flag => dbms_sql.native); + dbms_sql.bind_variable(l_cursor_number, 'a_error_stack', to_char(null), 32767); + dbms_sql.bind_variable(l_cursor_number, 'a_error_backtrace', to_char(null), 32767); + + l_status := dbms_sql.execute(l_cursor_number); + dbms_sql.variable_value(l_cursor_number, 'a_error_stack', self.error_stack); + dbms_sql.variable_value(l_cursor_number, 'a_error_backtrace', self.error_backtrace); + dbms_sql.close_cursor(l_cursor_number); + + save_dbms_output; + + l_completed_without_errors := (self.error_stack||self.error_backtrace) is null; + if self.error_stack like '%ORA-04068%' or self.error_stack like '%ORA-04061%' then + ut_expectation_processor.set_invalidation_exception(); + end if; + --listener - after call to executable + ut_event_manager.trigger_event('after_'||self.associated_event_name, self); - l_end_transaction_id := dbms_transaction.local_transaction_id(); - if l_start_transaction_id != l_end_transaction_id or l_end_transaction_id is null then - a_item.add_transaction_invalidator(self.form_name()); + l_end_transaction_id := dbms_transaction.local_transaction_id(); + if l_start_transaction_id != l_end_transaction_id or l_end_transaction_id is null then + a_item.add_transaction_invalidator(self.form_name()); + end if; + ut_utils.set_client_info(null); end if; - ut_utils.set_client_info(null); end if; return l_completed_without_errors; diff --git a/source/core/types/ut_executable.tps b/source/core/types/ut_executable.tps index b7b97510b..71278dedc 100644 --- a/source/core/types/ut_executable.tps +++ b/source/core/types/ut_executable.tps @@ -1,4 +1,4 @@ -create or replace type ut_executable authid current_user as object( +create or replace type ut_executable under ut_event_item( /* utPLSQL - Version 3 Copyright 2016 - 2017 utPLSQL Project @@ -29,13 +29,13 @@ create or replace type ut_executable authid current_user as object( member function is_valid(self in out nocopy ut_executable) return boolean, member function is_defined return boolean, member function form_name return varchar2, - member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base), + member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item), /** * executes the defines executable * returns true if executed without exceptions * returns false if exceptions were raised */ - member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base) return boolean, + member function do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item) return boolean, member function get_error_stack_trace return varchar2 ) not final / diff --git a/source/core/types/ut_executable_test.tpb b/source/core/types/ut_executable_test.tpb index 6be12b653..53b98157a 100644 --- a/source/core/types/ut_executable_test.tpb +++ b/source/core/types/ut_executable_test.tpb @@ -12,17 +12,17 @@ create or replace type body ut_executable_test as end; member procedure do_execute( - self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_listener in out nocopy ut_event_listener_base, a_expected_error_codes in ut_integer_list + self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, + a_expected_error_codes in ut_integer_list ) is l_completed_without_errors boolean; begin - l_completed_without_errors := self.do_execute(a_item, a_listener, a_expected_error_codes); + l_completed_without_errors := self.do_execute(a_item, a_expected_error_codes); end do_execute; member function do_execute( - self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_listener in out nocopy ut_event_listener_base, a_expected_error_codes in ut_integer_list + self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, + a_expected_error_codes in ut_integer_list ) return boolean is l_expected_except_message varchar2(4000); @@ -53,7 +53,7 @@ create or replace type body ut_executable_test as end; begin --Create a ut_executable object and call do_execute after that get the data to know the test's execution result - self.do_execute(a_item, a_listener); + self.do_execute(a_item); if a_expected_error_codes is not null and a_expected_error_codes is not empty then l_expected_except_message := failed_expec_errnum_message(a_expected_error_codes); diff --git a/source/core/types/ut_executable_test.tps b/source/core/types/ut_executable_test.tps index c5502b636..c4c865db7 100644 --- a/source/core/types/ut_executable_test.tps +++ b/source/core/types/ut_executable_test.tps @@ -22,12 +22,12 @@ create or replace type ut_executable_test authid current_user under ut_executabl member procedure do_execute( self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_listener in out nocopy ut_event_listener_base, a_expected_error_codes in ut_integer_list + a_expected_error_codes in ut_integer_list ), member function do_execute( self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item, - a_listener in out nocopy ut_event_listener_base, a_expected_error_codes in ut_integer_list + a_expected_error_codes in ut_integer_list ) return boolean ) final; diff --git a/source/core/types/ut_logical_suite.tpb b/source/core/types/ut_logical_suite.tpb index f289f5010..2824db10d 100644 --- a/source/core/types/ut_logical_suite.tpb +++ b/source/core/types/ut_logical_suite.tpb @@ -39,15 +39,15 @@ create or replace type body ut_logical_suite as self.items(self.items.last) := a_item; end; - overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base) is + overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite) is begin - a_listener.fire_before_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_event_manager.before_suite, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop - self.items(i).mark_as_skipped(a_listener); + self.items(i).mark_as_skipped(); end loop; self.end_time := self.start_time; - a_listener.fire_after_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_event_manager.after_suite, self); self.calc_execution_result(); end; @@ -59,25 +59,25 @@ create or replace type body ut_logical_suite as end loop; end; - overriding member function do_execute(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base) return boolean is + overriding member function do_execute(self in out nocopy ut_logical_suite) return boolean is l_suite_savepoint varchar2(30); l_item_savepoint varchar2(30); l_completed_without_errors boolean; begin ut_utils.debug_log('ut_logical_suite.execute'); - a_listener.fire_before_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_event_manager.before_suite, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop -- execute the item (test or suite) - self.items(i).do_execute(a_listener); + self.items(i).do_execute(); end loop; self.calc_execution_result(); self.end_time := current_timestamp; - a_listener.fire_after_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_event_manager.after_suite, self); return l_completed_without_errors; end; @@ -98,18 +98,18 @@ create or replace type body ut_logical_suite as self.result := l_result; end; - overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2) is + overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_error_stack_trace varchar2) is begin ut_utils.debug_log('ut_logical_suite.fail'); - a_listener.fire_before_event(ut_utils.gc_suite, self); + ut_event_manager.trigger_event(ut_event_manager.before_suite, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop -- execute the item (test or suite) - self.items(i).mark_as_errored(a_listener, a_error_stack_trace); + self.items(i).mark_as_errored(a_error_stack_trace); end loop; self.calc_execution_result(); self.end_time := self.start_time; - a_listener.fire_after_event(ut_utils.gc_suite, self); + ut_event_manager.trigger_event(ut_event_manager.after_suite, self); end; overriding member function get_error_stack_traces return ut_varchar2_list is diff --git a/source/core/types/ut_logical_suite.tps b/source/core/types/ut_logical_suite.tps index 37b5d3ba3..f30a6f4a3 100644 --- a/source/core/types/ut_logical_suite.tps +++ b/source/core/types/ut_logical_suite.tps @@ -26,11 +26,11 @@ create or replace type ut_logical_suite under ut_suite_item ( ) return self as result, member function is_valid(self in out nocopy ut_logical_suite) return boolean, member procedure add_item(self in out nocopy ut_logical_suite, a_item ut_suite_item), - overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base), + overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite), overriding member procedure set_default_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer), - overriding member function do_execute(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base) return boolean, + overriding member function do_execute(self in out nocopy ut_logical_suite) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_logical_suite), - overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2), + overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_error_stack_trace varchar2), overriding member function get_error_stack_traces return ut_varchar2_list, overriding member function get_serveroutputs return clob, overriding member function get_transaction_invalidators return ut_varchar2_list diff --git a/source/core/types/ut_output_reporter_base.tpb b/source/core/types/ut_output_reporter_base.tpb index c65d6c879..d83f72792 100644 --- a/source/core/types/ut_output_reporter_base.tpb +++ b/source/core/types/ut_output_reporter_base.tpb @@ -74,7 +74,7 @@ create or replace type body ut_output_reporter_base is end if; end; - overriding final member procedure finalize(self in out nocopy ut_output_reporter_base) is + overriding final member procedure on_finalize(self in out nocopy ut_output_reporter_base, a_run in ut_run) is begin self.output_buffer.close(); end; diff --git a/source/core/types/ut_output_reporter_base.tps b/source/core/types/ut_output_reporter_base.tps index 8684bf78a..776711fad 100644 --- a/source/core/types/ut_output_reporter_base.tps +++ b/source/core/types/ut_output_reporter_base.tps @@ -28,7 +28,7 @@ create or replace type ut_output_reporter_base under ut_reporter_base( final member function get_lines(a_initial_timeout natural := null, a_timeout_sec natural := null) return ut_varchar2_rows pipelined, final member function get_lines_cursor(a_initial_timeout natural := null, a_timeout_sec natural := null) return sys_refcursor, final member procedure lines_to_dbms_output(self in ut_output_reporter_base, a_initial_timeout natural := null, a_timeout_sec natural := null), - overriding final member procedure finalize(self in out nocopy ut_output_reporter_base) + overriding final member procedure on_finalize(self in out nocopy ut_output_reporter_base, a_run in ut_run) ) not final not instantiable / diff --git a/source/core/types/ut_reporter_base.tpb b/source/core/types/ut_reporter_base.tpb index c5a48996a..9cf85e01c 100644 --- a/source/core/types/ut_reporter_base.tpb +++ b/source/core/types/ut_reporter_base.tpb @@ -50,20 +50,20 @@ create or replace type body ut_reporter_base is null; end; - member procedure before_calling_before_all(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite) is + member procedure before_calling_before_all(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_before_all (self in out nocopy ut_reporter_base, a_suite in ut_logical_suite) is + member procedure after_calling_before_all (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure before_calling_before_each(self in out nocopy ut_reporter_base, a_suite in ut_test) is + member procedure before_calling_before_each(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_before_each (self in out nocopy ut_reporter_base, a_suite in ut_test) is + member procedure after_calling_before_each (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; @@ -74,11 +74,11 @@ create or replace type body ut_reporter_base is null; end; - member procedure before_calling_before_test(self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure before_calling_before_test(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_before_test (self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure after_calling_before_test (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; @@ -92,11 +92,11 @@ create or replace type body ut_reporter_base is null; end; - member procedure before_calling_after_test(self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure before_calling_after_test(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_after_test (self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure after_calling_after_test (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; @@ -107,20 +107,20 @@ create or replace type body ut_reporter_base is end; --suite hooks continued - member procedure before_calling_after_each(self in out nocopy ut_reporter_base, a_suite in ut_test) is + member procedure before_calling_after_each(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_after_each (self in out nocopy ut_reporter_base, a_suite in ut_test) is + member procedure after_calling_after_each (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure before_calling_after_all(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite) is + member procedure before_calling_after_all(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_after_all (self in out nocopy ut_reporter_base, a_suite in ut_logical_suite) is + member procedure after_calling_after_all (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; @@ -136,5 +136,65 @@ create or replace type body ut_reporter_base is null; end; + overriding final member function get_supported_events return ut_varchar2_list is + l_events_list ut_varchar2_list; + begin + select lower(replace(procedure_name,'CALLING_')) + bulk collect into l_events_list + from user_procedures + where object_name = upper(self_type) + and (procedure_name like 'BEFORE_%' or procedure_name like 'AFTER_%'); + l_events_list.extend; + l_events_list(l_events_list.last) := 'on_finalize'; + return l_events_list; + end; + + overriding final member procedure on_event( self in out nocopy ut_reporter_base, a_event_name varchar2, a_event_item ut_event_item) is + begin + if a_event_name = ut_event_manager.before_run then + self.before_calling_run(treat(a_event_item as ut_run)); + elsif a_event_name = ut_event_manager.before_suite then + self.before_calling_suite(treat(a_event_item as ut_logical_suite)); + elsif a_event_name = ut_event_manager.before_before_all then + self.before_calling_before_all(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.before_before_each then + self.before_calling_before_each(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.before_test then + self.before_calling_test(treat(a_event_item as ut_test)); + elsif a_event_name = ut_event_manager.before_before_test then + self.before_calling_before_test(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.before_test_execute then + self.before_calling_test_execute(treat(a_event_item as ut_test)); + elsif a_event_name = ut_event_manager.before_after_test then + self.before_calling_after_test(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.before_after_each then + self.before_calling_after_each(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.before_after_all then + self.before_calling_after_all(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.after_run then + self.after_calling_run(treat(a_event_item as ut_run)); + elsif a_event_name = ut_event_manager.after_suite then + self.after_calling_suite(treat(a_event_item as ut_logical_suite)); + elsif a_event_name = ut_event_manager.after_before_all then + self.after_calling_before_all(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.after_before_each then + self.after_calling_before_each(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.after_test then + self.after_calling_test(treat(a_event_item as ut_test)); + elsif a_event_name = ut_event_manager.after_before_test then + self.after_calling_before_test(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.after_test_execute then + self.after_calling_test_execute(treat(a_event_item as ut_test)); + elsif a_event_name = ut_event_manager.after_after_test then + self.after_calling_after_test(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.after_after_each then + self.after_calling_after_each(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.after_after_all then + self.after_calling_after_all(treat(a_event_item as ut_executable)); + elsif a_event_name = ut_event_manager.on_finalize then + self.on_finalize(treat(a_event_item as ut_run)); + end if; + end; + end; / diff --git a/source/core/types/ut_reporter_base.tps b/source/core/types/ut_reporter_base.tps index a1ee6be25..87e0aba0d 100644 --- a/source/core/types/ut_reporter_base.tps +++ b/source/core/types/ut_reporter_base.tps @@ -1,4 +1,4 @@ -create or replace type ut_reporter_base authid current_user as object( +create or replace type ut_reporter_base under ut_event_listener ( /* utPLSQL - Version 3 Copyright 2016 - 2017 utPLSQL Project @@ -15,7 +15,6 @@ create or replace type ut_reporter_base authid current_user as object( See the License for the specific language governing permissions and limitations under the License. */ - self_type varchar2(250), id raw(32), final member procedure init(self in out nocopy ut_reporter_base, a_self_type varchar2), member procedure set_reporter_id(self in out nocopy ut_reporter_base, a_reporter_id raw), @@ -28,32 +27,32 @@ create or replace type ut_reporter_base authid current_user as object( -- suite hooks member procedure before_calling_suite(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), - member procedure before_calling_before_all(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), - member procedure after_calling_before_all (self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), + member procedure before_calling_before_all(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_before_all (self in out nocopy ut_reporter_base, a_executable in ut_executable), - member procedure before_calling_before_each(self in out nocopy ut_reporter_base, a_suite in ut_test), - member procedure after_calling_before_each (self in out nocopy ut_reporter_base, a_suite in ut_test), + member procedure before_calling_before_each(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_before_each (self in out nocopy ut_reporter_base, a_executable in ut_executable), -- test hooks member procedure before_calling_test(self in out nocopy ut_reporter_base, a_test in ut_test), - member procedure before_calling_before_test(self in out nocopy ut_reporter_base, a_test in ut_test), - member procedure after_calling_before_test (self in out nocopy ut_reporter_base, a_test in ut_test), + member procedure before_calling_before_test(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_before_test (self in out nocopy ut_reporter_base, a_executable in ut_executable), member procedure before_calling_test_execute(self in out nocopy ut_reporter_base, a_test in ut_test), member procedure after_calling_test_execute (self in out nocopy ut_reporter_base, a_test in ut_test), - member procedure before_calling_after_test(self in out nocopy ut_reporter_base, a_test in ut_test), - member procedure after_calling_after_test (self in out nocopy ut_reporter_base, a_test in ut_test), + member procedure before_calling_after_test(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_after_test (self in out nocopy ut_reporter_base, a_executable in ut_executable), member procedure after_calling_test(self in out nocopy ut_reporter_base, a_test in ut_test), --suite hooks continued - member procedure before_calling_after_each(self in out nocopy ut_reporter_base, a_suite in ut_test), - member procedure after_calling_after_each (self in out nocopy ut_reporter_base, a_suite in ut_test), + member procedure before_calling_after_each(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_after_each (self in out nocopy ut_reporter_base, a_executable in ut_executable), - member procedure before_calling_after_all(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), - member procedure after_calling_after_all (self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), + member procedure before_calling_after_all(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_after_all (self in out nocopy ut_reporter_base, a_executable in ut_executable), member procedure after_calling_suite(self in out nocopy ut_reporter_base, a_suite in ut_logical_suite), @@ -63,7 +62,17 @@ create or replace type ut_reporter_base authid current_user as object( -- This method is executed when reporter is getting finalized -- it differs from after_calling_run, as it is getting called, even when the run fails -- This way, you may close all open outputs, files, connections etc. that need closing before the run finishes - not instantiable member procedure finalize(self in out nocopy ut_reporter_base) + not instantiable member procedure on_finalize(self in out nocopy ut_reporter_base, a_run in ut_run), + + /** + * Returns the list of events that are supported by particular implementation of the reporter + */ + overriding final member function get_supported_events return ut_varchar2_list, + + /** + * Delegates execution of event into individual reporting procedures + */ + overriding final member procedure on_event( self in out nocopy ut_reporter_base, a_event_name varchar2, a_event_item ut_event_item) ) not final not instantiable diff --git a/source/core/types/ut_run.tpb b/source/core/types/ut_run.tpb index dac651bb1..928a8220d 100644 --- a/source/core/types/ut_run.tpb +++ b/source/core/types/ut_run.tpb @@ -47,32 +47,32 @@ create or replace type body ut_run as return; end; - overriding member procedure mark_as_skipped(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base) is + overriding member procedure mark_as_skipped(self in out nocopy ut_run) is begin null; end; - overriding member function do_execute(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base) return boolean is + overriding member function do_execute(self in out nocopy ut_run) return boolean is l_completed_without_errors boolean; begin ut_utils.debug_log('ut_run.execute'); - a_listener.fire_before_event(ut_utils.gc_run, self); + ut_event_manager.trigger_event(ut_event_manager.before_run, self); self.start_time := current_timestamp; -- clear anything that might stay in the session's cache ut_expectation_processor.clear_expectations; for i in 1 .. self.items.count loop - l_completed_without_errors := self.items(i).do_execute(a_listener); + l_completed_without_errors := self.items(i).do_execute(); end loop; self.calc_execution_result(); self.end_time := current_timestamp; - a_listener.fire_after_event(ut_utils.gc_run, self); - a_listener.fire_on_event(ut_utils.gc_finalize); + ut_event_manager.trigger_event(ut_event_manager.after_run, self); + ut_event_manager.trigger_event(ut_event_manager.on_finalize, self); return l_completed_without_errors; end; @@ -93,21 +93,21 @@ create or replace type body ut_run as self.result := l_result; end; - overriding member procedure mark_as_errored(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2) is + overriding member procedure mark_as_errored(self in out nocopy ut_run, a_error_stack_trace varchar2) is begin ut_utils.debug_log('ut_run.fail'); - a_listener.fire_before_event(ut_utils.gc_run, self); + ut_event_manager.trigger_event(ut_event_manager.before_run, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop - self.items(i).mark_as_errored(a_listener, a_error_stack_trace); + self.items(i).mark_as_errored(a_error_stack_trace); end loop; self.calc_execution_result(); self.end_time := self.start_time; - a_listener.fire_after_event(ut_utils.gc_run, self); + ut_event_manager.trigger_event(ut_event_manager.after_run, self); end; member function get_run_schemes return ut_varchar2_rows is diff --git a/source/core/types/ut_run.tps b/source/core/types/ut_run.tps index d072b545a..0db9ad375 100644 --- a/source/core/types/ut_run.tps +++ b/source/core/types/ut_run.tps @@ -33,10 +33,10 @@ create or replace type ut_run under ut_suite_item ( a_project_file_mappings ut_file_mappings := null, a_test_file_mappings ut_file_mappings := null ) return self as result, - overriding member procedure mark_as_skipped(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base), - overriding member function do_execute(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base) return boolean, + overriding member procedure mark_as_skipped(self in out nocopy ut_run), + overriding member function do_execute(self in out nocopy ut_run) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_run), - overriding member procedure mark_as_errored(self in out nocopy ut_run, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2), + overriding member procedure mark_as_errored(self in out nocopy ut_run, a_error_stack_trace varchar2), member function get_run_schemes return ut_varchar2_rows, overriding member function get_error_stack_traces return ut_varchar2_list, overriding member function get_serveroutputs return clob diff --git a/source/core/types/ut_suite.tpb b/source/core/types/ut_suite.tpb index 1a264eef1..aa17d7983 100644 --- a/source/core/types/ut_suite.tpb +++ b/source/core/types/ut_suite.tpb @@ -38,14 +38,14 @@ create or replace type body ut_suite as return l_is_valid; end; - overriding member function do_execute(self in out nocopy ut_suite, a_listener in out nocopy ut_event_listener_base) return boolean is + overriding member function do_execute(self in out nocopy ut_suite) return boolean is l_suite_savepoint varchar2(30); l_no_errors boolean; procedure propagate_error(a_error_stack_trace varchar2) is begin for i in 1..self.items.count loop - self.items(i).mark_as_errored(a_listener, a_error_stack_trace); + self.items(i).mark_as_errored(a_error_stack_trace); end loop; end; begin @@ -54,9 +54,9 @@ create or replace type body ut_suite as ut_utils.set_action(self.object_name); if self.get_disabled_flag() then - self.mark_as_skipped(a_listener); + self.mark_as_skipped(); else - a_listener.fire_before_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_event_manager.before_suite, self); self.start_time := current_timestamp; if self.is_valid() then @@ -65,7 +65,7 @@ create or replace type body ut_suite as --includes listener calls for before and after actions l_no_errors := true; for i in 1 .. self.before_all_list.count loop - l_no_errors := self.before_all_list(i).do_execute(self, a_listener); + l_no_errors := self.before_all_list(i).do_execute(self); if not l_no_errors then propagate_error(self.before_all_list(i).get_error_stack_trace()); exit; @@ -74,12 +74,12 @@ create or replace type body ut_suite as if l_no_errors then for i in 1 .. self.items.count loop - self.items(i).do_execute(a_listener); + self.items(i).do_execute(); end loop; end if; for i in 1 .. after_all_list.count loop - l_no_errors := self.after_all_list(i).do_execute(self, a_listener); + l_no_errors := self.after_all_list(i).do_execute(self); if not l_no_errors then self.put_warning(self.after_all_list(i).get_error_stack_trace()); end if; @@ -92,7 +92,7 @@ create or replace type body ut_suite as end if; self.calc_execution_result(); self.end_time := current_timestamp; - a_listener.fire_after_event(ut_utils.gc_suite,self); + ut_event_manager.trigger_event(ut_event_manager.after_suite, self); end if; ut_utils.set_action(null); diff --git a/source/core/types/ut_suite.tps b/source/core/types/ut_suite.tps index 3b5e77415..68de8e29c 100644 --- a/source/core/types/ut_suite.tps +++ b/source/core/types/ut_suite.tps @@ -28,7 +28,7 @@ create or replace type ut_suite under ut_logical_suite ( after_all_list ut_executables, constructor function ut_suite (self in out nocopy ut_suite , a_object_owner varchar2, a_object_name varchar2) return self as result, overriding member function is_valid(self in out nocopy ut_suite) return boolean, - overriding member function do_execute(self in out nocopy ut_suite , a_listener in out nocopy ut_event_listener_base) return boolean, + overriding member function do_execute(self in out nocopy ut_suite) return boolean, overriding member function get_error_stack_traces(self ut_suite) return ut_varchar2_list, overriding member function get_serveroutputs return clob ) diff --git a/source/core/types/ut_suite_item.tpb b/source/core/types/ut_suite_item.tpb index 1bfc4adff..d782a8a71 100644 --- a/source/core/types/ut_suite_item.tpb +++ b/source/core/types/ut_suite_item.tpb @@ -42,10 +42,10 @@ create or replace type body ut_suite_item as self.rollback_type := coalesce(self.rollback_type, a_rollback_type); end; - final member procedure do_execute(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base) is + final member procedure do_execute(self in out nocopy ut_suite_item) is l_completed_without_errors boolean; begin - l_completed_without_errors := self.do_execute(a_listener); + l_completed_without_errors := self.do_execute(); end; member function create_savepoint_if_needed return varchar2 is diff --git a/source/core/types/ut_suite_item.tps b/source/core/types/ut_suite_item.tps index 35bee4d25..b78a2f3a7 100644 --- a/source/core/types/ut_suite_item.tps +++ b/source/core/types/ut_suite_item.tps @@ -21,7 +21,7 @@ create or replace type ut_suite_item force under ut_suite_item_base ( member procedure init(self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2), member procedure set_disabled_flag(self in out nocopy ut_suite_item, a_disabled_flag boolean), member function get_disabled_flag return boolean, - not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base), + not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item), member procedure set_default_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer), member function create_savepoint_if_needed return varchar2, member procedure rollback_to_savepoint(self in out nocopy ut_suite_item, a_savepoint varchar2), @@ -32,10 +32,10 @@ create or replace type ut_suite_item force under ut_suite_item_base ( */ member function execution_time return number, - not instantiable member function do_execute(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base) return boolean, - final member procedure do_execute(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base), + not instantiable member function do_execute(self in out nocopy ut_suite_item) return boolean, + final member procedure do_execute(self in out nocopy ut_suite_item), not instantiable member procedure calc_execution_result(self in out nocopy ut_suite_item), - not instantiable member procedure mark_as_errored(self in out nocopy ut_suite_item, a_listener in out nocopy ut_event_listener_base, a_error_stack_trace varchar2), + not instantiable member procedure mark_as_errored(self in out nocopy ut_suite_item, a_error_stack_trace varchar2), not instantiable member function get_error_stack_traces return ut_varchar2_list, not instantiable member function get_serveroutputs return clob, member procedure put_warning(self in out nocopy ut_suite_item, a_message varchar2) diff --git a/source/core/types/ut_suite_item_base.tps b/source/core/types/ut_suite_item_base.tps index 0802e85f2..e4288b71f 100644 --- a/source/core/types/ut_suite_item_base.tps +++ b/source/core/types/ut_suite_item_base.tps @@ -1,4 +1,4 @@ -create or replace type ut_suite_item_base authid current_user as object ( +create or replace type ut_suite_item_base under ut_event_item ( /* utPLSQL - Version 3 Copyright 2016 - 2017 utPLSQL Project @@ -16,11 +16,6 @@ create or replace type ut_suite_item_base authid current_user as object ( limitations under the License. */ - /** - * Object type is a pre-declaration to be referenced by ut_event_listener_base - * The true abstract type is ut_suite_item - */ - self_type varchar2(250 byte), /** * owner of the database object (package) */ diff --git a/source/core/types/ut_test.tpb b/source/core/types/ut_test.tpb index 0016230be..b4f271056 100644 --- a/source/core/types/ut_test.tpb +++ b/source/core/types/ut_test.tpb @@ -52,18 +52,18 @@ create or replace type body ut_test as return l_is_valid; end; - overriding member procedure mark_as_skipped(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base) is + overriding member procedure mark_as_skipped(self in out nocopy ut_test) is begin - a_listener.fire_before_event(ut_utils.gc_test,self); + ut_event_manager.trigger_event(ut_event_manager.before_test, self); self.start_time := current_timestamp; self.result := ut_utils.tr_disabled; ut_utils.debug_log('ut_test.execute - disabled'); self.results_count.set_counter_values(self.result); self.end_time := self.start_time; - a_listener.fire_after_event(ut_utils.gc_test,self); + ut_event_manager.trigger_event(ut_event_manager.after_test, self); end; - overriding member function do_execute(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base) return boolean is + overriding member function do_execute(self in out nocopy ut_test) return boolean is l_no_errors boolean; l_savepoint varchar2(30); begin @@ -71,9 +71,9 @@ create or replace type body ut_test as ut_utils.debug_log('ut_test.execute'); if self.get_disabled_flag() then - mark_as_skipped(a_listener); + mark_as_skipped(); else - a_listener.fire_before_event(ut_utils.gc_test,self); + ut_event_manager.trigger_event(ut_event_manager.before_test, self); self.start_time := current_timestamp; if self.is_valid() then @@ -82,36 +82,36 @@ create or replace type body ut_test as --includes listener calls for before and after actions l_no_errors := true; for i in 1 .. self.before_each_list.count loop - l_no_errors := self.before_each_list(i).do_execute(self, a_listener); + l_no_errors := self.before_each_list(i).do_execute(self); exit when not l_no_errors; end loop; if l_no_errors then for i in 1 .. self.before_test_list.count loop - l_no_errors := self.before_test_list(i).do_execute(self, a_listener); + l_no_errors := self.before_test_list(i).do_execute(self); exit when not l_no_errors; end loop; if l_no_errors then -- execute the test - self.item.do_execute(self, a_listener, self.expected_error_codes); + self.item.do_execute(self, self.expected_error_codes); end if; -- perform cleanup regardless of the test or setup failure for i in 1 .. self.after_test_list.count loop - self.after_test_list(i).do_execute(self, a_listener); + self.after_test_list(i).do_execute(self); end loop; end if; for i in 1 .. self.after_each_list.count loop - self.after_each_list(i).do_execute(self, a_listener); + self.after_each_list(i).do_execute(self); end loop; self.rollback_to_savepoint(l_savepoint); end if; self.calc_execution_result(); self.end_time := current_timestamp; - a_listener.fire_after_event(ut_utils.gc_test,self); + ut_event_manager.trigger_event(ut_event_manager.after_test, self); end if; return l_no_errors; end; @@ -134,15 +134,15 @@ create or replace type body ut_test as ut_expectation_processor.clear_expectations(); end; - 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) is + overriding member procedure mark_as_errored(self in out nocopy ut_test, a_error_stack_trace varchar2) is begin ut_utils.debug_log('ut_test.fail'); - a_listener.fire_before_event(ut_utils.gc_test, self); + ut_event_manager.trigger_event(ut_event_manager.before_test, self); self.start_time := current_timestamp; self.parent_error_stack_trace := a_error_stack_trace; self.calc_execution_result(); self.end_time := self.start_time; - a_listener.fire_after_event(ut_utils.gc_test, self); + ut_event_manager.trigger_event(ut_event_manager.after_test, self); end; overriding member function get_error_stack_traces(self ut_test) return ut_varchar2_list is diff --git a/source/core/types/ut_test.tps b/source/core/types/ut_test.tps index b9074ba34..962ebe239 100644 --- a/source/core/types/ut_test.tps +++ b/source/core/types/ut_test.tps @@ -60,10 +60,10 @@ create or replace type ut_test under ut_suite_item ( a_expected_error_codes ut_integer_list := null ) return self as result, member function is_valid(self in out nocopy ut_test) return boolean, - overriding member procedure mark_as_skipped(self in out nocopy ut_test, a_listener in out nocopy ut_event_listener_base), - 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 mark_as_skipped(self in out nocopy ut_test), + overriding member function do_execute(self in out nocopy ut_test) 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), + overriding member procedure mark_as_errored(self in out nocopy ut_test, a_error_stack_trace varchar2), overriding member function get_error_stack_traces(self ut_test) return ut_varchar2_list, overriding member function get_serveroutputs return clob ) diff --git a/source/install.sql b/source/install.sql index 4bc6a907e..133c44b45 100644 --- a/source/install.sql +++ b/source/install.sql @@ -50,12 +50,17 @@ alter session set current_schema = &&ut3_owner; @@install_component.sql 'reporters/ut_ansiconsole_helper.pks' @@install_component.sql 'reporters/ut_ansiconsole_helper.pkb' +--event manager objects +@@install_component.sql 'core/events/ut_event_item.tps' +@@install_component.sql 'core/events/ut_event_listener.tps' +@@install_component.sql 'core/events/ut_event_manager.pks' +@@install_component.sql 'core/events/ut_event_manager.pkb' + --core types @@install_component.sql 'core/types/ut_expectation_result.tps' @@install_component.sql 'core/types/ut_expectation_results.tps' @@install_component.sql 'core/types/ut_results_counter.tps' @@install_component.sql 'core/types/ut_suite_item_base.tps' -@@install_component.sql 'core/types/ut_event_listener_base.tps' @@install_component.sql 'core/types/ut_suite_item.tps' @@install_component.sql 'core/types/ut_suite_items.tps' @@install_component.sql 'core/types/ut_executable.tps' @@ -70,7 +75,6 @@ alter session set current_schema = &&ut3_owner; @@install_component.sql 'core/types/ut_run.tps' @@install_component.sql 'core/types/ut_reporter_base.tps' @@install_component.sql 'core/types/ut_reporters.tps' -@@install_component.sql 'core/types/ut_event_listener.tps' --output buffer base api @@install_component.sql 'core/output_buffers/ut_output_buffer_base.tps' @@ -135,7 +139,6 @@ prompt Installing PLSQL profiler objects into &&ut3_owner schema @@install_component.sql 'core/types/ut_logical_suite.tpb' @@install_component.sql 'core/types/ut_suite.tpb' @@install_component.sql 'core/types/ut_run.tpb' -@@install_component.sql 'core/types/ut_event_listener.tpb' @@install_component.sql 'core/types/ut_expectation_result.tpb' @@install_component.sql 'core/types/ut_reporter_base.tpb' @@install_component.sql 'core/types/ut_output_reporter_base.tpb' diff --git a/source/reporters/ut_documentation_reporter.tpb b/source/reporters/ut_documentation_reporter.tpb index 96545dd53..4884f0113 100644 --- a/source/reporters/ut_documentation_reporter.tpb +++ b/source/reporters/ut_documentation_reporter.tpb @@ -65,26 +65,18 @@ create or replace type body ut_documentation_reporter is self.print_clob(a_test.get_serveroutputs); end; - overriding member procedure after_calling_before_all(self in out nocopy ut_documentation_reporter, a_suite in ut_logical_suite) is - l_list ut_executables; + overriding member procedure after_calling_before_all(self in out nocopy ut_documentation_reporter, a_executable in ut_executable) is begin - l_list := treat(a_suite as ut_suite).before_all_list; - for i in 1 .. l_list.count loop - if l_list(i).serveroutput is not null and l_list(i).serveroutput != empty_clob() then - self.print_clob(l_list(i).serveroutput); - end if; - end loop; + if a_executable.serveroutput is not null and a_executable.serveroutput != empty_clob() then + self.print_clob(a_executable.serveroutput); + end if; end; - overriding member procedure after_calling_after_all(self in out nocopy ut_documentation_reporter, a_suite in ut_logical_suite) is - l_list ut_executables; + overriding member procedure after_calling_after_all(self in out nocopy ut_documentation_reporter, a_executable in ut_executable) is begin - l_list := treat(a_suite as ut_suite).after_all_list; - for i in 1 .. l_list.count loop - if l_list(i).serveroutput is not null and l_list(i).serveroutput != empty_clob() then - self.print_clob(l_list(i).serveroutput); - end if; - end loop; + if a_executable.serveroutput is not null and a_executable.serveroutput != empty_clob() then + self.print_clob(a_executable.serveroutput); + end if; end; overriding member procedure after_calling_suite(self in out nocopy ut_documentation_reporter, a_suite ut_logical_suite) as diff --git a/source/reporters/ut_documentation_reporter.tps b/source/reporters/ut_documentation_reporter.tps index 152544452..6048eb970 100644 --- a/source/reporters/ut_documentation_reporter.tps +++ b/source/reporters/ut_documentation_reporter.tps @@ -23,8 +23,8 @@ create or replace type ut_documentation_reporter under ut_console_reporter_base( overriding member procedure print_text(self in out nocopy ut_documentation_reporter, a_text varchar2), overriding member procedure before_calling_suite(self in out nocopy ut_documentation_reporter, a_suite ut_logical_suite), overriding member procedure after_calling_test(self in out nocopy ut_documentation_reporter, a_test ut_test), - overriding member procedure after_calling_after_all (self in out nocopy ut_documentation_reporter, a_suite in ut_logical_suite), - overriding member procedure after_calling_before_all (self in out nocopy ut_documentation_reporter, a_suite in ut_logical_suite), + overriding member procedure after_calling_after_all (self in out nocopy ut_documentation_reporter, a_executable in ut_executable), + overriding member procedure after_calling_before_all (self in out nocopy ut_documentation_reporter, a_executable in ut_executable), overriding member procedure after_calling_suite(self in out nocopy ut_documentation_reporter, a_suite ut_logical_suite), overriding member procedure after_calling_run(self in out nocopy ut_documentation_reporter, a_run in ut_run), diff --git a/source/uninstall.sql b/source/uninstall.sql index d7494d81f..5fe859819 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -230,8 +230,6 @@ drop type ut_suite_items force; drop type ut_suite_item force; -drop type ut_event_listener_base force; - drop type ut_suite_item_base force; drop type ut_output_table_buffer force; @@ -254,6 +252,12 @@ drop type ut_expectation_results force; drop type ut_expectation_result force; +drop package ut_event_manager; + +drop type ut_event_listener force; + +drop type ut_event_item force; + drop type ut_key_value_pairs force; drop type ut_key_value_pair force; diff --git a/test/core/test_ut_suite.pkb b/test/core/test_ut_suite.pkb index 56ca28b9b..1e04beb23 100644 --- a/test/core/test_ut_suite.pkb +++ b/test/core/test_ut_suite.pkb @@ -7,7 +7,6 @@ create or replace package body test_ut_suite is procedure disabled_suite is l_suite ut3.ut_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); begin --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); @@ -18,7 +17,7 @@ create or replace package body test_ut_suite is l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_be_null; ut.expect(l_suite.result).to_equal(ut3.ut_utils.tr_disabled); @@ -31,7 +30,6 @@ create or replace package body test_ut_suite is procedure beforeall_errors is l_suite ut3.ut_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); begin --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); @@ -39,7 +37,7 @@ create or replace package body test_ut_suite is l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'failing_procedure', ut3.ut_utils.gc_before_all)); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'set_g_number_0')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_be_null; ut.expect(l_suite.result).to_equal(ut3.ut_utils.tr_error); @@ -52,7 +50,6 @@ create or replace package body test_ut_suite is procedure aftereall_errors is l_suite ut3.ut_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); begin --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); @@ -62,7 +59,7 @@ create or replace package body test_ut_suite is l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'set_g_number_0')); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(1); ut.expect(l_suite.result).to_equal(ut3.ut_utils.tr_success); @@ -75,33 +72,30 @@ create or replace package body test_ut_suite is procedure package_without_body is l_suite ut3.ut_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); begin l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_WITHOUT_BODY'); l_suite.path := 'UT_WITHOUT_BODY'; l_suite.add_item(ut3.ut_test(a_object_name => 'ut_without_body',a_name => 'test1'/*, a_rollback_type => ut3.ut_utils.gc_rollback_auto*/)); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(l_suite.result).to_equal(ut3.ut_utils.tr_error); end; procedure package_with_invalid_body is l_suite ut3.ut_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); begin l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_WITH_INVALID_BODY'); l_suite.path := 'UT_WITH_INVALID_BODY'; l_suite.add_item(ut3.ut_test(a_object_name => 'ut_with_invalid_body',a_name => 'test1'/*, a_rollback_type => ut3.ut_utils.gc_rollback_auto*/)); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(l_suite.result).to_equal(ut3.ut_utils.tr_error); end; procedure test_rollback_type(a_procedure_name varchar2, a_rollback_type integer, a_expectation ut3_latest_release.ut_matcher) is l_suite ut3.ut_suite; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); begin --Arrange execute immediate 'delete from ut$test_table'; @@ -112,7 +106,7 @@ create or replace package body test_ut_suite is l_suite.set_default_rollback_type(a_rollback_type); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(core.get_value(q'[ut_transaction_control.count_rows('t')]')).to_( a_expectation ); diff --git a/test/core/test_ut_test.pkb b/test/core/test_ut_test.pkb index ba48e2533..243aadfe7 100644 --- a/test/core/test_ut_test.pkb +++ b/test/core/test_ut_test.pkb @@ -8,7 +8,6 @@ create or replace package body test_ut_test is procedure disabled_test is l_suite ut3.ut_suite; l_test ut3.ut_test; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); begin --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); @@ -19,7 +18,7 @@ create or replace package body test_ut_test is l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); l_suite.items(l_suite.items.last).disabled_flag := ut3.ut_utils.boolean_to_int(true); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(1); ut.expect(l_suite.result).to_equal(ut3.ut_utils.tr_success); @@ -33,7 +32,6 @@ create or replace package body test_ut_test is procedure aftertest_errors is l_suite ut3.ut_suite; l_test ut3.ut_test; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); begin --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); @@ -46,7 +44,7 @@ create or replace package body test_ut_test is l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(3); ut.expect(l_suite.result).to_equal(ut3.ut_utils.tr_error); @@ -60,7 +58,6 @@ create or replace package body test_ut_test is procedure aftereach_errors is l_suite ut3.ut_suite; l_test ut3.ut_test; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); begin --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); @@ -71,7 +68,7 @@ create or replace package body test_ut_test is l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(3); ut.expect(l_suite.result).to_equal(ut3.ut_utils.tr_error); @@ -85,7 +82,6 @@ create or replace package body test_ut_test is procedure beforetest_errors is l_suite ut3.ut_suite; l_test ut3.ut_test; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); begin --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); @@ -96,7 +92,7 @@ create or replace package body test_ut_test is l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(2); ut.expect(l_suite.result).to_equal(ut3.ut_utils.tr_error); @@ -110,7 +106,6 @@ create or replace package body test_ut_test is procedure beforeeach_errors is l_suite ut3.ut_suite; l_test ut3.ut_test; - l_listener ut3.ut_event_listener := ut3.ut_event_listener(ut3.ut_reporters()); begin --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); @@ -121,7 +116,7 @@ create or replace package body test_ut_test is l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act - l_suite.do_execute(l_listener); + l_suite.do_execute(); --Assert ut.expect(ut_example_tests.g_number).to_equal(2); ut.expect(l_suite.result).to_equal(ut3.ut_utils.tr_error); From f955d2ad17505e854b53afe4d34df1a53e8195ee Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sun, 25 Mar 2018 15:00:38 +0100 Subject: [PATCH 05/28] Redefined event listeners mechanism. --- ...RunnerForTheTop2PackageProcedureByPath.sql | 8 +- ...orTheTop2PackageProcedureByPathCurUser.sql | 6 +- ...th.PrepareRunnerForTheTopPackageByName.sql | 12 +- ...areRunnerForTheTopPackageByNameCurUser.sql | 12 +- ...eRunnerForTheTopPackageProcedureByPath.sql | 4 +- ...ForTheTopPackageProcedureByPathCurUser.sql | 4 +- ...PackageProcedureWithoutSubsuitesByName.sql | 4 +- ...ProcedureWithoutSubsuitesByNameCurUser.sql | 4 +- .../ut_test.TeardownProcedureNameNull.sql | 2 +- source/api/ut_runner.pkb | 2 +- source/core/events/ut_event_manager.pks | 25 +--- source/core/types/ut_executable.tpb | 121 ++++++++---------- source/core/types/ut_executable.tps | 1 - source/core/types/ut_logical_suite.tpb | 12 +- source/core/types/ut_reporter_base.tpb | 95 +++++++------- source/core/types/ut_reporter_base.tps | 4 +- source/core/types/ut_run.tpb | 9 +- source/core/types/ut_suite.tpb | 64 ++++----- source/core/types/ut_suite.tps | 1 - source/core/types/ut_test.tpb | 76 ++++------- source/core/types/ut_test.tps | 1 - source/core/ut_utils.pks | 41 ++++-- 22 files changed, 225 insertions(+), 283 deletions(-) diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPath.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPath.sql index 7c4147736..7ac36e983 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPath.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPath.sql @@ -30,8 +30,8 @@ begin l_test_proc := treat(l_test2_suite.items(1) as 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; + ut.expect(l_test_proc.before_test_list.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.tr_success then :test_result := ut_utils.tr_success; @@ -44,8 +44,8 @@ begin dbms_output.put_line(q'[ut.expect(l_test2_suite.name).to_equal('test_package_2');=]'||l_test2_suite.name); dbms_output.put_line(q'[ut.expect(l_test2_suite.items.count).to_equal(1);=]'||l_test2_suite.items.count); dbms_output.put_line(q'[ut.expect(l_test_proc.name).to_equal('test2');=]'||l_test_proc.name); - dbms_output.put_line(q'[ut.expect(l_test_proc.before_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.before_test is not null)); - dbms_output.put_line(q'[ut.expect(l_test_proc.after_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.after_test is not null)); + dbms_output.put_line(q'[ut.expect(l_test_proc.before_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.before_test_list.count())); + dbms_output.put_line(q'[ut.expect(l_test_proc.after_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.after_test_list.count())); end if; end; diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPathCurUser.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPathCurUser.sql index e0552534f..4ed18cd5a 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPathCurUser.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTop2PackageProcedureByPathCurUser.sql @@ -31,7 +31,7 @@ begin l_test_proc := treat(l_test2_suite.items(1) as ut_test); ut.expect(l_test_proc.name).to_equal('test2'); ut.expect(l_test_proc.before_test_list.count).to_equal(1); - ut.expect(l_test_proc.after_test.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.tr_success then :test_result := ut_utils.tr_success; @@ -44,8 +44,8 @@ begin dbms_output.put_line(q'[ut.expect(l_test2_suite.name).to_equal('test_package_2');=]'||l_test2_suite.name); dbms_output.put_line(q'[ut.expect(l_test2_suite.items.count).to_equal(1);=]'||l_test2_suite.items.count); dbms_output.put_line(q'[ut.expect(l_test_proc.name).to_equal('test2');=]'||l_test_proc.name); - dbms_output.put_line(q'[ut.expect(l_test_proc.before_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.before_test is not null)); - dbms_output.put_line(q'[ut.expect(l_test_proc.after_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.after_test is not null)); + dbms_output.put_line(q'[ut.expect(l_test_proc.before_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.before_test_list.count())); + dbms_output.put_line(q'[ut.expect(l_test_proc.after_test is not null).to_be_true;=]'||ut_utils.to_string(l_test_proc.after_test_list.count())); end if; end; diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByName.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByName.sql index 8253892c4..392af0476 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByName.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByName.sql @@ -25,16 +25,16 @@ begin 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 ut_test).before_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut_test).after_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut_test).before_each.is_defined).to_be_true; + ut.expect(treat(l_test1_suite.items(1) as ut_test).before_test_list.count()).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut_test).after_test_list.count()).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut_test).before_each_list.count()).to_equal(1); ut.expect(treat(l_test1_suite.items(1) as 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 ut_test).before_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut_test).after_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut_test).before_each.is_defined).to_be_true; + ut.expect(treat(l_test1_suite.items(2) as ut_test).before_test_list.count()).to_equal(1); + ut.expect(treat(l_test1_suite.items(2) as ut_test).after_test_list.count()).to_equal(1); + ut.expect(treat(l_test1_suite.items(2) as ut_test).before_each_list.count()).to_equal(1); ut.expect(treat(l_test1_suite.items(2) as ut_test).disabled_flag).to_equal(0); -- temporary behavior. diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByNameCurUser.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByNameCurUser.sql index 4140ad2b2..5fb68e752 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByNameCurUser.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageByNameCurUser.sql @@ -25,16 +25,16 @@ begin 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 ut_test).before_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut_test).after_test.is_defined).to_be_false; - ut.expect(treat(l_test1_suite.items(1) as ut_test).before_each.is_defined).to_be_true; + ut.expect(treat(l_test1_suite.items(1) as ut_test).before_test_list.count()).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut_test).after_test_list.count()).to_equal(0); + ut.expect(treat(l_test1_suite.items(1) as ut_test).before_each_list.count()).to_equal(1); ut.expect(treat(l_test1_suite.items(1) as 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 ut_test).before_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut_test).after_test.is_defined).to_be_true; - ut.expect(treat(l_test1_suite.items(2) as ut_test).before_each.is_defined).to_be_true; + ut.expect(treat(l_test1_suite.items(2) as ut_test).before_test_list.count()).to_equal(1); + ut.expect(treat(l_test1_suite.items(2) as ut_test).after_test_list.count()).to_equal(1); + ut.expect(treat(l_test1_suite.items(2) as ut_test).before_each_list.count()).to_equal(1); ut.expect(treat(l_test1_suite.items(2) as ut_test).disabled_flag).to_equal(0); -- temporary behavior. diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPath.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPath.sql index cfbaefa66..86ecb3d84 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPath.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPath.sql @@ -27,8 +27,8 @@ begin 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; + ut.expect(l_test_proc.before_test_list.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.tr_success then :test_result := ut_utils.tr_success; diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPathCurUser.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPathCurUser.sql index 56d527fb2..c86b8ce46 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPathCurUser.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureByPathCurUser.sql @@ -27,8 +27,8 @@ begin 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; + ut.expect(l_test_proc.before_test_list.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.tr_success then :test_result := ut_utils.tr_success; diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByName.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByName.sql index 531c2daa9..9b1e5c94c 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByName.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByName.sql @@ -25,8 +25,8 @@ begin l_test_proc := treat(l_test1_suite.items(1) as 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; + ut.expect(l_test_proc.before_test_list.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.tr_success then :test_result := ut_utils.tr_success; diff --git a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByNameCurUser.sql b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByNameCurUser.sql index f744f56cb..5d2db6124 100644 --- a/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByNameCurUser.sql +++ b/old_tests/ut_suite_manager/ut_suite_manager.configure_execution_by_path.PrepareRunnerForTheTopPackageProcedureWithoutSubsuitesByNameCurUser.sql @@ -25,8 +25,8 @@ begin l_test_proc := treat(l_test1_suite.items(1) as 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; + ut.expect(l_test_proc.before_test_list.count).to_equal(1); + ut.expect(l_test_proc.after_test_list.count).to_equal(1); if ut_expectation_processor.get_status = ut_utils.tr_success then :test_result := ut_utils.tr_success; diff --git a/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql b/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql index d42078d30..6bfdeb73b 100644 --- a/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql +++ b/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql @@ -14,7 +14,7 @@ begin if ut_example_tests.g_char = 'a' then :test_result := ut_utils.tr_success; else - dbms_output.put_line('expected: ut_example_tests.g_char = ''a'', got: '||ut_example_tests.g_char ); + dbms_output.put_line('expected: ut_example_tests.g_char = ''a'', got: '''||ut_example_tests.g_char||'''' ); end if; end; / diff --git a/source/api/ut_runner.pkb b/source/api/ut_runner.pkb index 73dc3057b..2492261a0 100644 --- a/source/api/ut_runner.pkb +++ b/source/api/ut_runner.pkb @@ -36,7 +36,7 @@ create or replace package body ut_runner is procedure finish_run(a_run ut_run) is begin ut_utils.cleanup_temp_tables; - ut_event_manager.trigger_event(ut_event_manager.on_finalize, a_run); + ut_event_manager.trigger_event(ut_utils.gc_finalize, a_run); ut_metadata.reset_source_definition_cache; ut_utils.read_cache_to_dbms_output(); ut_coverage_helper.cleanup_tmp_table(); diff --git a/source/core/events/ut_event_manager.pks b/source/core/events/ut_event_manager.pks index 146eded53..4e690082c 100644 --- a/source/core/events/ut_event_manager.pks +++ b/source/core/events/ut_event_manager.pks @@ -15,30 +15,7 @@ create or replace package ut_event_manager authid current_user as See the License for the specific language governing permissions and limitations under the License. */ - - /* Constants: Event names */ - subtype t_event_name is varchar2(30); - before_run constant t_event_name := 'before_run'; - before_suite constant t_event_name := 'before_suite'; - before_before_all constant t_event_name := 'before_before_all'; - before_before_each constant t_event_name := 'before_before_each'; - before_before_test constant t_event_name := 'before_before_test'; - before_test constant t_event_name := 'before_test'; - before_test_execute constant t_event_name := 'before_test_execute'; - before_after_test constant t_event_name := 'before_after_test'; - before_after_each constant t_event_name := 'before_after_each'; - before_after_all constant t_event_name := 'before_after_all'; - after_run constant t_event_name := 'after_run'; - after_suite constant t_event_name := 'after_suite'; - after_before_all constant t_event_name := 'after_before_all'; - after_before_each constant t_event_name := 'after_before_each'; - after_before_test constant t_event_name := 'after_before_test'; - after_test constant t_event_name := 'after_test'; - after_test_execute constant t_event_name := 'after_test_execute'; - after_after_test constant t_event_name := 'after_after_test'; - after_after_each constant t_event_name := 'after_after_each'; - after_after_all constant t_event_name := 'after_after_all'; - on_finalize constant t_event_name := 'on_finalize'; + subtype t_event_name is varchar2(250); procedure trigger_event( a_event_name t_event_name, a_event_object ut_event_item ); diff --git a/source/core/types/ut_executable.tpb b/source/core/types/ut_executable.tpb index 51915700d..87c1cb533 100644 --- a/source/core/types/ut_executable.tpb +++ b/source/core/types/ut_executable.tpb @@ -28,11 +28,6 @@ create or replace type body ut_executable is return; end; - member function is_defined return boolean is - begin - return self.procedure_name is not null and self.object_name is not null; - end; - member function is_valid(self in out nocopy ut_executable) return boolean is l_result boolean := false; l_message_part varchar2(4000) := 'Call params for ' || self.associated_event_name || ' are not valid: '; @@ -45,7 +40,7 @@ create or replace type body ut_executable is elsif self.procedure_name is null then self.error_stack := l_message_part || 'procedure is not defined'; elsif not ut_metadata.procedure_exists(self.owner_name, self.object_name, self.procedure_name) then - self.error_stack := l_message_part || 'package missing procedure ' + self.error_stack := l_message_part || 'procedure does not exist ' || upper(self.owner_name || '.' || self.object_name || '.' ||self.procedure_name); else l_result := true; @@ -91,70 +86,62 @@ create or replace type body ut_executable is end loop; end save_dbms_output; begin - if self.is_defined() then - l_start_transaction_id := dbms_transaction.local_transaction_id(true); - - -- report to application_info - ut_utils.set_client_info(self.procedure_name); - - --listener - before call to executable - ut_event_manager.trigger_event('before_'||self.associated_event_name, self); - - begin - ut_metadata.do_resolve(a_owner => self.owner_name, a_object => self.object_name, a_procedure_name => self.procedure_name); - exception - when others then - self.error_stack := dbms_utility.format_error_stack; - self.error_backtrace := dbms_utility.format_error_backtrace; - l_completed_without_errors := false; - end; - if l_completed_without_errors then - l_statement := - 'declare' || chr(10) || - ' l_error_stack varchar2(32767);' || chr(10) || - ' l_error_backtrace varchar2(32767);' || chr(10) || - 'begin' || chr(10) || - ' begin' || chr(10) || - ' ' || ut_metadata.form_name(self.owner_name, self.object_name, self.procedure_name) || ';' || chr(10) || - ' exception' || chr(10) || - ' when others then ' || chr(10) || - ' l_error_stack := dbms_utility.format_error_stack;' || chr(10) || - ' l_error_backtrace := dbms_utility.format_error_backtrace;' || chr(10) || - ' --raise on ORA-04068, ORA-04061: existing state of packages has been discarded to avoid unrecoverable session exception' || chr(10) || - ' end;' || chr(10) || - ' :a_error_stack := l_error_stack;' || chr(10) || - ' :a_error_backtrace := l_error_backtrace;' || chr(10) || - 'end;'; - - ut_utils.debug_log('ut_executable.do_execute l_statement: ' || l_statement); - - l_cursor_number := dbms_sql.open_cursor; - dbms_sql.parse(l_cursor_number, statement => l_statement, language_flag => dbms_sql.native); - dbms_sql.bind_variable(l_cursor_number, 'a_error_stack', to_char(null), 32767); - dbms_sql.bind_variable(l_cursor_number, 'a_error_backtrace', to_char(null), 32767); - - l_status := dbms_sql.execute(l_cursor_number); - dbms_sql.variable_value(l_cursor_number, 'a_error_stack', self.error_stack); - dbms_sql.variable_value(l_cursor_number, 'a_error_backtrace', self.error_backtrace); - dbms_sql.close_cursor(l_cursor_number); - - save_dbms_output; - - l_completed_without_errors := (self.error_stack||self.error_backtrace) is null; - if self.error_stack like '%ORA-04068%' or self.error_stack like '%ORA-04061%' then - ut_expectation_processor.set_invalidation_exception(); - end if; - --listener - after call to executable - ut_event_manager.trigger_event('after_'||self.associated_event_name, self); - - l_end_transaction_id := dbms_transaction.local_transaction_id(); - if l_start_transaction_id != l_end_transaction_id or l_end_transaction_id is null then - a_item.add_transaction_invalidator(self.form_name()); - end if; - ut_utils.set_client_info(null); + l_start_transaction_id := dbms_transaction.local_transaction_id(true); + + -- report to application_info + ut_utils.set_client_info(self.procedure_name); + + --listener - before call to executable + ut_event_manager.trigger_event('before_'||self.associated_event_name, self); + + l_completed_without_errors := self.is_valid(); + if l_completed_without_errors then + l_statement := + 'declare' || chr(10) || + ' l_error_stack varchar2(32767);' || chr(10) || + ' l_error_backtrace varchar2(32767);' || chr(10) || + 'begin' || chr(10) || + ' begin' || chr(10) || + ' ' || ut_metadata.form_name(self.owner_name, self.object_name, self.procedure_name) || ';' || chr(10) || + ' exception' || chr(10) || + ' when others then ' || chr(10) || + ' l_error_stack := dbms_utility.format_error_stack;' || chr(10) || + ' l_error_backtrace := dbms_utility.format_error_backtrace;' || chr(10) || + ' --raise on ORA-04068, ORA-04061: existing state of packages has been discarded to avoid unrecoverable session exception' || chr(10) || + ' end;' || chr(10) || + ' :a_error_stack := l_error_stack;' || chr(10) || + ' :a_error_backtrace := l_error_backtrace;' || chr(10) || + 'end;'; + + ut_utils.debug_log('ut_executable.do_execute l_statement: ' || l_statement); + + l_cursor_number := dbms_sql.open_cursor; + dbms_sql.parse(l_cursor_number, statement => l_statement, language_flag => dbms_sql.native); + dbms_sql.bind_variable(l_cursor_number, 'a_error_stack', to_char(null), 32767); + dbms_sql.bind_variable(l_cursor_number, 'a_error_backtrace', to_char(null), 32767); + + l_status := dbms_sql.execute(l_cursor_number); + dbms_sql.variable_value(l_cursor_number, 'a_error_stack', self.error_stack); + dbms_sql.variable_value(l_cursor_number, 'a_error_backtrace', self.error_backtrace); + dbms_sql.close_cursor(l_cursor_number); + + save_dbms_output; + + l_completed_without_errors := (self.error_stack||self.error_backtrace) is null; + if self.error_stack like '%ORA-04068%' or self.error_stack like '%ORA-04061%' then + ut_expectation_processor.set_invalidation_exception(); end if; end if; + --listener - after call to executable + ut_event_manager.trigger_event('after_'||self.associated_event_name, self); + + l_end_transaction_id := dbms_transaction.local_transaction_id(); + if l_start_transaction_id != l_end_transaction_id or l_end_transaction_id is null then + a_item.add_transaction_invalidator(self.form_name()); + end if; + ut_utils.set_client_info(null); + return l_completed_without_errors; end do_execute; diff --git a/source/core/types/ut_executable.tps b/source/core/types/ut_executable.tps index 71278dedc..50b71d323 100644 --- a/source/core/types/ut_executable.tps +++ b/source/core/types/ut_executable.tps @@ -27,7 +27,6 @@ create or replace type ut_executable under ut_event_item( serveroutput clob, constructor function ut_executable( self in out nocopy ut_executable, a_context ut_suite_item, a_procedure_name varchar2, a_associated_event_name varchar2) return self as result, member function is_valid(self in out nocopy ut_executable) return boolean, - member function is_defined return boolean, member function form_name return varchar2, member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item), /** diff --git a/source/core/types/ut_logical_suite.tpb b/source/core/types/ut_logical_suite.tpb index 2824db10d..870b31bcf 100644 --- a/source/core/types/ut_logical_suite.tpb +++ b/source/core/types/ut_logical_suite.tpb @@ -41,13 +41,13 @@ create or replace type body ut_logical_suite as overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite) is begin - ut_event_manager.trigger_event(ut_event_manager.before_suite, self); + ut_event_manager.trigger_event(ut_utils.gc_before_suite, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop self.items(i).mark_as_skipped(); end loop; self.end_time := self.start_time; - ut_event_manager.trigger_event(ut_event_manager.after_suite, self); + ut_event_manager.trigger_event(ut_utils.gc_after_suite, self); self.calc_execution_result(); end; @@ -66,7 +66,7 @@ create or replace type body ut_logical_suite as begin ut_utils.debug_log('ut_logical_suite.execute'); - ut_event_manager.trigger_event(ut_event_manager.before_suite, self); + ut_event_manager.trigger_event(ut_utils.gc_before_suite, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop @@ -77,7 +77,7 @@ create or replace type body ut_logical_suite as self.calc_execution_result(); self.end_time := current_timestamp; - ut_event_manager.trigger_event(ut_event_manager.after_suite, self); + ut_event_manager.trigger_event(ut_utils.gc_after_suite, self); return l_completed_without_errors; end; @@ -101,7 +101,7 @@ create or replace type body ut_logical_suite as overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_error_stack_trace varchar2) is begin ut_utils.debug_log('ut_logical_suite.fail'); - ut_event_manager.trigger_event(ut_event_manager.before_suite, self); + ut_event_manager.trigger_event(ut_utils.gc_before_suite, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop -- execute the item (test or suite) @@ -109,7 +109,7 @@ create or replace type body ut_logical_suite as end loop; self.calc_execution_result(); self.end_time := self.start_time; - ut_event_manager.trigger_event(ut_event_manager.after_suite, self); + ut_event_manager.trigger_event(ut_utils.gc_after_suite, self); end; overriding member function get_error_stack_traces return ut_varchar2_list is diff --git a/source/core/types/ut_reporter_base.tpb b/source/core/types/ut_reporter_base.tpb index 9cf85e01c..508299cd7 100644 --- a/source/core/types/ut_reporter_base.tpb +++ b/source/core/types/ut_reporter_base.tpb @@ -136,7 +136,7 @@ create or replace type body ut_reporter_base is null; end; - overriding final member function get_supported_events return ut_varchar2_list is + overriding member function get_supported_events return ut_varchar2_list is l_events_list ut_varchar2_list; begin select lower(replace(procedure_name,'CALLING_')) @@ -145,55 +145,56 @@ create or replace type body ut_reporter_base is where object_name = upper(self_type) and (procedure_name like 'BEFORE_%' or procedure_name like 'AFTER_%'); l_events_list.extend; - l_events_list(l_events_list.last) := 'on_finalize'; + l_events_list(l_events_list.last) := ut_utils.gc_finalize; return l_events_list; end; - overriding final member procedure on_event( self in out nocopy ut_reporter_base, a_event_name varchar2, a_event_item ut_event_item) is - begin - if a_event_name = ut_event_manager.before_run then - self.before_calling_run(treat(a_event_item as ut_run)); - elsif a_event_name = ut_event_manager.before_suite then - self.before_calling_suite(treat(a_event_item as ut_logical_suite)); - elsif a_event_name = ut_event_manager.before_before_all then - self.before_calling_before_all(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.before_before_each then - self.before_calling_before_each(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.before_test then - self.before_calling_test(treat(a_event_item as ut_test)); - elsif a_event_name = ut_event_manager.before_before_test then - self.before_calling_before_test(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.before_test_execute then - self.before_calling_test_execute(treat(a_event_item as ut_test)); - elsif a_event_name = ut_event_manager.before_after_test then - self.before_calling_after_test(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.before_after_each then - self.before_calling_after_each(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.before_after_all then - self.before_calling_after_all(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.after_run then - self.after_calling_run(treat(a_event_item as ut_run)); - elsif a_event_name = ut_event_manager.after_suite then - self.after_calling_suite(treat(a_event_item as ut_logical_suite)); - elsif a_event_name = ut_event_manager.after_before_all then - self.after_calling_before_all(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.after_before_each then - self.after_calling_before_each(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.after_test then - self.after_calling_test(treat(a_event_item as ut_test)); - elsif a_event_name = ut_event_manager.after_before_test then - self.after_calling_before_test(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.after_test_execute then - self.after_calling_test_execute(treat(a_event_item as ut_test)); - elsif a_event_name = ut_event_manager.after_after_test then - self.after_calling_after_test(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.after_after_each then - self.after_calling_after_each(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.after_after_all then - self.after_calling_after_all(treat(a_event_item as ut_executable)); - elsif a_event_name = ut_event_manager.on_finalize then - self.on_finalize(treat(a_event_item as ut_run)); - end if; + overriding member procedure on_event( self in out nocopy ut_reporter_base, a_event_name varchar2, a_event_item ut_event_item) is + begin + case a_event_name + when ut_utils.gc_before_run + then self.before_calling_run(treat(a_event_item as ut_run)); + when ut_utils.gc_before_suite + then self.before_calling_suite(treat(a_event_item as ut_logical_suite)); + when ut_utils.gc_before_before_all + then self.before_calling_before_all(treat(a_event_item as ut_executable)); + when ut_utils.gc_before_before_each + then self.before_calling_before_each(treat(a_event_item as ut_executable)); + when ut_utils.gc_before_test + then self.before_calling_test(treat(a_event_item as ut_test)); + when ut_utils.gc_before_before_test + then self.before_calling_before_test(treat(a_event_item as ut_executable)); + when ut_utils.gc_before_test_execute + then self.before_calling_test_execute(treat(a_event_item as ut_test)); + when ut_utils.gc_before_after_test + then self.before_calling_after_test(treat(a_event_item as ut_executable)); + when ut_utils.gc_before_after_each + then self.before_calling_after_each(treat(a_event_item as ut_executable)); + when ut_utils.gc_before_after_all + then self.before_calling_after_all(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_run + then self.after_calling_run(treat(a_event_item as ut_run)); + when ut_utils.gc_after_suite + then self.after_calling_suite(treat(a_event_item as ut_logical_suite)); + when ut_utils.gc_after_before_all + then self.after_calling_before_all(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_before_each + then self.after_calling_before_each(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_test + then self.after_calling_test(treat(a_event_item as ut_test)); + when ut_utils.gc_after_before_test + then self.after_calling_before_test(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_test_execute + then self.after_calling_test_execute(treat(a_event_item as ut_test)); + when ut_utils.gc_after_after_test + then self.after_calling_after_test(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_after_each + then self.after_calling_after_each(treat(a_event_item as ut_executable)); + when ut_utils.gc_after_after_all + then self.after_calling_after_all(treat(a_event_item as ut_executable)); + when ut_utils.gc_finalize + then self.on_finalize(treat(a_event_item as ut_run)); + end case; end; end; diff --git a/source/core/types/ut_reporter_base.tps b/source/core/types/ut_reporter_base.tps index 87e0aba0d..688314e8f 100644 --- a/source/core/types/ut_reporter_base.tps +++ b/source/core/types/ut_reporter_base.tps @@ -67,12 +67,12 @@ create or replace type ut_reporter_base under ut_event_listener ( /** * Returns the list of events that are supported by particular implementation of the reporter */ - overriding final member function get_supported_events return ut_varchar2_list, + overriding member function get_supported_events return ut_varchar2_list, /** * Delegates execution of event into individual reporting procedures */ - overriding final member procedure on_event( self in out nocopy ut_reporter_base, a_event_name varchar2, a_event_item ut_event_item) + overriding member procedure on_event( self in out nocopy ut_reporter_base, a_event_name varchar2, a_event_item ut_event_item) ) not final not instantiable diff --git a/source/core/types/ut_run.tpb b/source/core/types/ut_run.tpb index 928a8220d..093b7b464 100644 --- a/source/core/types/ut_run.tpb +++ b/source/core/types/ut_run.tpb @@ -57,7 +57,7 @@ create or replace type body ut_run as begin ut_utils.debug_log('ut_run.execute'); - ut_event_manager.trigger_event(ut_event_manager.before_run, self); + ut_event_manager.trigger_event(ut_utils.gc_before_run, self); self.start_time := current_timestamp; -- clear anything that might stay in the session's cache @@ -71,8 +71,7 @@ create or replace type body ut_run as self.end_time := current_timestamp; - ut_event_manager.trigger_event(ut_event_manager.after_run, self); - ut_event_manager.trigger_event(ut_event_manager.on_finalize, self); + ut_event_manager.trigger_event(ut_utils.gc_after_run, self); return l_completed_without_errors; end; @@ -97,7 +96,7 @@ create or replace type body ut_run as begin ut_utils.debug_log('ut_run.fail'); - ut_event_manager.trigger_event(ut_event_manager.before_run, self); + ut_event_manager.trigger_event(ut_utils.gc_before_run, self); self.start_time := current_timestamp; for i in 1 .. self.items.count loop @@ -107,7 +106,7 @@ create or replace type body ut_run as self.calc_execution_result(); self.end_time := self.start_time; - ut_event_manager.trigger_event(ut_event_manager.after_run, self); + ut_event_manager.trigger_event(ut_utils.gc_after_run, self); end; member function get_run_schemes return ut_varchar2_rows is diff --git a/source/core/types/ut_suite.tpb b/source/core/types/ut_suite.tpb index aa17d7983..ba2599aa4 100644 --- a/source/core/types/ut_suite.tpb +++ b/source/core/types/ut_suite.tpb @@ -26,18 +26,6 @@ create or replace type body ut_suite as return; end; - overriding member function is_valid(self in out nocopy ut_suite) return boolean is - l_is_valid boolean := true; - begin - for i in 1 .. before_all_list.count loop - l_is_valid := self.before_all_list(i).is_valid() and l_is_valid; - end loop; - for i in 1 .. after_all_list.count loop - l_is_valid := self.after_all_list(i).is_valid() and l_is_valid; - end loop; - return l_is_valid; - end; - overriding member function do_execute(self in out nocopy ut_suite) return boolean is l_suite_savepoint varchar2(30); l_no_errors boolean; @@ -56,43 +44,39 @@ create or replace type body ut_suite as if self.get_disabled_flag() then self.mark_as_skipped(); else - ut_event_manager.trigger_event(ut_event_manager.before_suite, self); + ut_event_manager.trigger_event(ut_utils.gc_before_suite, self); self.start_time := current_timestamp; - if self.is_valid() then - - l_suite_savepoint := self.create_savepoint_if_needed(); - - --includes listener calls for before and after actions - l_no_errors := true; - for i in 1 .. self.before_all_list.count loop - l_no_errors := self.before_all_list(i).do_execute(self); - if not l_no_errors then - propagate_error(self.before_all_list(i).get_error_stack_trace()); - exit; - end if; - end loop; - if l_no_errors then - for i in 1 .. self.items.count loop - self.items(i).do_execute(); - end loop; + l_suite_savepoint := self.create_savepoint_if_needed(); + + --includes listener calls for before and after actions + l_no_errors := true; + for i in 1 .. self.before_all_list.count loop + l_no_errors := self.before_all_list(i).do_execute(self); + if not l_no_errors then + propagate_error(self.before_all_list(i).get_error_stack_trace()); + exit; end if; + end loop; - for i in 1 .. after_all_list.count loop - l_no_errors := self.after_all_list(i).do_execute(self); - if not l_no_errors then - self.put_warning(self.after_all_list(i).get_error_stack_trace()); - end if; + if l_no_errors then + for i in 1 .. self.items.count loop + self.items(i).do_execute(); end loop; + end if; - self.rollback_to_savepoint(l_suite_savepoint); + for i in 1 .. after_all_list.count loop + l_no_errors := self.after_all_list(i).do_execute(self); + if not l_no_errors then + self.put_warning(self.after_all_list(i).get_error_stack_trace()); + end if; + end loop; + + self.rollback_to_savepoint(l_suite_savepoint); - else - propagate_error(ut_utils.table_to_clob(self.get_error_stack_traces())); - end if; self.calc_execution_result(); self.end_time := current_timestamp; - ut_event_manager.trigger_event(ut_event_manager.after_suite, self); + ut_event_manager.trigger_event(ut_utils.gc_after_suite, self); end if; ut_utils.set_action(null); diff --git a/source/core/types/ut_suite.tps b/source/core/types/ut_suite.tps index 68de8e29c..dfa55bda6 100644 --- a/source/core/types/ut_suite.tps +++ b/source/core/types/ut_suite.tps @@ -27,7 +27,6 @@ create or replace type ut_suite under ut_logical_suite ( */ after_all_list ut_executables, constructor function ut_suite (self in out nocopy ut_suite , a_object_owner varchar2, a_object_name varchar2) return self as result, - overriding member function is_valid(self in out nocopy ut_suite) return boolean, overriding member function do_execute(self in out nocopy ut_suite) return boolean, overriding member function get_error_stack_traces(self ut_suite) return ut_varchar2_list, overriding member function get_serveroutputs return clob diff --git a/source/core/types/ut_test.tpb b/source/core/types/ut_test.tpb index b4f271056..b835f0413 100644 --- a/source/core/types/ut_test.tpb +++ b/source/core/types/ut_test.tpb @@ -34,33 +34,15 @@ create or replace type body ut_test as return; end; - member function is_valid(self in out nocopy ut_test) return boolean is - l_is_valid boolean := true; - begin - for i in 1 .. before_each_list.count loop - l_is_valid := self.before_each_list(i).is_valid() and l_is_valid; - end loop; - for i in 1 .. after_each_list.count loop - l_is_valid := self.after_each_list(i).is_valid() and l_is_valid; - end loop; - for i in 1 .. before_test_list.count loop - l_is_valid := self.before_test_list(i).is_valid() and l_is_valid; - end loop; - for i in 1 .. after_test_list.count loop - l_is_valid := self.after_test_list(i).is_valid() and l_is_valid; - end loop; - return l_is_valid; - end; - overriding member procedure mark_as_skipped(self in out nocopy ut_test) is begin - ut_event_manager.trigger_event(ut_event_manager.before_test, self); + ut_event_manager.trigger_event(ut_utils.gc_before_test, self); self.start_time := current_timestamp; self.result := ut_utils.tr_disabled; ut_utils.debug_log('ut_test.execute - disabled'); self.results_count.set_counter_values(self.result); self.end_time := self.start_time; - ut_event_manager.trigger_event(ut_event_manager.after_test, self); + ut_event_manager.trigger_event(ut_utils.gc_after_test, self); end; overriding member function do_execute(self in out nocopy ut_test) return boolean is @@ -73,45 +55,43 @@ create or replace type body ut_test as if self.get_disabled_flag() then mark_as_skipped(); else - ut_event_manager.trigger_event(ut_event_manager.before_test, self); + ut_event_manager.trigger_event(ut_utils.gc_before_test, self); self.start_time := current_timestamp; - if self.is_valid() then - l_savepoint := self.create_savepoint_if_needed(); + l_savepoint := self.create_savepoint_if_needed(); + + --includes listener calls for before and after actions + l_no_errors := true; + for i in 1 .. self.before_each_list.count loop + l_no_errors := self.before_each_list(i).do_execute(self); + exit when not l_no_errors; + end loop; - --includes listener calls for before and after actions - l_no_errors := true; - for i in 1 .. self.before_each_list.count loop - l_no_errors := self.before_each_list(i).do_execute(self); + if l_no_errors then + for i in 1 .. self.before_test_list.count loop + l_no_errors := self.before_test_list(i).do_execute(self); exit when not l_no_errors; end loop; if l_no_errors then - for i in 1 .. self.before_test_list.count loop - l_no_errors := self.before_test_list(i).do_execute(self); - exit when not l_no_errors; - end loop; - - if l_no_errors then - -- execute the test - self.item.do_execute(self, self.expected_error_codes); - - end if; - -- perform cleanup regardless of the test or setup failure - for i in 1 .. self.after_test_list.count loop - self.after_test_list(i).do_execute(self); - end loop; - end if; + -- execute the test + self.item.do_execute(self, self.expected_error_codes); - for i in 1 .. self.after_each_list.count loop - self.after_each_list(i).do_execute(self); + end if; + -- perform cleanup regardless of the test or setup failure + for i in 1 .. self.after_test_list.count loop + self.after_test_list(i).do_execute(self); end loop; - self.rollback_to_savepoint(l_savepoint); end if; + for i in 1 .. self.after_each_list.count loop + self.after_each_list(i).do_execute(self); + end loop; + self.rollback_to_savepoint(l_savepoint); + self.calc_execution_result(); self.end_time := current_timestamp; - ut_event_manager.trigger_event(ut_event_manager.after_test, self); + ut_event_manager.trigger_event(ut_utils.gc_after_test, self); end if; return l_no_errors; end; @@ -137,12 +117,12 @@ create or replace type body ut_test as overriding member procedure mark_as_errored(self in out nocopy ut_test, a_error_stack_trace varchar2) is begin ut_utils.debug_log('ut_test.fail'); - ut_event_manager.trigger_event(ut_event_manager.before_test, self); + ut_event_manager.trigger_event(ut_utils.gc_before_test, self); self.start_time := current_timestamp; self.parent_error_stack_trace := a_error_stack_trace; self.calc_execution_result(); self.end_time := self.start_time; - ut_event_manager.trigger_event(ut_event_manager.after_test, self); + ut_event_manager.trigger_event(ut_utils.gc_after_test, self); end; overriding member function get_error_stack_traces(self ut_test) return ut_varchar2_list is diff --git a/source/core/types/ut_test.tps b/source/core/types/ut_test.tps index 962ebe239..e8ecd3cb4 100644 --- a/source/core/types/ut_test.tps +++ b/source/core/types/ut_test.tps @@ -59,7 +59,6 @@ create or replace type ut_test under ut_suite_item ( self in out nocopy ut_test, a_object_owner varchar2 := null, a_object_name varchar2, a_name varchar2, a_expected_error_codes ut_integer_list := null ) return self as result, - member function is_valid(self in out nocopy ut_test) return boolean, overriding member procedure mark_as_skipped(self in out nocopy ut_test), overriding member function do_execute(self in out nocopy ut_test) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_test), diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 7afd3b932..72c47e5ea 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -24,18 +24,35 @@ create or replace package ut_utils authid definer is gc_version constant varchar2(50) := 'v3.1.0.1688-develop'; /* Constants: Event names */ - subtype t_event_name is varchar2(12); - gc_run constant t_event_name := 'run'; - gc_suite constant t_event_name := 'suite'; - gc_before_all constant t_event_name := 'before_all'; - gc_before_each constant t_event_name := 'before_each'; - gc_before_test constant t_event_name := 'before_test'; - gc_test constant t_event_name := 'test'; - gc_test_execute constant t_event_name := 'test_execute'; - gc_after_test constant t_event_name := 'after_test'; - gc_after_each constant t_event_name := 'after_each'; - gc_after_all constant t_event_name := 'after_all'; - gc_finalize constant t_event_name := 'finalize'; + subtype t_event_name is varchar2(30); + gc_before_run constant t_event_name := 'before_run'; + gc_before_suite constant t_event_name := 'before_suite'; + gc_before_before_all constant t_event_name := 'before_before_all'; + gc_before_before_each constant t_event_name := 'before_before_each'; + gc_before_before_test constant t_event_name := 'before_before_test'; + gc_before_test constant t_event_name := 'before_test'; + gc_before_test_execute constant t_event_name := 'before_test_execute'; + gc_before_after_test constant t_event_name := 'before_after_test'; + gc_before_after_each constant t_event_name := 'before_after_each'; + gc_before_after_all constant t_event_name := 'before_after_all'; + gc_after_run constant t_event_name := 'after_run'; + gc_after_suite constant t_event_name := 'after_suite'; + gc_after_before_all constant t_event_name := 'after_before_all'; + gc_after_before_each constant t_event_name := 'after_before_each'; + gc_after_before_test constant t_event_name := 'after_before_test'; + gc_after_test constant t_event_name := 'after_test'; + gc_after_test_execute constant t_event_name := 'after_test_execute'; + gc_after_after_test constant t_event_name := 'after_after_test'; + gc_after_after_each constant t_event_name := 'after_after_each'; + gc_after_after_all constant t_event_name := 'after_after_all'; + gc_finalize constant t_event_name := 'finalize'; + + subtype t_executable_type is varchar2(30); + gc_before_all constant t_executable_type := 'before_all'; + gc_before_each constant t_executable_type := 'before_each'; + gc_test_execute constant t_executable_type := 'test_execute'; + gc_after_each constant t_executable_type := 'after_each'; + gc_after_all constant t_executable_type := 'after_all'; /* Constants: Test Results */ subtype t_test_result is binary_integer range 0 .. 3; From 430f76f304f3153d5807b992a3ab0227827ed877 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sun, 25 Mar 2018 15:01:13 +0100 Subject: [PATCH 06/28] Fixed minor bug in XUnit reporter (showing CDATA for errors even when no errors found) --- source/reporters/ut_xunit_reporter.tpb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/reporters/ut_xunit_reporter.tpb b/source/reporters/ut_xunit_reporter.tpb index 047f8346b..df1aebce0 100644 --- a/source/reporters/ut_xunit_reporter.tpb +++ b/source/reporters/ut_xunit_reporter.tpb @@ -97,7 +97,7 @@ create or replace type body ut_xunit_reporter is end if; l_errors := l_suite.get_error_stack_traces(); - if l_data is not null and l_data != empty_clob() then + if l_errors is not empty then self.print_text(''); self.print_text(' Date: Thu, 29 Mar 2018 01:07:08 +0100 Subject: [PATCH 07/28] Changed propagation of rollback. Now rollback will propagate from parent suite to child suite. Added `ut_utils.gc_rollback_default` and `ut_suite_item.get_rollback_type` to return default when rollback not set explicitly. Added support for `--%context`/`--%endcontext` annotations. Refactored `ut_executable` and `ut_executable_test` to have suite-agnostic constructor. Extended constructor for `ut_suite` to accept `a_name` separately from `a_object_name`. Removed `ut_suite_item_base` - leftover from refactoring of listener model. Significant changes to suite builder - added mechanism to display warnings for invalid annotations. Removed mis-located old tests. --- old_tests/RunAll.sql | 1 - .../ut_test/ut_test.AfterEachExecuted.sql | 2 +- .../ut_test.AfterEachProcedureNameInvalid.sql | 2 +- .../ut_test.AfterEachProcedureNameNull.sql | 2 +- .../ut_test/ut_test.BeforeEachExecuted.sql | 2 +- ...ut_test.BeforeEachProcedureNameInvalid.sql | 2 +- .../ut_test.BeforeEachProcedureNameNull.sql | 2 +- .../ut_test.SetupExecutedBeforeTest.sql | 2 +- .../ut_test.SetupProcedureNameInvalid.sql | 2 +- .../ut_test.SetupProcedureNameNull.sql | 2 +- .../ut_test.TeardownExecutedAfterTest.sql | 2 +- .../ut_test.TeardownProcedureNameInvalid.sql | 2 +- .../ut_test.TeardownProcedureNameNull.sql | 2 +- source/core/types/ut_executable.tpb | 7 +- source/core/types/ut_executable.tps | 2 +- source/core/types/ut_executable_test.tpb | 7 +- source/core/types/ut_executable_test.tps | 2 +- source/core/types/ut_logical_suite.tpb | 4 +- source/core/types/ut_logical_suite.tps | 2 +- source/core/types/ut_suite.tpb | 6 +- source/core/types/ut_suite.tps | 4 +- source/core/types/ut_suite_item.tpb | 13 +- source/core/types/ut_suite_item.tps | 39 +- source/core/types/ut_suite_item_base.tps | 56 -- source/core/types/ut_test.tpb | 2 +- source/core/ut_suite_builder.pkb | 419 ++++++++++----- source/core/ut_suite_manager.pkb | 5 + source/core/ut_utils.pks | 1 + source/install.sql | 1 - source/uninstall.sql | 2 - test/core/test_suite_builder.pkb | 479 +++++++++++++++++- test/core/test_suite_builder.pks | 58 +++ test/core/test_suite_manager.pkb | 10 + test/core/test_ut_suite.pkb | 12 +- test/core/test_ut_test.pkb | 26 +- ...ut.run.ExecutesSuccesfullyAnEmptySuite.sql | 30 -- ...a.GivesFailureWhenComparingTheSameData.sql | 17 - ...itsForMoreDataToAppearForSpecifiedTime.sql | 24 - 38 files changed, 946 insertions(+), 307 deletions(-) delete mode 100644 source/core/types/ut_suite_item_base.tps delete mode 100644 tests/ut/ut.run.ExecutesSuccesfullyAnEmptySuite.sql delete mode 100644 tests/ut_expectations/ut.expect.not_to_equal.anydata.GivesFailureWhenComparingTheSameData.sql delete mode 100644 tests/ut_output_buffer/get_lines.WaitsForMoreDataToAppearForSpecifiedTime.sql diff --git a/old_tests/RunAll.sql b/old_tests/RunAll.sql index 93be77e78..ca6aae898 100644 --- a/old_tests/RunAll.sql +++ b/old_tests/RunAll.sql @@ -321,7 +321,6 @@ begin 'source/core/types/ut_suite_item.tpb', 'source/core/types/ut_suite_item.tps', 'source/core/types/ut_suite_items.tps', - 'source/core/types/ut_suite_item_base.tps', 'source/core/types/ut_test.tpb', 'source/core/types/ut_test.tps', 'source/core/types/ut_varchar2_list.tps', diff --git a/old_tests/ut_test/ut_test.AfterEachExecuted.sql b/old_tests/ut_test/ut_test.AfterEachExecuted.sql index d3ca5cf58..bdf833b65 100644 --- a/old_tests/ut_test/ut_test.AfterEachExecuted.sql +++ b/old_tests/ut_test/ut_test.AfterEachExecuted.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_passing_test' ); begin - simple_test.after_each_list := ut_executables(ut_executable(simple_test, 'aftereach', ut_utils.gc_after_each)); + simple_test.after_each_list := ut_executables(ut_executable(user, 'ut_example_tests', 'aftereach', ut_utils.gc_after_each)); --Act simple_test.do_execute(); --Assert diff --git a/old_tests/ut_test/ut_test.AfterEachProcedureNameInvalid.sql b/old_tests/ut_test/ut_test.AfterEachProcedureNameInvalid.sql index d27a8d4fc..68396caa1 100644 --- a/old_tests/ut_test/ut_test.AfterEachProcedureNameInvalid.sql +++ b/old_tests/ut_test/ut_test.AfterEachProcedureNameInvalid.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_exampletest' ); begin - simple_test.after_each_list := ut_executables(ut_executable(simple_test, 'invalid setup name', ut_utils.gc_after_each)); + simple_test.after_each_list := ut_executables(ut_executable(user, 'ut_example_tests', 'invalid setup name', ut_utils.gc_after_each)); ut_example_tests.g_char := 'x'; ut_example_tests.g_char2 := 'x'; --Act diff --git a/old_tests/ut_test/ut_test.AfterEachProcedureNameNull.sql b/old_tests/ut_test/ut_test.AfterEachProcedureNameNull.sql index eefdcd583..df6602b37 100644 --- a/old_tests/ut_test/ut_test.AfterEachProcedureNameNull.sql +++ b/old_tests/ut_test/ut_test.AfterEachProcedureNameNull.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_passing_test' ); begin - simple_test.after_each_list := ut_executables(ut_executable(simple_test, '', ut_utils.gc_after_each)); + simple_test.after_each_list := ut_executables(ut_executable(user, 'ut_example_tests', '', ut_utils.gc_after_each)); --Act simple_test.do_execute(); --Assert diff --git a/old_tests/ut_test/ut_test.BeforeEachExecuted.sql b/old_tests/ut_test/ut_test.BeforeEachExecuted.sql index 0dc0f236e..2345e9c1e 100644 --- a/old_tests/ut_test/ut_test.BeforeEachExecuted.sql +++ b/old_tests/ut_test/ut_test.BeforeEachExecuted.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_passing_test' ); begin - simple_test.before_each_list := ut_executables(ut_executable(simple_test, 'beforeeach', ut_utils.gc_before_each)); + simple_test.before_each_list := ut_executables(ut_executable(user, 'ut_example_tests', 'beforeeach', ut_utils.gc_before_each)); ut_example_tests.g_number2 := null; --Act simple_test.do_execute(); diff --git a/old_tests/ut_test/ut_test.BeforeEachProcedureNameInvalid.sql b/old_tests/ut_test/ut_test.BeforeEachProcedureNameInvalid.sql index d6d00bcfa..6180ab0d5 100644 --- a/old_tests/ut_test/ut_test.BeforeEachProcedureNameInvalid.sql +++ b/old_tests/ut_test/ut_test.BeforeEachProcedureNameInvalid.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_exampletest' ); begin - simple_test.before_each_list := ut_executables(ut_executable(simple_test, 'invalid setup name', ut_utils.gc_before_each)); + simple_test.before_each_list := ut_executables(ut_executable(user, 'ut_example_tests', 'invalid setup name', ut_utils.gc_before_each)); ut_example_tests.g_char2 := null; --Act simple_test.do_execute(); diff --git a/old_tests/ut_test/ut_test.BeforeEachProcedureNameNull.sql b/old_tests/ut_test/ut_test.BeforeEachProcedureNameNull.sql index cc7fed6c7..5b37c47de 100644 --- a/old_tests/ut_test/ut_test.BeforeEachProcedureNameNull.sql +++ b/old_tests/ut_test/ut_test.BeforeEachProcedureNameNull.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_passing_test' ); begin - simple_test.before_each_list := ut_executables(ut_executable(simple_test, '', ut_utils.gc_before_each)); + simple_test.before_each_list := ut_executables(ut_executable(user, 'ut_example_tests', '', ut_utils.gc_before_each)); ut_example_tests.g_number2 := null; --Act simple_test.do_execute(); diff --git a/old_tests/ut_test/ut_test.SetupExecutedBeforeTest.sql b/old_tests/ut_test/ut_test.SetupExecutedBeforeTest.sql index b1dfb86ea..3ad4f050c 100644 --- a/old_tests/ut_test/ut_test.SetupExecutedBeforeTest.sql +++ b/old_tests/ut_test/ut_test.SetupExecutedBeforeTest.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_passing_test' ); begin - simple_test.before_test_list := ut_executables(ut_executable(simple_test, 'setup', ut_utils.gc_before_test)); + simple_test.before_test_list := ut_executables(ut_executable(user, 'ut_example_tests', 'setup', ut_utils.gc_before_test)); ut_example_tests.g_number := null; --Act simple_test.do_execute(); diff --git a/old_tests/ut_test/ut_test.SetupProcedureNameInvalid.sql b/old_tests/ut_test/ut_test.SetupProcedureNameInvalid.sql index 4a4c5d385..527083f15 100644 --- a/old_tests/ut_test/ut_test.SetupProcedureNameInvalid.sql +++ b/old_tests/ut_test/ut_test.SetupProcedureNameInvalid.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_exampletest' ); begin - simple_test.before_test_list := ut_executables(ut_executable(simple_test, 'invalid setup name', ut_utils.gc_before_test)); + simple_test.before_test_list := ut_executables(ut_executable(user, 'ut_example_tests', 'invalid setup name', ut_utils.gc_before_test)); ut_example_tests.g_char := null; --Act simple_test.do_execute(); diff --git a/old_tests/ut_test/ut_test.SetupProcedureNameNull.sql b/old_tests/ut_test/ut_test.SetupProcedureNameNull.sql index bb0352bdb..c0159386d 100644 --- a/old_tests/ut_test/ut_test.SetupProcedureNameNull.sql +++ b/old_tests/ut_test/ut_test.SetupProcedureNameNull.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_passing_test' ); begin - simple_test.before_test_list := ut_executables(ut_executable(simple_test, null, ut_utils.gc_before_test)); + simple_test.before_test_list := ut_executables(ut_executable(user, 'ut_example_tests', null, ut_utils.gc_before_test)); ut_example_tests.g_number := null; --Act simple_test.do_execute(); diff --git a/old_tests/ut_test/ut_test.TeardownExecutedAfterTest.sql b/old_tests/ut_test/ut_test.TeardownExecutedAfterTest.sql index 112aa8735..d45bf7404 100644 --- a/old_tests/ut_test/ut_test.TeardownExecutedAfterTest.sql +++ b/old_tests/ut_test/ut_test.TeardownExecutedAfterTest.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_passing_test' ); begin - simple_test.after_test_list := ut_executables(ut_executable(simple_test, 'teardown', ut_utils.gc_after_test)); + simple_test.after_test_list := ut_executables(ut_executable(user, 'ut_example_tests', 'teardown', ut_utils.gc_after_test)); --Act simple_test.do_execute(); --Assert diff --git a/old_tests/ut_test/ut_test.TeardownProcedureNameInvalid.sql b/old_tests/ut_test/ut_test.TeardownProcedureNameInvalid.sql index c74d1536f..84b3c262e 100644 --- a/old_tests/ut_test/ut_test.TeardownProcedureNameInvalid.sql +++ b/old_tests/ut_test/ut_test.TeardownProcedureNameInvalid.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_exampletest' ); begin - simple_test.after_test_list := ut_executables(ut_executable(simple_test, 'invalid setup name', ut_utils.gc_after_test)); + simple_test.after_test_list := ut_executables(ut_executable(user, 'ut_example_tests', 'invalid setup name', ut_utils.gc_after_test)); ut_example_tests.g_char := 'x'; --Act simple_test.do_execute(); diff --git a/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql b/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql index 6bfdeb73b..2c2e18e76 100644 --- a/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql +++ b/old_tests/ut_test/ut_test.TeardownProcedureNameNull.sql @@ -7,7 +7,7 @@ declare ,a_name => 'ut_passing_test' ); begin - simple_test.after_test_list := ut_executables(ut_executable(simple_test, null, ut_utils.gc_after_test)); + simple_test.after_test_list := ut_executables(ut_executable(user, 'ut_example_tests', null, ut_utils.gc_after_test)); --Act simple_test.do_execute(); --Assert diff --git a/source/core/types/ut_executable.tpb b/source/core/types/ut_executable.tpb index 87c1cb533..e51440408 100644 --- a/source/core/types/ut_executable.tpb +++ b/source/core/types/ut_executable.tpb @@ -17,13 +17,14 @@ create or replace type body ut_executable is */ constructor function ut_executable( - self in out nocopy ut_executable, a_context ut_suite_item, + self in out nocopy ut_executable, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_associated_event_name varchar2 ) return self as result is begin + self.self_type := $$plsql_unit; self.associated_event_name := a_associated_event_name; - self.owner_name := a_context.object_owner; - self.object_name := a_context.object_name; + self.owner_name := a_owner; + self.object_name := a_package; self.procedure_name := a_procedure_name; return; end; diff --git a/source/core/types/ut_executable.tps b/source/core/types/ut_executable.tps index 50b71d323..79813c391 100644 --- a/source/core/types/ut_executable.tps +++ b/source/core/types/ut_executable.tps @@ -25,7 +25,7 @@ create or replace type ut_executable under ut_event_item( error_backtrace varchar2(4000), error_stack varchar2(4000), serveroutput clob, - constructor function ut_executable( self in out nocopy ut_executable, a_context ut_suite_item, a_procedure_name varchar2, a_associated_event_name varchar2) return self as result, + constructor function ut_executable( self in out nocopy ut_executable, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_associated_event_name varchar2) return self as result, member function is_valid(self in out nocopy ut_executable) return boolean, member function form_name return varchar2, member procedure do_execute(self in out nocopy ut_executable, a_item in out nocopy ut_suite_item), diff --git a/source/core/types/ut_executable_test.tpb b/source/core/types/ut_executable_test.tpb index 53b98157a..d530c5d12 100644 --- a/source/core/types/ut_executable_test.tpb +++ b/source/core/types/ut_executable_test.tpb @@ -1,12 +1,13 @@ create or replace type body ut_executable_test as constructor function ut_executable_test( - self in out nocopy ut_executable_test, a_context ut_suite_item, + self in out nocopy ut_executable_test, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_associated_event_name varchar2 ) return self as result is begin + self.self_type := $$plsql_unit; self.associated_event_name := a_associated_event_name; - self.owner_name := a_context.object_owner; - self.object_name := a_context.object_name; + self.owner_name := a_owner; + self.object_name := a_package; self.procedure_name := a_procedure_name; return; end; diff --git a/source/core/types/ut_executable_test.tps b/source/core/types/ut_executable_test.tps index c4c865db7..af3cb1923 100644 --- a/source/core/types/ut_executable_test.tps +++ b/source/core/types/ut_executable_test.tps @@ -16,7 +16,7 @@ create or replace type ut_executable_test authid current_user under ut_executabl limitations under the License. */ constructor function ut_executable_test( - self in out nocopy ut_executable_test, a_context ut_suite_item, + self in out nocopy ut_executable_test, a_owner varchar2, a_package varchar2, a_procedure_name varchar2, a_associated_event_name varchar2 ) return self as result, diff --git a/source/core/types/ut_logical_suite.tpb b/source/core/types/ut_logical_suite.tpb index 870b31bcf..a743e8d25 100644 --- a/source/core/types/ut_logical_suite.tpb +++ b/source/core/types/ut_logical_suite.tpb @@ -51,11 +51,11 @@ create or replace type body ut_logical_suite as self.calc_execution_result(); end; - overriding member procedure set_default_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer) is + overriding member procedure set_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer) is begin self.rollback_type := coalesce(self.rollback_type, a_rollback_type); for i in 1 .. self.items.count loop - self.items(i).set_default_rollback_type(a_rollback_type); + self.items(i).set_rollback_type(self.rollback_type); end loop; end; diff --git a/source/core/types/ut_logical_suite.tps b/source/core/types/ut_logical_suite.tps index f30a6f4a3..e9f260035 100644 --- a/source/core/types/ut_logical_suite.tps +++ b/source/core/types/ut_logical_suite.tps @@ -27,7 +27,7 @@ create or replace type ut_logical_suite under ut_suite_item ( member function is_valid(self in out nocopy ut_logical_suite) return boolean, member procedure add_item(self in out nocopy ut_logical_suite, a_item ut_suite_item), overriding member procedure mark_as_skipped(self in out nocopy ut_logical_suite), - overriding member procedure set_default_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer), + overriding member procedure set_rollback_type(self in out nocopy ut_logical_suite, a_rollback_type integer), overriding member function do_execute(self in out nocopy ut_logical_suite) return boolean, overriding member procedure calc_execution_result(self in out nocopy ut_logical_suite), overriding member procedure mark_as_errored(self in out nocopy ut_logical_suite, a_error_stack_trace varchar2), diff --git a/source/core/types/ut_suite.tpb b/source/core/types/ut_suite.tpb index ba2599aa4..7b5525664 100644 --- a/source/core/types/ut_suite.tpb +++ b/source/core/types/ut_suite.tpb @@ -16,10 +16,12 @@ create or replace type body ut_suite as limitations under the License. */ - constructor function ut_suite (self in out nocopy ut_suite , a_object_owner varchar2, a_object_name varchar2) return self as result is + constructor function ut_suite ( + self in out nocopy ut_suite, a_object_owner varchar2, a_object_name varchar2, a_suite_name varchar2 := null + ) return self as result is begin self.self_type := $$plsql_unit; - self.init(a_object_owner, a_object_name, a_object_name); + self.init(a_object_owner, a_object_name, nvl(a_suite_name, a_object_name)); self.items := ut_suite_items(); before_all_list := ut_executables(); after_all_list := ut_executables(); diff --git a/source/core/types/ut_suite.tps b/source/core/types/ut_suite.tps index dfa55bda6..c80316172 100644 --- a/source/core/types/ut_suite.tps +++ b/source/core/types/ut_suite.tps @@ -26,7 +26,9 @@ create or replace type ut_suite under ut_logical_suite ( * Procedure exists within the package of the suite */ after_all_list ut_executables, - constructor function ut_suite (self in out nocopy ut_suite , a_object_owner varchar2, a_object_name varchar2) return self as result, + constructor function ut_suite ( + self in out nocopy ut_suite, a_object_owner varchar2, a_object_name varchar2, a_suite_name varchar2 := null + ) return self as result, overriding member function do_execute(self in out nocopy ut_suite) return boolean, overriding member function get_error_stack_traces(self ut_suite) return ut_varchar2_list, overriding member function get_serveroutputs return clob diff --git a/source/core/types/ut_suite_item.tpb b/source/core/types/ut_suite_item.tpb index d782a8a71..5325be9de 100644 --- a/source/core/types/ut_suite_item.tpb +++ b/source/core/types/ut_suite_item.tpb @@ -37,12 +37,17 @@ create or replace type body ut_suite_item as return ut_utils.int_to_boolean(self.disabled_flag); end; - member procedure set_default_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer) is + member procedure set_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer) is begin self.rollback_type := coalesce(self.rollback_type, a_rollback_type); end; - final member procedure do_execute(self in out nocopy ut_suite_item) is + member function get_rollback_type return integer is + begin + return nvl(self.rollback_type, ut_utils.gc_rollback_default); + end; + +final member procedure do_execute(self in out nocopy ut_suite_item) is l_completed_without_errors boolean; begin l_completed_without_errors := self.do_execute(); @@ -51,7 +56,7 @@ create or replace type body ut_suite_item as member function create_savepoint_if_needed return varchar2 is l_savepoint varchar2(30); begin - if self.rollback_type = ut_utils.gc_rollback_auto then + if get_rollback_type() = ut_utils.gc_rollback_auto then l_savepoint := ut_utils.gen_savepoint_name(); execute immediate 'savepoint ' || l_savepoint; end if; @@ -62,7 +67,7 @@ create or replace type body ut_suite_item as ex_savepoint_not_exists exception; pragma exception_init(ex_savepoint_not_exists, -1086); begin - if self.rollback_type = ut_utils.gc_rollback_auto and a_savepoint is not null then + if get_rollback_type() = ut_utils.gc_rollback_auto and a_savepoint is not null then execute immediate 'rollback to ' || a_savepoint; end if; exception diff --git a/source/core/types/ut_suite_item.tps b/source/core/types/ut_suite_item.tps index b78a2f3a7..6a36d88ee 100644 --- a/source/core/types/ut_suite_item.tps +++ b/source/core/types/ut_suite_item.tps @@ -1,4 +1,4 @@ -create or replace type ut_suite_item force under ut_suite_item_base ( +create or replace type ut_suite_item force under ut_event_item ( /* utPLSQL - Version 3 Copyright 2016 - 2017 utPLSQL Project @@ -16,13 +16,48 @@ create or replace type ut_suite_item force under ut_suite_item_base ( limitations under the License. */ + /** + * owner of the database object (package) + */ + object_owner varchar2(4000 byte), + /** + * name of the database object (package) + */ + object_name varchar2(4000 byte), + /** + * Name of the object (suite, sub-suite, test) + */ + name varchar2(4000 byte), + /** + * Description fo the suite item (as given by the annotation) + */ + description varchar2(4000 byte), + + /** + * Full path of the invocation of the item (including the items name itself) + */ + path varchar2(4000 byte), + /** + * The type of the rollback behavior + */ + rollback_type integer(1), + /** + * Indicates if the test is to be disabled by execution + */ + disabled_flag integer(1), + --execution result fields + start_time timestamp with time zone, + end_time timestamp with time zone, + result integer(1), + warnings ut_varchar2_list, results_count ut_results_counter, transaction_invalidators ut_varchar2_list, member procedure init(self in out nocopy ut_suite_item, a_object_owner varchar2, a_object_name varchar2, a_name varchar2), member procedure set_disabled_flag(self in out nocopy ut_suite_item, a_disabled_flag boolean), member function get_disabled_flag return boolean, not instantiable member procedure mark_as_skipped(self in out nocopy ut_suite_item), - member procedure set_default_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer), + member procedure set_rollback_type(self in out nocopy ut_suite_item, a_rollback_type integer), + member function get_rollback_type return integer, member function create_savepoint_if_needed return varchar2, member procedure rollback_to_savepoint(self in out nocopy ut_suite_item, a_savepoint varchar2), member function get_transaction_invalidators return ut_varchar2_list, diff --git a/source/core/types/ut_suite_item_base.tps b/source/core/types/ut_suite_item_base.tps deleted file mode 100644 index e4288b71f..000000000 --- a/source/core/types/ut_suite_item_base.tps +++ /dev/null @@ -1,56 +0,0 @@ -create or replace type ut_suite_item_base under ut_event_item ( - /* - utPLSQL - Version 3 - 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. - */ - - /** - * owner of the database object (package) - */ - object_owner varchar2(4000 byte), - /** - * name of the database object (package) - */ - object_name varchar2(4000 byte), - /** - * Name of the object (suite, sub-suite, test) - */ - name varchar2(4000 byte), - /** - * Description fo the suite item (as given by the annotation) - */ - description varchar2(4000 byte), - - /** - * Full path of the invocation of the item (including the items name itself) - */ - path varchar2(4000 byte), - /** - * The type of the rollback behavior - */ - rollback_type integer(1), - /** - * Indicates if the test is to be disabled by execution - */ - disabled_flag integer(1), - --execution result fields - start_time timestamp with time zone, - end_time timestamp with time zone, - result integer(1), - warnings ut_varchar2_list - -) -not final not instantiable -/ diff --git a/source/core/types/ut_test.tpb b/source/core/types/ut_test.tpb index b835f0413..db589d685 100644 --- a/source/core/types/ut_test.tpb +++ b/source/core/types/ut_test.tpb @@ -23,7 +23,7 @@ create or replace type body ut_test as begin self.self_type := $$plsql_unit; self.init(a_object_owner, a_object_name, a_name); - self.item := ut_executable_test(self, a_name, ut_utils.gc_test_execute); + self.item := ut_executable_test(a_object_owner, a_object_name, a_name, ut_utils.gc_test_execute); self.before_each_list := ut_executables(); self.before_test_list := ut_executables(); self.after_test_list := ut_executables(); diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index a0365221f..c0eb08392 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -18,7 +18,7 @@ create or replace package body ut_suite_builder is subtype t_annotation_text is varchar2(4000); subtype t_annotation_name is varchar2(4000); - subtype t_procedure_name is varchar2(500); + subtype t_object_name is varchar2(500); subtype t_annotation_position is binary_integer; @@ -31,23 +31,28 @@ create or replace package body ut_suite_builder is -- when you'd like to have two beforetest procedures executed in a single test type tt_annotation_texts is table of t_annotation_text index by t_annotation_position; - type tt_annotations_by_name is table of tt_annotation_texts index by t_annotation_name; - - type t_object_annotation is record( - text varchar2(4000), - name varchar2(4000), - procedure_name varchar2(500), - procedure_annotations tt_annotations_by_name + type tt_procedure_annotations is table of tt_annotation_texts index by t_annotation_name; + + --holds information about + -- package level annotation + -- or + -- procedure and annotations associated with the procedure + type t_package_annotation is record( + text t_annotation_text, + name t_annotation_name, + procedure_name t_object_name, + procedure_annotations tt_procedure_annotations ); - --holds a list of package level annotations indexed (order) by position. - type tt_object_annotations is table of t_object_annotation index by t_annotation_position; + --holds a list of package and procedure level annotations indexed (order) by position. + --procedure level annotations are grouped under procedure name + type tt_package_annotations is table of t_package_annotation index by t_annotation_position; --holds all annotations for object type t_package_annotations_info is record( - owner t_procedure_name, - name t_procedure_name, - annotations tt_object_annotations + owner t_object_name, + name t_object_name, + annotations tt_package_annotations ); --list of all package level annotation positions for a given annotaion name @@ -60,8 +65,8 @@ create or replace package body ut_suite_builder is return a_index = a_annotations.count or a_annotations(a_index).subobject_name != nvl(a_annotations(a_index+1).subobject_name, ' '); end; - function get_procedure_annotations(a_annotations ut_annotations, a_index binary_integer) return tt_annotations_by_name is - l_result tt_annotations_by_name; + function get_procedure_annotations(a_annotations ut_annotations, a_index binary_integer) return tt_procedure_annotations is + l_result tt_procedure_annotations; i binary_integer := a_index; begin loop @@ -72,7 +77,7 @@ create or replace package body ut_suite_builder is return l_result; end; - function convert_object_annotations(a_object ut_annotated_object) return t_package_annotations_info is + function convert_package_annotations(a_object ut_annotated_object) return t_package_annotations_info is l_result t_package_annotations_info; l_annotation_no binary_integer; begin @@ -95,7 +100,7 @@ create or replace package body ut_suite_builder is return l_result; end; - function build_annotation_index(a_annotations tt_object_annotations ) return tt_annotations_index is + function build_annotation_index(a_annotations tt_package_annotations ) return tt_annotations_index is l_result tt_annotations_index; i binary_integer; begin @@ -133,8 +138,6 @@ create or replace package body ut_suite_builder is case lower(a_rollback_type_name) when 'manual' then ut_utils.gc_rollback_manual when 'auto' then ut_utils.gc_rollback_auto - --TODO - if invalid or no rollback type text specified - give a warning - else ut_utils.gc_rollback_auto end; return l_rollback_type; end; @@ -169,60 +172,81 @@ create or replace package body ut_suite_builder is end; procedure add_to_list( - a_executables in out nocopy ut_executables, - a_procedure_name varchar2, - a_event_name ut_utils.t_event_name, - a_suite_item ut_suite_item + a_executables in out nocopy ut_executables, + a_owner varchar2, + a_package_name varchar2, + a_procedure_name varchar2, + a_executable_type ut_utils.t_executable_type ) is begin if a_executables is null then a_executables := ut_executables(); end if; a_executables.extend; - a_executables(a_executables.last) := ut_executable(a_suite_item, a_procedure_name, a_event_name); + a_executables(a_executables.last) := ut_executable(a_owner, a_package_name, a_procedure_name, a_executable_type); end; - procedure add_to_list( + procedure add_all_to_list( a_executables in out nocopy ut_executables, + a_owner varchar2, + a_package_name varchar2, a_annotation_texts tt_annotation_texts, - a_event_name ut_utils.t_event_name, - a_suite_item ut_suite_item + a_event_name ut_utils.t_event_name ) is l_annotation_pos binary_integer; begin l_annotation_pos := a_annotation_texts.first; while l_annotation_pos is not null loop - add_to_list(a_executables, a_annotation_texts(l_annotation_pos), a_event_name, a_suite_item ); + add_to_list(a_executables, a_owner, a_package_name, a_annotation_texts(l_annotation_pos), a_event_name ); l_annotation_pos := a_annotation_texts.next( l_annotation_pos); end loop; end; - procedure warning_on_extra_annotations( - a_suite in out nocopy ut_suite_item, - a_procedure_info t_object_annotation, + procedure duplicate_annotations_warning( + a_suite in out nocopy ut_suite_item, + a_annoations tt_annotations_index, a_for_annotation varchar2 ) is l_annotation_name t_annotation_name; l_warning varchar2(32767); begin - l_annotation_name := a_procedure_info.procedure_annotations.first; + if a_annoations.exists(a_for_annotation) then + if a_annoations(a_for_annotation).count > 1 then + a_suite.put_warning( + 'Multiple occurrences of annotation "--%'||a_for_annotation||'" were found. Last occurrence of annotation was used.' + ); + end if; + end if; + end; + + procedure warning_on_extra_annotations( + a_suite in out nocopy ut_suite_item, + a_procedure_name t_object_name, + a_proc_annotations tt_procedure_annotations, + a_for_annotation varchar2, + a_invalid_annotations ut_varchar2_list + ) is + l_annotation_name t_annotation_name; + l_warning varchar2(32767); + begin + l_annotation_name := a_proc_annotations.first; while l_annotation_name is not null loop - l_annotation_name := a_procedure_info.procedure_annotations.next(l_annotation_name); - if l_annotation_name != a_for_annotation then + if l_annotation_name member of a_invalid_annotations then l_warning := l_warning ||'"--%'|| l_annotation_name || '", '; end if; + l_annotation_name := a_proc_annotations.next(l_annotation_name); end loop; if l_warning is not null then a_suite.put_warning( - 'Annotations: '||rtrim(l_warning,', ')||' were ignored for procedure "'||a_procedure_info.procedure_name||'".' || - ' Those annotations cannot be used with annotation:"'||a_for_annotation||'"'); + 'Annotations: '||rtrim(l_warning,', ')||' were ignored for procedure "'||upper(a_procedure_name)||'".' || + ' Those annotations cannot be used with annotation: "--%'||a_for_annotation||'"'); end if; end; procedure add_test( - a_suite in out nocopy ut_suite, + a_suite in out nocopy ut_suite, a_procedure_name varchar2, - a_annotations tt_annotations_by_name + a_annotations tt_procedure_annotations ) is l_test ut_test; l_annotation_texts tt_annotation_texts; @@ -242,13 +266,16 @@ create or replace package body ut_suite_builder is if a_annotations.exists('rollback') then l_annotation_texts := a_annotations('rollback'); l_test.rollback_type := get_rollback_type(l_annotation_texts(l_annotation_texts.last)); + if l_test.rollback_type is null then + a_suite.put_warning('"--%rollback" annotation requires one of values: "auto" or "manual". Annotation ignored.'); + end if; end if; if a_annotations.exists('beforetest') then - add_to_list( l_test.before_test_list, a_annotations('beforetest'), ut_utils.gc_before_test, l_test ); + add_all_to_list( l_test.before_test_list, l_test.object_owner, l_test.object_name, a_annotations('beforetest'), ut_utils.gc_before_test ); end if; if a_annotations.exists('aftertest') then - add_to_list( l_test.after_test_list, a_annotations('aftertest'), ut_utils.gc_after_test, l_test ); + add_all_to_list( l_test.after_test_list, l_test.object_owner, l_test.object_name, a_annotations('aftertest'), ut_utils.gc_after_test ); end if; if a_annotations.exists('throws') then add_to_throws_numbers_list(l_test.expected_error_codes, a_annotations('throws')); @@ -277,122 +304,268 @@ create or replace package body ut_suite_builder is end if; end; - procedure add_procedures_from_annot( - l_annotations tt_object_annotations, - l_suite in out nocopy ut_suite, - l_before_each_list out ut_executables, - l_after_each_list out ut_executables + procedure add_annotated_procedure( + a_procedure_name t_object_name, + a_proc_annotations tt_procedure_annotations, + a_suite in out nocopy ut_suite, + a_before_each_list in out nocopy ut_executables, + a_after_each_list in out nocopy ut_executables + ) is + begin + if a_proc_annotations.exists('test') then + add_test( a_suite, a_procedure_name, a_proc_annotations); + + warning_on_extra_annotations( + a_suite, a_procedure_name, a_proc_annotations, 'test', + ut_varchar2_list('beforeeach', 'aftereach', 'beforeall', 'afterall') + ); + + else + if a_proc_annotations.exists('beforeeach') then + add_to_list( a_before_each_list, a_suite.object_owner, a_suite.object_name, a_procedure_name, ut_utils.gc_before_each ); + --TODO add warning if annotation has text - text ignored + end if; + if a_proc_annotations.exists('aftereach') then + add_to_list( a_after_each_list, a_suite.object_owner, a_suite.object_name, a_procedure_name, ut_utils.gc_after_each ); + --TODO add warning if annotation has text - text ignored + end if; + if a_proc_annotations.exists('beforeall') then + add_to_list( a_suite.before_all_list, a_suite.object_owner, a_suite.object_name, a_procedure_name, ut_utils.gc_before_all ); + --TODO add warning if annotation has text - text ignored + end if; + if a_proc_annotations.exists('afterall') then + add_to_list( a_suite.after_all_list, a_suite.object_owner, a_suite.object_name, a_procedure_name, ut_utils.gc_after_all ); + --TODO add warning if annotation has text - text ignored + end if; + end if; + end; + + procedure add_annotated_procedures( + a_annotations tt_package_annotations, + a_suite in out nocopy ut_suite, + a_before_each_list out ut_executables, + a_after_each_list out ut_executables ) is l_position t_annotation_position; begin - l_before_each_list := ut_executables(); - l_after_each_list := ut_executables(); - l_position := l_annotations.first; + a_before_each_list := ut_executables(); + a_after_each_list := ut_executables(); + l_position := a_annotations.first; while l_position is not null loop - if l_annotations(l_position).procedure_name is not null then - if l_annotations(l_position).procedure_annotations.exists('beforeeach') then - add_to_list( l_before_each_list, l_annotations(l_position).procedure_name, ut_utils.gc_before_each, l_suite ); - warning_on_extra_annotations(l_suite, l_annotations(l_position), 'beforeeach'); - elsif l_annotations(l_position).procedure_annotations.exists('aftereach') then - add_to_list( l_after_each_list, l_annotations(l_position).procedure_name, ut_utils.gc_after_each, l_suite ); - warning_on_extra_annotations(l_suite, l_annotations(l_position), 'aftereach'); - elsif l_annotations(l_position).procedure_annotations.exists('beforeall') then - add_to_list( l_suite.before_all_list, l_annotations(l_position).procedure_name, ut_utils.gc_before_all, l_suite ); - warning_on_extra_annotations(l_suite, l_annotations(l_position), 'beforeall'); - elsif l_annotations(l_position).procedure_annotations.exists('afterall') then - add_to_list( l_suite.after_all_list, l_annotations(l_position).procedure_name, ut_utils.gc_after_all, l_suite ); - warning_on_extra_annotations(l_suite, l_annotations(l_position), 'afterall'); - elsif l_annotations(l_position).procedure_annotations.exists('test') then - add_test( l_suite, l_annotations(l_position).procedure_name, l_annotations(l_position).procedure_annotations); - end if; + if a_annotations(l_position).procedure_name is not null then + add_annotated_procedure( + a_annotations(l_position).procedure_name, + a_annotations(l_position).procedure_annotations, + a_suite, + a_before_each_list, + a_after_each_list + ); end if; - l_position := l_annotations.next(l_position); + l_position := a_annotations.next( l_position); end loop; end; - procedure add_suite_context( + procedure populate_suite_contents( a_suite in out nocopy ut_suite, - a_package_ann_index in out nocopy tt_annotations_index, - a_annotations in out nocopy tt_object_annotations + a_annotations tt_package_annotations, + a_package_ann_index tt_annotations_index, + a_context_name t_object_name := null ) is - begin - null; --- while l_package_ann_index.exists('context') loop --- if l_package_ann_index.exists('endcontext') --- and l_package_ann_index.exists('endcontext').first > l_package_ann_index.exists('context').first then --- null; --- end if; --- end loop; - end; - - function create_suite(a_package t_package_annotations_info) return ut_logical_suite is - l_package_ann_index tt_annotations_index; - l_annotations tt_object_annotations; - l_suite ut_suite; l_before_each_list ut_executables; l_after_each_list ut_executables; l_rollback_type ut_utils.t_rollback_type; l_annotation_text varchar2(32767); + l_object_name t_object_name; begin - l_annotations := a_package.annotations; - l_package_ann_index := build_annotation_index(l_annotations); - if l_package_ann_index.exists('suite') then - --create an incomplete suite - l_suite := ut_suite(a_package.owner, a_package.name); - - l_suite.description := l_annotations(l_package_ann_index('suite').first).text; - --TODO - check that there is only one `suite` annotation defined - if not -> warning - --if l_package_ann_index('suite').count > 1 then ... end if; - - if l_package_ann_index.exists('context') then - if l_package_ann_index.exists('endcontext') then --- add_suite_context( --- l_suite, --- l_package_ann_index('context').first, --- l_package_ann_index('endcontext').first --- ); - l_annotations.delete(l_package_ann_index('context').first, l_package_ann_index('endcontext').first); - delete_from_annotation_index(l_package_ann_index, l_package_ann_index('context').first, l_package_ann_index('endcontext').first); - -- else TODO - add warning about context without endcontext + if a_context_name is not null then + l_object_name := a_suite.object_name||'.'||a_context_name; + else + l_object_name := a_suite.object_name; + end if; + if a_package_ann_index.exists('suitepath') then + l_annotation_text := trim(a_annotations(a_package_ann_index('suitepath').last).text); + if l_annotation_text is not null then + if regexp_like(l_annotation_text,'^((\w|[$#])+\.)*(\w|[$#])+$') then + a_suite.path := l_annotation_text||'.'||l_object_name; + else + a_suite.put_warning('Invalid path value in annotation "--%suitepath('||l_annotation_text||')". Annotation ignored.'); end if; + else + a_suite.put_warning('"--%suitepath" annotation requires a non-empty value. Annotation ignored.'); end if; + duplicate_annotations_warning(a_suite, a_package_ann_index, 'suitepath'); + end if; + a_suite.path := lower(coalesce(a_suite.path, l_object_name)); - if l_package_ann_index.exists('suitepath') then - --TODO - check that there is only one `suitepath` annotation defined - if not -> warning - --TODO - check that the `suitepath` annotation has text in it - if not -> warning - --TODO - check that text of `suitepath` annotation is of valid format - if not -> warning - l_annotation_text := l_annotations(l_package_ann_index('suitepath').first).text; - if l_annotation_text is not null then - l_suite.path := trim(l_annotations(l_package_ann_index('suitepath').first).text)||'.'||a_package.name; - end if; + if a_package_ann_index.exists('displayname') then + l_annotation_text := trim(a_annotations(a_package_ann_index('displayname').last).text); + if l_annotation_text is not null then + a_suite.description := l_annotation_text; + else + a_suite.put_warning('"--%displayname" annotation requires a non-empty value. Annotation ignored.'); end if; - l_suite.path := lower(coalesce(l_suite.path, a_package.name)); + duplicate_annotations_warning(a_suite, a_package_ann_index, 'displayname'); + end if; - if l_package_ann_index.exists('displayname') then - --TODO - check that there is only one `displayname` annotation defined - if not -> warning - l_suite.description := l_annotations(l_package_ann_index('displayname').first).text; + if a_package_ann_index.exists('rollback') then + l_rollback_type := get_rollback_type(a_annotations(a_package_ann_index('rollback').last).text); + if l_rollback_type is null then + a_suite.put_warning('"--%rollback" annotation requires one of values: "auto" or "manual". Annotation ignored.'); end if; + duplicate_annotations_warning(a_suite, a_package_ann_index, 'rollback'); + end if; - if l_package_ann_index.exists('rollback') then - l_rollback_type := get_rollback_type(l_annotations(l_package_ann_index('rollback').first).text); - end if; + a_suite.disabled_flag := ut_utils.boolean_to_int(a_package_ann_index.exists('disabled')); + + --process procedure annotations for suite + add_annotated_procedures(a_annotations, a_suite, l_before_each_list, l_after_each_list); - l_suite.disabled_flag := ut_utils.boolean_to_int(l_package_ann_index.exists('disabled')); + a_suite.set_rollback_type(l_rollback_type); + update_before_after_list(a_suite, l_before_each_list, l_after_each_list); + end; + + + procedure add_suite_contexts( + a_suite in out nocopy ut_suite, + a_annotations in out nocopy tt_package_annotations, + a_package_ann_index in out nocopy tt_annotations_index + ) is + l_context_pos t_annotation_position; + l_end_context_pos t_annotation_position; + l_package_ann_index tt_annotations_index; + l_annotations tt_package_annotations; + l_suite ut_suite; + l_context_no binary_integer := 1; - --process procedure annotations for suite - add_procedures_from_annot(l_annotations, l_suite, l_before_each_list, l_after_each_list); + function get_endcontext_position( + a_context_ann_pos t_annotation_position, + a_package_ann_index in out nocopy tt_annotations_index + ) return t_annotation_position is + l_result t_annotation_position; + begin + if a_package_ann_index.exists('endcontext') then + l_result := a_package_ann_index('endcontext').first; + while l_result <= a_context_ann_pos loop + --TODO add warning about endcontext before context annotation + --remove invalid endcontext + delete_from_annotation_index(a_package_ann_index, l_result, l_result); + --remove the bad endcontext from index + l_result := a_package_ann_index('endcontext').next(l_result); + end loop; + end if; + return l_result; + end; - l_suite.set_default_rollback_type(coalesce(l_rollback_type, ut_utils.gc_rollback_auto)); + function get_annotations_in_context( + a_annotations tt_package_annotations, + a_context_pos t_annotation_position, + a_end_context_pos t_annotation_position + ) return tt_package_annotations is + l_annotations tt_package_annotations; + l_position t_annotation_position; + begin + l_position := a_context_pos; + while l_position is not null and l_position <= a_end_context_pos loop + l_annotations(l_position) := a_annotations(l_position); + l_position := a_annotations.next(l_position); + end loop; + return l_annotations; + end; + + begin + if not a_package_ann_index.exists('context') then + return; end if; + l_context_pos := a_package_ann_index('context').first; + while l_context_pos is not null loop + l_end_context_pos := get_endcontext_position(l_context_pos, a_package_ann_index); + if l_end_context_pos is null then + a_suite.put_warning( + 'Annotation "--%context('||a_annotations(l_context_pos).text||')" was ignored. Cannot find following "--%endcontext".'); + exit; + end if; + + --create a sub-set of annotations to process as sub-suite (context) + l_annotations := get_annotations_in_context(a_annotations, l_context_pos, l_end_context_pos); + l_package_ann_index := build_annotation_index(l_annotations); + + l_suite := ut_suite(a_suite.object_owner, a_suite.object_name, 'context_'||l_context_no); - update_before_after_list(l_suite, l_before_each_list, l_after_each_list); + l_suite.description := l_annotations(l_package_ann_index('context').first).text; + l_suite.description := l_annotations(l_context_pos).text; + duplicate_annotations_warning( l_suite, l_package_ann_index, 'suite' ); + populate_suite_contents( l_suite, l_annotations, l_package_ann_index, 'context_'||l_context_no ); + + a_suite.add_item(l_suite); + + -- remove annotations within context after processing them + a_annotations.delete(l_context_pos, l_end_context_pos); + delete_from_annotation_index(a_package_ann_index, l_context_pos, l_end_context_pos); + + if a_package_ann_index.exists('context') then + l_context_pos := a_package_ann_index('context').next(l_context_pos); + else + l_context_pos := null; + end if; + l_context_no := l_context_no + 1; + end loop; + end; + + procedure warning_on_incomplete_context( + a_suite in out nocopy ut_suite, + a_annotations tt_package_annotations, + a_package_ann_index tt_annotations_index + ) is + l_annotation_pos t_annotation_position; + begin + if a_package_ann_index.exists('context') then + l_annotation_pos := a_package_ann_index('context').first; + while l_annotation_pos is not null loop + a_suite.put_warning( + 'Annotation "--%context('||a_annotations(l_annotation_pos).text||')" was ignored. Cannot find following "--%endcontext".'); + l_annotation_pos := a_package_ann_index('context').next(l_annotation_pos); + end loop; + end if; + if a_package_ann_index.exists('endcontext') then + l_annotation_pos := a_package_ann_index('endcontext').first; + while l_annotation_pos is not null loop + a_suite.put_warning( + 'Annotation "--%endcontext" was ignored. Cannot find preceding "--%context".'); + l_annotation_pos := a_package_ann_index('endcontext').next(l_annotation_pos); + end loop; + end if; + end; + + function create_suite( + a_package_annotations t_package_annotations_info + ) return ut_logical_suite is + l_annotations tt_package_annotations := a_package_annotations.annotations; + l_package_ann_index tt_annotations_index; + l_suite ut_suite; + begin + l_package_ann_index := build_annotation_index(l_annotations); + if l_package_ann_index.exists('suite') then + + --create an incomplete suite + l_suite := ut_suite(a_package_annotations.owner, a_package_annotations.name); + + l_suite.description := l_annotations(l_package_ann_index('suite').last).text; + duplicate_annotations_warning(l_suite, l_package_ann_index, 'suite'); + + add_suite_contexts( l_suite, l_annotations, l_package_ann_index ); + + warning_on_incomplete_context( l_suite, l_annotations, l_package_ann_index ); + + populate_suite_contents( l_suite, l_annotations, l_package_ann_index ); + + end if; return l_suite; end; function create_suite(a_object ut_annotated_object) return ut_logical_suite is begin - return create_suite(convert_object_annotations(a_object)); + return create_suite( convert_package_annotations(a_object) ); end create_suite; function build_suites_hierarchy(a_suites_by_path tt_schema_suites) return tt_schema_suites is diff --git a/source/core/ut_suite_manager.pkb b/source/core/ut_suite_manager.pkb index 93dbc18fe..26f9f6747 100644 --- a/source/core/ut_suite_manager.pkb +++ b/source/core/ut_suite_manager.pkb @@ -309,6 +309,11 @@ create or replace package body ut_suite_manager is l_schema := l_schema_paths.next(l_schema); end loop; + --propagate rollback type to suite items after organizing suites into hierarchy + for i in 1 .. l_objects_to_run.count loop + l_objects_to_run(i).set_rollback_type( l_objects_to_run(i).get_rollback_type() ); + end loop; + return l_objects_to_run; end configure_execution_by_path; diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 72c47e5ea..2f0c784d5 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -72,6 +72,7 @@ create or replace package ut_utils authid definer is subtype t_rollback_type is binary_integer range 0 .. 1; gc_rollback_auto constant t_rollback_type := 0; -- rollback after each test and suite gc_rollback_manual constant t_rollback_type := 1; -- leave transaction control manual + gc_rollback_default constant t_rollback_type := gc_rollback_auto; ex_unsupported_rollback_type exception; gc_unsupported_rollback_type constant pls_integer := -20200; diff --git a/source/install.sql b/source/install.sql index 133c44b45..1e524e4f8 100644 --- a/source/install.sql +++ b/source/install.sql @@ -60,7 +60,6 @@ alter session set current_schema = &&ut3_owner; @@install_component.sql 'core/types/ut_expectation_result.tps' @@install_component.sql 'core/types/ut_expectation_results.tps' @@install_component.sql 'core/types/ut_results_counter.tps' -@@install_component.sql 'core/types/ut_suite_item_base.tps' @@install_component.sql 'core/types/ut_suite_item.tps' @@install_component.sql 'core/types/ut_suite_items.tps' @@install_component.sql 'core/types/ut_executable.tps' diff --git a/source/uninstall.sql b/source/uninstall.sql index 5fe859819..1dfacf81e 100644 --- a/source/uninstall.sql +++ b/source/uninstall.sql @@ -230,8 +230,6 @@ drop type ut_suite_items force; drop type ut_suite_item force; -drop type ut_suite_item_base force; - drop type ut_output_table_buffer force; drop type ut_output_buffer_base force; diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb index 472c29251..df9600b94 100644 --- a/test/core/test_suite_builder.pkb +++ b/test/core/test_suite_builder.pkb @@ -19,13 +19,490 @@ create or replace package body test_suite_builder is l_actual clob; l_annotations ut3.ut_annotations; begin + --Arrange l_annotations := ut3.ut_annotations( ut3.ut_annotation(1, 'suite',null, null) ); + --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); - ut.expect(l_actual).to_be_like('%UT3_TESTERsome_packagesome_package%'); + --Assert + ut.expect(l_actual).to_be_like( + '%UT3_TESTERsome_packagesome_package%' + ); + end; + + procedure suite_description_from_suite is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Some description', null), + ut3.ut_annotation(2, 'suite','Another description', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%some_packageAnother description%' + ); + end; + + procedure suitepath_from_non_empty_path is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite',null, null), + ut3.ut_annotation(2, 'suitepath','dummy.utplsql.some', null), + ut3.ut_annotation(3, 'suitepath','org.utplsql.some', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%org.utplsql.some%' + ); + end; + + procedure suite_descr_from_displayname is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Some description', null), + ut3.ut_annotation(2, 'suite','Another description', null), + ut3.ut_annotation(3, 'displayname','New description', null), + ut3.ut_annotation(4, 'displayname','Newest description', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%some_packageNewest description%' + ); + end; + + procedure rollback_type_valid is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite',null, null), + ut3.ut_annotation(2, 'rollback','bad', null), + ut3.ut_annotation(3, 'rollback','manual', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%'||ut3.ut_utils.gc_rollback_manual||'%' + ); + end; + + procedure rollback_type_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite',null, null), + ut3.ut_annotation(2, 'rollback','bad', null), + ut3.ut_annotation(3, 'rollback','manual', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%rollback"%%' + ); + end; + + procedure suite_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Blah', null), + ut3.ut_annotation(2, 'suite','Cool', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%suite"%%' + ); end; + procedure suitepath_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'suitepath','dummy.utplsql.some', null), + ut3.ut_annotation(3, 'suitepath','org.utplsql.some', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%suitepath"%%' + ); + end; + + procedure displayname_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(3, 'displayname','New description', null), + ut3.ut_annotation(4, 'displayname','Newest description', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%displayname"%%' + ); + end; + + procedure suitepath_annot_empty is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(3, 'suitepath',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%suitepath" annotation requires a non-empty value.%%' + ); + end; + + procedure suitepath_annot_invalid_path is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'suitepath','path with spaces', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%Invalid path value in annotation "--%suitepath(path with spaces)"%%' + ); + end; + + procedure displayname_annot_empty is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(3, 'displayname',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%displayname" annotation requires a non-empty value.%%' + ); + end; + + procedure rollback_type_empty is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(3, 'rollback',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%rollback" annotation requires one of values:%%' + ); + end; + + procedure rollback_type_invalid is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'rollback','bad', null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%"--%rollback" annotation requires one of values:%%' + ); + end; + + procedure multiple_before_after is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'beforeall',null, 'first_before_all'), + ut3.ut_annotation(3, 'beforeall',null, 'another_before_all'), + ut3.ut_annotation(4, 'beforeeach',null, 'first_bfore_each'), + ut3.ut_annotation(5, 'beforeeach',null, 'another_before_each'), + ut3.ut_annotation(6, 'aftereach',null, 'first_after_each'), + ut3.ut_annotation(7, 'aftereach',null, 'another_after_each'), + ut3.ut_annotation(8, 'afterall',null, 'first_after_all'), + ut3.ut_annotation(9, 'afterall',null, 'another_after_all'), + ut3.ut_annotation(10, 'test','A test', 'some_test'), + ut3.ut_annotation(11, 'beforetest','before_test_proc', 'some_test'), + ut3.ut_annotation(12, 'beforetest','before_test_proc2', 'some_test'), + ut3.ut_annotation(13, 'aftertest','after_test_proc', 'some_test'), + ut3.ut_annotation(14, 'aftertest','after_test_proc2', 'some_test') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%some_package%some_test' || + '%' || + '%some_packagefirst_bfore_each' || + '%some_packageanother_before_each' || + '%' || + '%' || + '%some_packagebefore_test_proc' || + '%some_packagebefore_test_proc2' || + '%' || + '%' || + '%some_packageafter_test_proc' || + '%some_packageafter_test_proc2' || + '%' || + '%' || + '%some_packagefirst_after_each' || + '%some_packageanother_after_each' || + '%' || + '%' || + '%' || + '%some_packagefirst_before_all' || + '%some_packageanother_before_all' || + '%' || + '%' || + '%some_packagefirst_after_all' || + '%some_packageanother_after_all' || + '%%' + ); + end; + + procedure before_after_on_single_proc is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'beforeall',null, 'do_stuff'), + ut3.ut_annotation(3, 'beforeeach',null, 'do_stuff'), + ut3.ut_annotation(4, 'aftereach',null, 'do_stuff'), + ut3.ut_annotation(5, 'afterall',null, 'do_stuff'), + ut3.ut_annotation(6, 'test','A test', 'some_test') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%%some_package%some_test' || + '%' || + '%some_packagedo_stuff' || + '%' || + '%' || + '%some_packagedo_stuff' || + '%' || + '%' || + '%' || + '%some_packagedo_stuff' || + '%' || + '%' || + '%some_packagedo_stuff' || + '%%' + ); + end; + + procedure before_after_mixed_with_test is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'beforeall',null, 'do_stuff'), + ut3.ut_annotation(3, 'beforeeach',null, 'do_stuff'), + ut3.ut_annotation(4, 'aftereach',null, 'do_stuff'), + ut3.ut_annotation(5, 'afterall',null, 'do_stuff'), + ut3.ut_annotation(6, 'test','A test', 'do_stuff') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%UT_SUITEUT3_TESTER' || + 'some_packagesome_packageCool' || + '%Annotations: "--%afterall", "--%aftereach", "--%beforeall", "--%beforeeach"' || + ' were ignored for procedure "DO_STUFF".' || + ' Those annotations cannot be used with annotation: "--%test"%'|| + '%%some_package%do_stuff' || + '%' || + '%' || + '%' || + '%%' + ); + end; + + procedure suite_from_context is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'beforeall',null, 'suite_level_beforeall'), + ut3.ut_annotation(3, 'test','In suite', 'suite_level_test'), + ut3.ut_annotation(4, 'context','A context', null), + ut3.ut_annotation(5, 'beforeall',null, 'context_setup'), + ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context'), + ut3.ut_annotation(8, 'endcontext',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%' || + '%' || + '' || + '%context_1A contextsome_package.context_1' || + '%' || + '' || + '%test_in_a_contextIn contextsome_package.context_1.test_in_a_context' || + '%' || + '' || + '' || + '%some_packagecontext_setup' || + '%' || + '' || + '' || + '' || + '%suite_level_testIn suitesome_package.suite_level_test' || + '%' || + '' || + '' || + '%some_packagesuite_level_beforeall' || + '%' || + '' || + '' + ); + end; + + procedure context_without_endcontext is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'beforeall',null, 'suite_level_beforeall'), + ut3.ut_annotation(3, 'test','In suite', 'suite_level_test'), + ut3.ut_annotation(4, 'context','A context', null), + ut3.ut_annotation(5, 'beforeall',null, 'context_setup'), + ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%Annotation "--%context(A context)" was ignored. Cannot find following "--%endcontext".' || + '%' || + '' || + '%suite_level_testIn suitesome_package.suite_level_test' || + '%' || + '' || + '%test_in_a_contextIn contextsome_package.test_in_a_context' || + '%' || + '' || + '' || + '%some_packagesuite_level_beforeall' || + '%some_packagecontext_setup' || + '%' || + '' || + '' + ); + end; + + procedure endcontext_without_context is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'beforeall',null, 'suite_level_beforeall'), + ut3.ut_annotation(3, 'test','In suite', 'suite_level_test'), + ut3.ut_annotation(4, 'context','A context', null), + ut3.ut_annotation(5, 'beforeall',null, 'context_setup'), + ut3.ut_annotation(7, 'test', 'In context', 'test_in_a_context'), + ut3.ut_annotation(8, 'endcontext',null, null), + ut3.ut_annotation(9, 'endcontext',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%' || + '%Annotation "--%endcontext" was ignored. Cannot find preceding "--%context".' || + '%' || + '' || + '%context_1A contextsome_package.context_1' || + '%' || + '' || + '%test_in_a_contextIn contextsome_package.context_1.test_in_a_context' || + '%' || + '' || + '' || + '%some_packagecontext_setup' || + '%' || + '' || + '' || + '' || + '%suite_level_testIn suitesome_package.suite_level_test' || + '%' || + '' || + '' || + '%some_packagesuite_level_beforeall' || + '%' || + '' || + '' + ); + end; end; / diff --git a/test/core/test_suite_builder.pks b/test/core/test_suite_builder.pks index f130fecc0..d3ea31e47 100644 --- a/test/core/test_suite_builder.pks +++ b/test/core/test_suite_builder.pks @@ -4,5 +4,63 @@ create or replace package test_suite_builder is --%test(Sets suite name from package name and leaves description empty) procedure no_suite_description; + + --%test(Sets suite description using last --%suite annotation) + procedure suite_description_from_suite; + + --%test(Sets suite path using last --%suitepath annotation) + procedure suitepath_from_non_empty_path; + + --%test(Overrides suite description using last --%displayname annotation) + procedure suite_descr_from_displayname; + + --%test(Sets rollback type using last --%rollback annotation) + procedure rollback_type_valid; + + --%test(Gives warning if more than one --%rollback annotation used) + procedure rollback_type_duplicated; + + --%test(Gives warning if more than one --%suite annotation used) + procedure suite_annot_duplicated; + + --%test(Gives warning if more than one --%suitepath annotation used) + procedure suitepath_annot_duplicated; + + --%test(Gives warning if more than one --%displayname annotation used) + procedure displayname_annot_duplicated; + + --%test(Gives warning if --%suitepath annotation has no value) + procedure suitepath_annot_empty; + + --%test(Gives warning if --%suitepath annotation has invalid value) + procedure suitepath_annot_invalid_path; + + --%test(Gives warning if --%displayname annotation has no value) + procedure displayname_annot_empty; + + --%test(Gives warning if --%rollback annotation has no value) + procedure rollback_type_empty; + + --%test(Gives warning if --%rollback annotation has invalid value) + procedure rollback_type_invalid; + + --%test(Supports multiple before/after definitions) + procedure multiple_before_after; + + --%test(Supports before/after all/each annotations on single procedure) + procedure before_after_on_single_proc; + + --%test(Gives warning on before/after all/each annotations mixed with test) + procedure before_after_mixed_with_test; + + --%test(Creates nested suite for content between context/endcontext annotations) + procedure suite_from_context; + + --%test(Does not create context and gives warning when endcontext is missing) + procedure context_without_endcontext; + + --%test(Gives warning when endcontext is missing a preceding context) + procedure endcontext_without_context; + end; / diff --git a/test/core/test_suite_manager.pkb b/test/core/test_suite_manager.pkb index a12190e84..271d4d579 100644 --- a/test/core/test_suite_manager.pkb +++ b/test/core/test_suite_manager.pkb @@ -11,6 +11,7 @@ create or replace package body test_suite_manager is --%suite --%displayname(test_package_1) --%suitepath(tests) + --%rollback(manual) gv_glob_val number; @@ -284,10 +285,12 @@ end test_package_3;]'; when 'tests' then 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.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); 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(3); + ut.expect(l_test2_suite.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); when 'tests2' then ut.expect(l_test1_suite.name).to_equal('test_package_3'); ut.expect(l_test1_suite.items.count).to_equal(3); @@ -318,9 +321,11 @@ end test_package_3;]'; ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); + ut.expect(l_test1_suite.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); 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.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); ut.expect(l_test2_suite.items.count).to_equal(3); end; @@ -345,9 +350,11 @@ end test_package_3;]'; ut.expect(l_test1_suite.name).to_equal('test_package_1'); ut.expect(l_test1_suite.items.count).to_equal(1); + ut.expect(l_test1_suite.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); 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.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); ut.expect(l_test2_suite.items.count).to_equal(3); end; @@ -426,14 +433,17 @@ end test_package_3;]'; 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.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); 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.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); ut.expect(l_test2_suite.items.count).to_equal(1); 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.rollback_type).to_equal(ut3.ut_utils.gc_rollback_manual); ut.expect(l_test_proc.before_test_list.count).to_be_greater_than(0); ut.expect(l_test_proc.after_test_list.count).to_be_greater_than(0); diff --git a/test/core/test_ut_suite.pkb b/test/core/test_ut_suite.pkb index 1e04beb23..87fb820ae 100644 --- a/test/core/test_ut_suite.pkb +++ b/test/core/test_ut_suite.pkb @@ -12,8 +12,8 @@ create or replace package body test_ut_suite is l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); l_suite.path := 'ut_example_tests'; l_suite.disabled_flag := ut3.ut_utils.boolean_to_int(true); - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'set_g_number_0', ut3.ut_utils.gc_before_all)); - l_suite.after_all_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'add_1_to_g_number', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite.after_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_before_all)); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act @@ -34,7 +34,7 @@ create or replace package body test_ut_suite is --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); l_suite.path := 'ut_example_tests'; - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'failing_procedure', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_before_all)); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'set_g_number_0')); --Act l_suite.do_execute(); @@ -54,7 +54,7 @@ create or replace package body test_ut_suite is --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); l_suite.path := 'ut_example_tests'; - l_suite.after_all_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'failing_procedure', ut3.ut_utils.gc_after_all)); + l_suite.after_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_after_all)); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'set_g_number_0')); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); @@ -101,9 +101,9 @@ create or replace package body test_ut_suite is execute immediate 'delete from ut$test_table'; l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_TRANSACTION_CONTROL'); l_suite.path := 'ut_transaction_control'; - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'setup', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_TRANSACTION_CONTROL', 'setup', ut3.ut_utils.gc_before_all)); l_suite.add_item(ut3.ut_test(a_object_owner => USER, a_object_name => 'ut_transaction_control',a_name => a_procedure_name)); - l_suite.set_default_rollback_type(a_rollback_type); + l_suite.set_rollback_type(a_rollback_type); --Act l_suite.do_execute(); diff --git a/test/core/test_ut_test.pkb b/test/core/test_ut_test.pkb index 243aadfe7..c0d8d4fd7 100644 --- a/test/core/test_ut_test.pkb +++ b/test/core/test_ut_test.pkb @@ -12,7 +12,7 @@ create or replace package body test_ut_test is --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); l_suite.path := 'ut_example_tests'; - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); @@ -36,11 +36,11 @@ create or replace package body test_ut_test is --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); l_suite.path := 'ut_example_tests'; - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); l_test := ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number'); - l_test.before_test_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'add_1_to_g_number', ut3.ut_utils.gc_before_test)); - l_test.after_test_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'failing_procedure', ut3.ut_utils.gc_after_test)); + l_test.before_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_before_test)); + l_test.after_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_after_test)); l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act @@ -61,10 +61,10 @@ create or replace package body test_ut_test is begin --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); l_test := ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number'); - l_test.before_each_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'add_1_to_g_number', ut3.ut_utils.gc_before_each)); - l_test.after_each_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'failing_procedure', ut3.ut_utils.gc_after_each)); + l_test.before_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_before_each)); + l_test.after_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_after_each)); l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act @@ -85,10 +85,10 @@ create or replace package body test_ut_test is begin --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); l_test := ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number'); - l_test.before_test_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'failing_procedure', ut3.ut_utils.gc_before_test)); - l_test.after_test_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'add_1_to_g_number', ut3.ut_utils.gc_after_test)); + l_test.before_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_before_test)); + l_test.after_test_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_after_test)); l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act @@ -109,10 +109,10 @@ create or replace package body test_ut_test is begin --Arrange l_suite := ut3.ut_suite(a_object_owner => USER, a_object_name => 'UT_EXAMPLE_TESTS'); - l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'set_g_number_0', ut3.ut_utils.gc_before_all)); + l_suite.before_all_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'set_g_number_0', ut3.ut_utils.gc_before_all)); l_test := ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number'); - l_test.before_each_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'failing_procedure', ut3.ut_utils.gc_before_each)); - l_test.after_each_list := ut3.ut_executables(ut3.ut_executable(l_suite, 'add_1_to_g_number', ut3.ut_utils.gc_after_each)); + l_test.before_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'failing_procedure', ut3.ut_utils.gc_before_each)); + l_test.after_each_list := ut3.ut_executables(ut3.ut_executable(USER, 'UT_EXAMPLE_TESTS', 'add_1_to_g_number', ut3.ut_utils.gc_after_each)); l_suite.add_item(l_test); l_suite.add_item(ut3.ut_test(a_object_name => 'UT_EXAMPLE_TESTS',a_name => 'add_1_to_g_number')); --Act diff --git a/tests/ut/ut.run.ExecutesSuccesfullyAnEmptySuite.sql b/tests/ut/ut.run.ExecutesSuccesfullyAnEmptySuite.sql deleted file mode 100644 index f739d97c8..000000000 --- a/tests/ut/ut.run.ExecutesSuccesfullyAnEmptySuite.sql +++ /dev/null @@ -1,30 +0,0 @@ -set termout off -create or replace package empty_suite as - -- %suite - - procedure not_a_test; -end; -/ -create or replace package body empty_suite as - procedure not_a_test is begin null; end; -end; -/ -set termout on -declare - l_result integer; -begin - select * - into l_result - from table(ut.run('empty_suite',utplsql_test_reporter())); ---Assert - if l_result = ut_utils.tr_error then - :test_result := ut_utils.tr_success; - else - dbms_output.put_line('expected failure of ''empty_suite'' got: '''||ut_utils.test_result_to_char(l_result)||'''' ); - end if; -end; -/ - -set termout off -drop package empty_suite; -set termout on diff --git a/tests/ut_expectations/ut.expect.not_to_equal.anydata.GivesFailureWhenComparingTheSameData.sql b/tests/ut_expectations/ut.expect.not_to_equal.anydata.GivesFailureWhenComparingTheSameData.sql deleted file mode 100644 index fd030a701..000000000 --- a/tests/ut_expectations/ut.expect.not_to_equal.anydata.GivesFailureWhenComparingTheSameData.sql +++ /dev/null @@ -1,17 +0,0 @@ ---Arrange -declare - l_expected department$ := department$('hr'); - l_actual department$ := department$('hr'); - l_result integer; -begin ---Act - ut.expect( anydata.convertObject(l_actual) ).not_to_equal( anydata.convertObject(l_expected) ); - l_result := ut_expectation_processor.get_status(); ---Assert - if l_result = ut_utils.tr_failure then - :test_result := ut_utils.tr_success; - else - dbms_output.put_line('expected: '''||ut_utils.tr_success||''', got: '''||l_result||'''' ); - end if; -end; -/ diff --git a/tests/ut_output_buffer/get_lines.WaitsForMoreDataToAppearForSpecifiedTime.sql b/tests/ut_output_buffer/get_lines.WaitsForMoreDataToAppearForSpecifiedTime.sql deleted file mode 100644 index 6c54e4330..000000000 --- a/tests/ut_output_buffer/get_lines.WaitsForMoreDataToAppearForSpecifiedTime.sql +++ /dev/null @@ -1,24 +0,0 @@ ---Arrange -declare - l_result integer; - l_dummy integer; - l_output ut_output_buffer_base := ut_output_table_buffer(); - l_start_time timestamp := systimestamp; - l_wait_seconds integer := 1; -begin - --Act - l_output.send_line(lpad('a text',4000,',a text')); - - select count(*) into l_dummy from table( l_output.get_lines( a_initial_timeout => 0, a_timeout_sec => l_wait_seconds )); - l_result := round(extract(second from (systimestamp - l_start_time))); - - --Assert - ut.expect(l_result).to_equal(l_wait_seconds); - - if ut_expectation_processor.get_status = ut_utils.tr_success then - :test_result := ut_utils.tr_success; - else - dbms_output.put_line(ut_expectation_processor.get_expectations_results()(1).get_result_clob); - end if; -end; -/ From 93086620eba4c4b80aed2ab1238f9e42dcbad08a Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Thu, 29 Mar 2018 02:39:28 +0100 Subject: [PATCH 08/28] Fixed examples. Moved/fixed some constants. --- ...nExampleComplexSuiteWithCustomReporter.sql | 62 +++++++++---------- .../RunExampleTestSuite.sql | 54 +++++++--------- ...nExampleTestSuiteWithCompositeReporter.sql | 28 +++------ .../RunExampleTestSuiteWithCustomReporter.sql | 40 ++++++------ source/core/ut_utils.pks | 4 +- 5 files changed, 85 insertions(+), 103 deletions(-) diff --git a/examples/developer_examples/RunExampleComplexSuiteWithCustomReporter.sql b/examples/developer_examples/RunExampleComplexSuiteWithCustomReporter.sql index d512c0856..1b9e94bfb 100644 --- a/examples/developer_examples/RunExampleComplexSuiteWithCustomReporter.sql +++ b/examples/developer_examples/RunExampleComplexSuiteWithCustomReporter.sql @@ -13,42 +13,39 @@ set echo off @@ut_custom_reporter.tpb declare - suite1 ut_logical_suite; - suite2 ut_logical_suite; - suite_complex ut_logical_suite; - l_reporter ut_output_reporter_base; - l_listener ut_event_listener; - l_run ut_run; + l_parent_suite ut_logical_suite; + l_suite ut_suite; + l_test ut_test; + l_reporter ut_output_reporter_base; + l_run ut_run; begin - suite1 := ut_logical_suite(a_object_owner=>null, a_object_name => null, a_name => null, a_description => 'Test Suite 1', a_path => null); - - suite1.add_item( - ut_test(a_object_name => 'ut_exampletest' - ,a_name => 'ut_exAmpletest' - ,a_description => 'Example test1' - ,a_before_test_proc_name => 'Setup' - ,a_after_test_proc_name => 'tEardown') - ); - - suite2 := ut_logical_suite(a_object_owner=>null, a_object_name => null, a_name => null, a_description => 'Test Suite 2', a_path => null); - - suite2.add_item( - ut_test( - a_object_name => 'UT_EXAMPLETEST2', - a_name => 'UT_EXAMPLETEST', - a_description => 'Another example test', - a_before_test_proc_name => 'SETUP', - a_after_test_proc_name => 'TEARDOWN') - ); - - suite_complex := ut_logical_suite( a_object_owner=>null, a_object_name => null, a_name => null, a_description => 'Complex Test Suite', a_path => null); - suite_complex.items := ut_suite_items(suite1, suite2); + ut_event_manager.initialize(); + l_parent_suite := ut_logical_suite( a_object_owner=>null, a_object_name => null, a_name => 'complex_test_suite', a_path => null); + + l_suite := ut_suite(user, 'ut_exampletest'); + l_test := ut_test(user, 'ut_exampletest','ut_exAmpletest'); + l_test.description := 'Example test1'; + l_test.before_test_list := ut_executables(ut_executable(user, 'ut_exampletest','Setup',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'ut_exampletest','tEardown',ut_utils.gc_after_test)); + + l_suite.add_item(l_test); + l_parent_suite.add_item(l_suite); + + + l_suite := ut_suite(user, 'ut_exampletest2'); + l_test := ut_test(user, 'UT_EXAMPLETEST2','UT_EXAMPLETEST'); + l_test.before_test_list := ut_executables(ut_executable(user, 'UT_EXAMPLETEST2','SETUP',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'UT_EXAMPLETEST2','TEARDOWN',ut_utils.gc_after_test)); + + l_suite.add_item(l_test); + l_parent_suite.add_item(l_suite); -- provide a reporter to process results l_reporter := ut_custom_reporter(a_tab_size => 2); - l_listener := ut_event_listener(ut_reporters(l_reporter)); - l_run := ut_run(ut_suite_items(suite_complex)); - l_run.do_execute(l_listener); + ut_event_manager.add_listener(l_reporter); + l_run := ut_run(ut_suite_items(l_parent_suite)); + l_run.do_execute(); + ut_event_manager.trigger_event(ut_utils.gc_finalize, l_run); l_reporter.lines_to_dbms_output(); end; / @@ -56,3 +53,4 @@ end; drop type ut_custom_reporter; drop package ut_exampletest; drop package ut_exampletest2; +exec dbms_session.reset_package; diff --git a/examples/developer_examples/RunExampleTestSuite.sql b/examples/developer_examples/RunExampleTestSuite.sql index 8d077e663..a0a470e46 100644 --- a/examples/developer_examples/RunExampleTestSuite.sql +++ b/examples/developer_examples/RunExampleTestSuite.sql @@ -11,43 +11,37 @@ set echo off @@ut_exampletest2.pkb declare - suite ut_logical_suite; - listener ut_event_listener := ut_event_listener(ut_reporters()); - test_item ut_test; - expectation ut_expectation_result; + l_suite ut_logical_suite; + l_test ut_test; + l_expectation ut_expectation_result; begin - suite := ut_logical_suite(a_object_owner=>null, a_object_name => 'ut_exampletest', a_name => null, a_description => 'Test Suite Name',a_path => null); + l_suite := ut_suite(user, 'ut_exampletest'); + l_suite.description := 'Test Suite Name'; + l_test := ut_test(user, 'ut_exampletest','ut_exAmpletest'); + l_test.description := 'Example test1'; + l_test.before_test_list := ut_executables(ut_executable(user, 'ut_exampletest','Setup',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'ut_exampletest','tEardown',ut_utils.gc_after_test)); + l_suite.add_item(l_test); - suite.add_item( - ut_test(a_object_name => 'ut_exampletest' - ,a_name => 'ut_exAmpletest' - ,a_description => 'Example test1' - ,a_before_test_proc_name => 'Setup' - ,a_after_test_proc_name => 'tEardown') - ); + l_test := ut_test(user, 'UT_EXAMPLETEST2','ut_exAmpletest'); + l_test.description := 'Another example test'; + l_test.before_test_list := ut_executables(ut_executable(user, 'UT_EXAMPLETEST2','SETUP',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'UT_EXAMPLETEST2','TEARDOWN',ut_utils.gc_after_test)); + l_suite.add_item(l_test); - suite.add_item( - ut_test( - a_object_name => 'UT_EXAMPLETEST2', - a_name => 'UT_EXAMPLETEST', - a_description => 'Another example test', - a_before_test_proc_name => 'SETUP', - a_after_test_proc_name => 'TEARDOWN') - ); - - suite.do_execute(listener); + l_suite.do_execute(); -- No reporter used in this example so outputing the results manually. - for test_idx in suite.items.first .. suite.items.last loop - test_item := treat(suite.items(test_idx) as ut_test); + for test_idx in l_suite.items.first .. l_suite.items.last loop + l_test := treat(l_suite.items(test_idx) as ut_test); dbms_output.put_line('---------------------------------------------------'); - dbms_output.put_line('Test:' || test_item.item.form_name); - dbms_output.put_line('Result: ' || ut_utils.test_result_to_char(test_item.result)); + dbms_output.put_line('Test:' || l_test.item.form_name); + dbms_output.put_line('Result: ' || ut_utils.test_result_to_char(l_test.result)); dbms_output.put_line('expectation Results:'); - for i in 1 .. test_item.failed_expectations.count loop - expectation := test_item.failed_expectations(i); - dbms_output.put_line(i || ' - result: ' || ut_utils.test_result_to_char(expectation.result)); - dbms_output.put_line(i || ' - Message: ' || expectation.message); + for i in 1 .. l_test.failed_expectations.count loop + l_expectation := l_test.failed_expectations(i); + dbms_output.put_line(i || ' - result: ' || ut_utils.test_result_to_char(l_expectation.result)); + dbms_output.put_line(i || ' - Message: ' || l_expectation.message); end loop; end loop; dbms_output.put_line('---------------------------------------------------'); diff --git a/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql b/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql index d86c862c9..4f9172510 100644 --- a/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql +++ b/examples/developer_examples/RunExampleTestSuiteWithCompositeReporter.sql @@ -15,31 +15,23 @@ declare suite ut_logical_suite; l_doc_reporter ut_output_reporter_base := ut_documentation_reporter(); l_tc_reporter ut_output_reporter_base := ut_teamcity_reporter(); - l_listener ut_event_listener := ut_event_listener(ut_reporters(l_doc_reporter, l_tc_reporter)); l_run ut_run; begin - suite := ut_logical_suite(a_object_owner=>null, a_object_name => 'ut_exampletest', a_name => null, a_description => 'Test Suite Name',a_path => null); + ut_event_manager.initialize(); + ut_event_manager.add_listener(l_doc_reporter); + ut_event_manager.add_listener(l_tc_reporter); - suite.add_item( - ut_test(a_object_name => 'ut_exampletest' - ,a_name => 'ut_exAmpletest' - ,a_description => 'Example test1' - ,a_before_test_proc_name => 'Setup' - ,a_after_test_proc_name => 'tEardown') - ); + suite := ut_suite(user, 'ut_exampletest'); + suite.description := 'Test Suite Name'; - suite.add_item( - ut_test( - a_object_name => 'UT_EXAMPLETEST2', - a_name => 'UT_EXAMPLETEST', - a_description => 'Another example test', - a_before_test_proc_name => 'SETUP', - a_after_test_proc_name => 'TEARDOWN') - ); + suite.add_item(ut_test(user,'ut_exampletest','ut_exAmpletest')); + suite.add_item(ut_test(user, 'UT_EXAMPLETEST2','UT_EXAMPLETEST')); -- provide a reporter to process results l_run := ut_run(ut_suite_items(suite)); - l_run.do_execute(l_listener); + l_run.do_execute(); + + ut_event_manager.trigger_event(ut_utils.gc_finalize, l_run); l_doc_reporter.lines_to_dbms_output(0,0); l_tc_reporter.lines_to_dbms_output(0,0); end; diff --git a/examples/developer_examples/RunExampleTestSuiteWithCustomReporter.sql b/examples/developer_examples/RunExampleTestSuiteWithCustomReporter.sql index 6658228d4..59fe15662 100644 --- a/examples/developer_examples/RunExampleTestSuiteWithCustomReporter.sql +++ b/examples/developer_examples/RunExampleTestSuiteWithCustomReporter.sql @@ -14,37 +14,34 @@ set echo off @@ut_custom_reporter.tpb declare - suite ut_logical_suite; + l_suite ut_logical_suite; + l_test ut_test; l_reporter ut_output_reporter_base; - l_listener ut_event_listener; l_run ut_run; begin + ut_event_manager.initialize(); -- Install ut_custom_reporter first from example folder - suite := ut_logical_suite(a_object_owner=>null, a_object_name => 'ut_exampletest', a_name => null, a_description => 'Test Suite Name',a_path => null); + l_suite := ut_suite(user, 'ut_exampletest'); - suite.add_item( - ut_test(a_object_name => 'ut_exampletest' - ,a_name => 'ut_exAmpletest' - ,a_description => 'Example test1' - ,a_before_test_proc_name => 'Setup' - ,a_after_test_proc_name => 'tEardown') - ); + l_test := ut_test(user, 'ut_exampletest','ut_exAmpletest'); + l_test.description := 'Example test1'; + l_test.before_test_list := ut_executables(ut_executable(user, 'ut_exampletest','Setup',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'ut_exampletest','tEardown',ut_utils.gc_after_test)); + l_suite.add_item(l_test); - suite.add_item( - ut_test( - a_object_name => 'UT_EXAMPLETEST2', - a_name => 'UT_EXAMPLETEST', - a_description => 'Another example test', - a_before_test_proc_name => 'SETUP', - a_after_test_proc_name => 'TEARDOWN') - ); + l_test := ut_test(user, 'UT_EXAMPLETEST2','ut_exAmpletest'); + l_test.description := 'Another example test'; + l_test.before_test_list := ut_executables(ut_executable(user, 'ut_exampletest','SETUP',ut_utils.gc_before_test)); + l_test.after_test_list := ut_executables(ut_executable(user, 'ut_exampletest','TEARDOWN',ut_utils.gc_after_test)); + l_suite.add_item(l_test); -- provide a reporter to process results tabbing each hierarcy level by tab_size l_reporter := ut_custom_reporter(a_tab_size => 2); - l_listener := ut_event_listener(ut_reporters(l_reporter)); - l_run := ut_run(ut_suite_items(suite)); - l_run.do_execute(l_listener); + ut_event_manager.add_listener(l_reporter); + l_run := ut_run(ut_suite_items(l_suite)); + l_run.do_execute(); + ut_event_manager.trigger_event(ut_utils.gc_finalize, l_run); l_reporter.lines_to_dbms_output(0,0); end; / @@ -54,3 +51,4 @@ end; drop type ut_custom_reporter; drop package ut_exampletest; drop package ut_exampletest2; +exec dbms_session.reset_package; diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 2f0c784d5..41854ce25 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -30,7 +30,6 @@ create or replace package ut_utils authid definer is gc_before_before_all constant t_event_name := 'before_before_all'; gc_before_before_each constant t_event_name := 'before_before_each'; gc_before_before_test constant t_event_name := 'before_before_test'; - gc_before_test constant t_event_name := 'before_test'; gc_before_test_execute constant t_event_name := 'before_test_execute'; gc_before_after_test constant t_event_name := 'before_after_test'; gc_before_after_each constant t_event_name := 'before_after_each'; @@ -40,7 +39,6 @@ create or replace package ut_utils authid definer is gc_after_before_all constant t_event_name := 'after_before_all'; gc_after_before_each constant t_event_name := 'after_before_each'; gc_after_before_test constant t_event_name := 'after_before_test'; - gc_after_test constant t_event_name := 'after_test'; gc_after_test_execute constant t_event_name := 'after_test_execute'; gc_after_after_test constant t_event_name := 'after_after_test'; gc_after_after_each constant t_event_name := 'after_after_each'; @@ -50,7 +48,9 @@ create or replace package ut_utils authid definer is subtype t_executable_type is varchar2(30); gc_before_all constant t_executable_type := 'before_all'; gc_before_each constant t_executable_type := 'before_each'; + gc_before_test constant t_executable_type := 'before_test'; gc_test_execute constant t_executable_type := 'test_execute'; + gc_after_test constant t_executable_type := 'after_test'; gc_after_each constant t_executable_type := 'after_each'; gc_after_all constant t_executable_type := 'after_all'; From 17ee8cb83c66ee1ecfb9aedb5e0e8ded1e5d73c5 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Thu, 29 Mar 2018 03:04:41 +0100 Subject: [PATCH 09/28] Fixed test for empty description in 11g. --- test/core/test_suite_builder.pkb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb index df9600b94..c0abcd8ba 100644 --- a/test/core/test_suite_builder.pkb +++ b/test/core/test_suite_builder.pkb @@ -26,8 +26,8 @@ create or replace package body test_suite_builder is --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert - ut.expect(l_actual).to_be_like( - '%UT3_TESTERsome_packagesome_package%' + ut.expect(l_actual).to_match( + 'UT3_TESTERsome_packagesome_package()?some_package' ); end; From 93be8734cb6866cc3d14cc3976c25e51d928ce34 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Thu, 29 Mar 2018 03:34:50 +0100 Subject: [PATCH 10/28] Fixed test for empty description in 11g. --- test/core/test_suite_builder.pkb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb index c0abcd8ba..6b80d708f 100644 --- a/test/core/test_suite_builder.pkb +++ b/test/core/test_suite_builder.pkb @@ -27,7 +27,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_match( - 'UT3_TESTERsome_packagesome_package()?some_package' + 'UT3_TESTERsome_packagesome_package()?\s*some_package' ); end; From 75b96c321ab8c9e56c75832d4ce8cba99427e38d Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Thu, 29 Mar 2018 15:27:13 +0100 Subject: [PATCH 11/28] Fixed double warning on missing endcontext annotation. Fixed tests. --- source/core/ut_suite_builder.pkb | 4 +--- test/core/test_suite_builder.pkb | 9 ++++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index c0eb08392..e7d2ff998 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -480,8 +480,6 @@ create or replace package body ut_suite_builder is while l_context_pos is not null loop l_end_context_pos := get_endcontext_position(l_context_pos, a_package_ann_index); if l_end_context_pos is null then - a_suite.put_warning( - 'Annotation "--%context('||a_annotations(l_context_pos).text||')" was ignored. Cannot find following "--%endcontext".'); exit; end if; @@ -554,7 +552,7 @@ create or replace package body ut_suite_builder is duplicate_annotations_warning(l_suite, l_package_ann_index, 'suite'); add_suite_contexts( l_suite, l_annotations, l_package_ann_index ); - + --by this time all contexts were consumed and l_annotations should not have any context/endcontext annotation in it. warning_on_incomplete_context( l_suite, l_annotations, l_package_ann_index ); populate_suite_contents( l_suite, l_annotations, l_package_ann_index ); diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb index 6b80d708f..71e62cf40 100644 --- a/test/core/test_suite_builder.pkb +++ b/test/core/test_suite_builder.pkb @@ -366,7 +366,7 @@ create or replace package body test_suite_builder is ut.expect(l_actual).to_be_like( '%UT_SUITEUT3_TESTER' || 'some_packagesome_packageCool' || - '%Annotations: "--%afterall", "--%aftereach", "--%beforeall", "--%beforeeach"' || + '%Annotations: "--\%afterall", "--\%aftereach", "--\%beforeall", "--\%beforeeach"' || ' were ignored for procedure "DO_STUFF".' || ' Those annotations cannot be used with annotation: "--%test"%'|| '%%some_package%do_stuff' || @@ -374,6 +374,7 @@ create or replace package body test_suite_builder is '%' || '%' || '%%' + ,'\' ); end; @@ -440,7 +441,7 @@ create or replace package body test_suite_builder is --Assert ut.expect(l_actual).to_be_like( '%' || - '%Annotation "--%context(A context)" was ignored. Cannot find following "--%endcontext".' || + '%Annotation "--\%context(A context)" was ignored. Cannot find following "--\%endcontext".' || '%' || '' || '%suite_level_testIn suitesome_package.suite_level_test' || @@ -455,6 +456,7 @@ create or replace package body test_suite_builder is '%' || '' || '' + ,'\' ); end; @@ -478,7 +480,7 @@ create or replace package body test_suite_builder is --Assert ut.expect(l_actual).to_be_like( '%' || - '%Annotation "--%endcontext" was ignored. Cannot find preceding "--%context".' || + '%Annotation "--\%endcontext" was ignored. Cannot find preceding "--\%context".' || '%' || '' || '%context_1A contextsome_package.context_1' || @@ -501,6 +503,7 @@ create or replace package body test_suite_builder is '%' || '' || '' + ,'\' ); end; From a6dadcc27212e42befc223afdaff0fbc2a9b8ad0 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Fri, 30 Mar 2018 02:44:12 +0100 Subject: [PATCH 12/28] fix in test to overcome 11g XML missing attributes in 3.0.4 --- test/core/test_suite_builder.pkb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb index 71e62cf40..9b0c9e5d2 100644 --- a/test/core/test_suite_builder.pkb +++ b/test/core/test_suite_builder.pkb @@ -363,17 +363,17 @@ create or replace package body test_suite_builder is --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert - ut.expect(l_actual).to_be_like( - '%UT_SUITEUT3_TESTER' || + ut.expect(l_actual).match( + 'UT_SUITEUT3_TESTER' || 'some_packagesome_packageCool' || - '%Annotations: "--\%afterall", "--\%aftereach", "--\%beforeall", "--\%beforeeach"' || + '.*Annotations: "--%afterall", "--%aftereach", "--%beforeall", "--%beforeeach"' || ' were ignored for procedure "DO_STUFF".' || - ' Those annotations cannot be used with annotation: "--%test"%'|| - '%%some_package%do_stuff' || - '%' || - '%' || - '%' || - '%%' + ' Those annotations cannot be used with annotation: "--%test"'|| + '.*.*some_package.*do_stuff' || + '(.*)?' || + '(.*)?' || + '(.*)?' || + '(.*)?' ,'\' ); end; From 8a2684534c71475eb321692c47ca573ab62caba7 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sat, 7 Apr 2018 17:17:34 +0100 Subject: [PATCH 13/28] Fixes after merging from develop. Added additional tests for suite_builder. Adjusted variable names to match convention. Fixed setting of nls_params for XMLTYPE conversion using DBMS_XMLGEN. --- source/core/events/ut_event_manager.pkb | 28 ++-- source/core/ut_suite_builder.pkb | 34 ++--- .../data_values/ut_data_value_anydata.tpb | 2 +- source/reporters/ut_tfs_junit_reporter.tpb | 12 +- .../reporters/test_tfs_junit_reporter.pkb | 5 +- test/core/test_suite_builder.pkb | 126 ++++++++++++++++-- test/core/test_suite_builder.pks | 8 +- 7 files changed, 165 insertions(+), 50 deletions(-) diff --git a/source/core/events/ut_event_manager.pkb b/source/core/events/ut_event_manager.pkb index a3733eeb0..312ddc9c3 100644 --- a/source/core/events/ut_event_manager.pkb +++ b/source/core/events/ut_event_manager.pkb @@ -21,29 +21,29 @@ create or replace package body ut_event_manager as type t_listener_numbers is table of boolean index by t_listener_number; type t_events_listeners is table of t_listener_numbers index by t_event_name; - gc_event_listeners_index t_events_listeners; - gc_listeners t_listeners; + g_event_listeners_index t_events_listeners; + g_listeners t_listeners; procedure initialize is begin - gc_event_listeners_index.delete; - gc_listeners := t_listeners(); + g_event_listeners_index.delete; + g_listeners := t_listeners(); end; procedure trigger_event( a_event_name t_event_name, a_event_object ut_event_item ) is begin - if a_event_name is not null and gc_event_listeners_index.exists(a_event_name) - and gc_event_listeners_index(a_event_name) is not null + if a_event_name is not null and g_event_listeners_index.exists(a_event_name) + and g_event_listeners_index(a_event_name) is not null then - for listener_number in 1 .. gc_event_listeners_index(a_event_name).count loop - gc_listeners(listener_number).on_event(a_event_name, a_event_object); + for listener_number in 1 .. g_event_listeners_index(a_event_name).count loop + g_listeners(listener_number).on_event(a_event_name, a_event_object); end loop; end if; end; procedure add_event( a_event_name t_event_name, a_listener_pos binary_integer ) is begin - gc_event_listeners_index(a_event_name)(a_listener_pos) := true; + g_event_listeners_index(a_event_name)(a_listener_pos) := true; end; procedure add_events( a_event_names ut_varchar2_list, a_listener_pos binary_integer ) is @@ -55,12 +55,12 @@ create or replace package body ut_event_manager as function add_listener( a_listener ut_event_listener ) return t_listener_number is begin - if gc_listeners is null then - gc_listeners := t_listeners(); + if g_listeners is null then + g_listeners := t_listeners(); end if; - gc_listeners.extend; - gc_listeners(gc_listeners.last) := a_listener; - return gc_listeners.last; + g_listeners.extend; + g_listeners(g_listeners.last) := a_listener; + return g_listeners.last; end; procedure add_listener( a_listener ut_event_listener ) is diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index e7d2ff998..ee23bbedb 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -67,12 +67,12 @@ create or replace package body ut_suite_builder is function get_procedure_annotations(a_annotations ut_annotations, a_index binary_integer) return tt_procedure_annotations is l_result tt_procedure_annotations; - i binary_integer := a_index; + l_index binary_integer := a_index; begin loop - l_result(a_annotations(i).name)(i) := a_annotations(i).text; - exit when is_last_annotation_for_proc(a_annotations, i); - i := a_annotations.next(i); + l_result(a_annotations(l_index).name)(l_index) := a_annotations(l_index).text; + exit when is_last_annotation_for_proc(a_annotations, l_index); + l_index := a_annotations.next(l_index); end loop; return l_result; end; @@ -102,14 +102,14 @@ create or replace package body ut_suite_builder is function build_annotation_index(a_annotations tt_package_annotations ) return tt_annotations_index is l_result tt_annotations_index; - i binary_integer; + l_idx binary_integer; begin - i := a_annotations.first; - while i is not null loop - if a_annotations(i).name is not null then - l_result(a_annotations(i).name)(i) := true; + l_idx := a_annotations.first; + while l_idx is not null loop + if a_annotations(l_idx).name is not null then + l_result(a_annotations(l_idx).name)(l_idx) := true; end if; - i := a_annotations.next(i); + l_idx := a_annotations.next(l_idx); end loop; return l_result; end; @@ -119,15 +119,15 @@ create or replace package body ut_suite_builder is a_start_pos t_annotation_position, a_end_pos t_annotation_position ) is - i t_annotation_name; + l_idx t_annotation_name; begin - i := a_index.first; - while i is not null loop - a_index(i).delete(a_start_pos, a_end_pos); - if a_index(i).count = 0 then - a_index.delete(i); + l_idx := a_index.first; + while l_idx is not null loop + a_index( l_idx ).delete( a_start_pos, a_end_pos); + if a_index( l_idx ).count = 0 then + a_index.delete( l_idx ); end if; - i := a_index.next(i); + l_idx := a_index.next( l_idx ); end loop; end; diff --git a/source/expectations/data_values/ut_data_value_anydata.tpb b/source/expectations/data_values/ut_data_value_anydata.tpb index 599cd9bb8..be91c2d4a 100644 --- a/source/expectations/data_values/ut_data_value_anydata.tpb +++ b/source/expectations/data_values/ut_data_value_anydata.tpb @@ -37,8 +37,8 @@ create or replace type body ut_data_value_anydata as self.is_data_null := 1; end if; if not self.is_null() then - open l_query for select a_value val from dual; ut_expectation_processor.set_xml_nls_params(); + open l_query for select a_value val from dual; l_ctx := sys.dbms_xmlgen.newcontext( l_query ); dbms_xmlgen.setrowtag(l_ctx, ''); dbms_xmlgen.setrowsettag(l_ctx, ''); diff --git a/source/reporters/ut_tfs_junit_reporter.tpb b/source/reporters/ut_tfs_junit_reporter.tpb index 4caa64184..42da37f05 100644 --- a/source/reporters/ut_tfs_junit_reporter.tpb +++ b/source/reporters/ut_tfs_junit_reporter.tpb @@ -95,6 +95,8 @@ create or replace type body ut_tfs_junit_reporter is l_tests_count integer := a_suite.results_count.disabled_count + a_suite.results_count.success_count + a_suite.results_count.failure_count + a_suite.results_count.errored_count; l_suite ut_suite; + l_outputs clob; + l_errors ut_varchar2_list; begin for i in 1 .. a_suite.items.count loop @@ -114,20 +116,22 @@ create or replace type body ut_tfs_junit_reporter is end if; end loop; l_suite := treat(a_suite as ut_suite); - if l_suite.before_all.serveroutput is not null or l_suite.after_all.serveroutput is not null then + l_outputs := l_suite.get_serveroutputs(); + if l_outputs is not null and l_outputs != empty_clob() then self.print_text(''); self.print_text(''); self.print_text(''); else self.print_text(''); end if; - if l_suite.before_all.error_stack is not null or l_suite.after_all.error_stack is not null then + l_errors := l_suite.get_error_stack_traces(); + if l_errors is not empty then self.print_text(''); self.print_text(''); self.print_text(''); else diff --git a/test/core/reporters/test_tfs_junit_reporter.pkb b/test/core/reporters/test_tfs_junit_reporter.pkb index f7ea923ff..9f518ac88 100644 --- a/test/core/reporters/test_tfs_junit_reporter.pkb +++ b/test/core/reporters/test_tfs_junit_reporter.pkb @@ -117,10 +117,7 @@ create or replace package body test_tfs_junit_reporter as ut.expect(l_actual).to_be_like(' - - - + %'); end; diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb index 9b0c9e5d2..97b565f68 100644 --- a/test/core/test_suite_builder.pkb +++ b/test/core/test_suite_builder.pkb @@ -5,14 +5,26 @@ create or replace package body test_suite_builder is a_package_name varchar2 := 'TEST_SUITE_BUILDER_PACKAGE' ) return clob is l_suites ut3.ut_suite_builder.tt_schema_suites; + l_suite ut3.ut_logical_suite; l_cursor sys_refcursor; + l_xml xmltype; begin open l_cursor for select value(x) from table( ut3.ut_annotated_objects( ut3.ut_annotated_object('UT3_TESTER', a_package_name, 'PACKAGE', a_annotations) ) ) x; l_suites := ut3.ut_suite_builder.build_suites(l_cursor).schema_suites; - return xmltype(l_suites(l_suites.first)).getClobVal(); + l_suite := l_suites(l_suites.first); + + select deletexml( + xmltype(l_suite), + '//RESULTS_COUNT|//START_TIME|//END_TIME|//RESULT|//ASSOCIATED_EVENT_NAME' || + '|//TRANSACTION_INVALIDATORS|//ERROR_BACKTRACE|//ERROR_STACK|//SERVEROUTPUT' + ) + into l_xml + from dual; + + return l_xml.getClobVal(); end; procedure no_suite_description is @@ -363,19 +375,19 @@ create or replace package body test_suite_builder is --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert - ut.expect(l_actual).match( + ut.expect(l_actual).to_be_like( 'UT_SUITEUT3_TESTER' || 'some_packagesome_packageCool' || - '.*Annotations: "--%afterall", "--%aftereach", "--%beforeall", "--%beforeeach"' || + '%Annotations: "--\%afterall", "--\%aftereach", "--\%beforeall", "--\%beforeeach"' || ' were ignored for procedure "DO_STUFF".' || - ' Those annotations cannot be used with annotation: "--%test"'|| - '.*.*some_package.*do_stuff' || - '(.*)?' || - '(.*)?' || - '(.*)?' || - '(.*)?' + ' Those annotations cannot be used with annotation: "--\%test"'|| + '%%some_package%do_stuff%' ,'\' ); + ut.expect(l_actual).not_to_be_like('%%'); + ut.expect(l_actual).not_to_be_like('%%'); + ut.expect(l_actual).not_to_be_like('%%'); + ut.expect(l_actual).not_to_be_like('%%'); end; procedure suite_from_context is @@ -423,6 +435,102 @@ create or replace package body test_suite_builder is ); end; + procedure before_after_in_context is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'test','In suite', 'suite_level_test'), + ut3.ut_annotation(3, 'context','A context', null), + ut3.ut_annotation(4, 'beforeall',null, 'context_beforeall'), + ut3.ut_annotation(5, 'beforeeach',null, 'context_beforeeach'), + ut3.ut_annotation(6, 'test', 'In context', 'test_in_a_context'), + ut3.ut_annotation(7, 'aftereach',null, 'context_aftereach'), + ut3.ut_annotation(8, 'afterall',null, 'context_afterall'), + ut3.ut_annotation(9, 'endcontext',null, null) + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '' || + '%' || + '%' || + '%context_1' || + '%' || + '%' || + '%test_in_a_context' || + '%%context_beforeeach%' || + '%%test_in_a_context%' || + '%%context_aftereach%' || + '%' || + '%' || + '%%context_beforeall%' || + '%%context_afterall%' || + '%' || + '%' || + '%suite_level_test' || + '%%suite_level_test%' || + '%' || + '%' || + '%' + ); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + end; + + procedure before_after_out_of_context is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(1, 'suite','Cool', null), + ut3.ut_annotation(2, 'beforeall',null, 'suite_level_beforeall'), + ut3.ut_annotation(3, 'beforeeach',null, 'suite_level_beforeeach'), + ut3.ut_annotation(4, 'test','In suite', 'suite_level_test'), + ut3.ut_annotation(5, 'context','A context', null), + ut3.ut_annotation(6, 'test', 'In context', 'test_in_a_context'), + ut3.ut_annotation(7, 'endcontext',null, null), + ut3.ut_annotation(8, 'aftereach',null, 'suite_level_aftereach'), + ut3.ut_annotation(9, 'afterall',null, 'suite_level_afterall') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '' || + '%' || + '%' || + '%context_1' || + '%' || + '%' || + '%test_in_a_context' || + '%%test_in_a_context%' || + '%' || + '%' || + '%' || + '%' || + '%suite_level_test' || + '%%suite_level_beforeeach%' || + '%%suite_level_test%' || + '%%suite_level_aftereach%' || + '%' || + '%' || + '%%suite_level_beforeall%' || + '%%suite_level_afterall%' || + '%' + ); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + ut.expect(l_actual).not_to_be_like('%%%%%%'); + end; + procedure context_without_endcontext is l_actual clob; l_annotations ut3.ut_annotations; diff --git a/test/core/test_suite_builder.pks b/test/core/test_suite_builder.pks index d3ea31e47..c723e36cd 100644 --- a/test/core/test_suite_builder.pks +++ b/test/core/test_suite_builder.pks @@ -56,10 +56,16 @@ create or replace package test_suite_builder is --%test(Creates nested suite for content between context/endcontext annotations) procedure suite_from_context; + --%test(Associates before/after all/each to tests in context only) + procedure before_after_in_context; + + --%test(Does not propagate before/after each to context) + procedure before_after_out_of_context; + --%test(Does not create context and gives warning when endcontext is missing) procedure context_without_endcontext; - --%test(Gives warning when endcontext is missing a preceding context) + --%test(Gives warning if --%endcontext is missing a preceding --%context) procedure endcontext_without_context; end; From e9c4e8009bae0bb66c6dfedabebe89424e44ad78 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sat, 7 Apr 2018 18:16:49 +0100 Subject: [PATCH 14/28] Fixed test failure on 11g R2 - some issues with LIKE operator, long pattern, CLOB and escape char. --- test/core/test_suite_builder.pkb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb index 97b565f68..119c5c547 100644 --- a/test/core/test_suite_builder.pkb +++ b/test/core/test_suite_builder.pkb @@ -548,8 +548,11 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%' || - '%Annotation "--\%context(A context)" was ignored. Cannot find following "--\%endcontext".' || + '%Annotation "--\%context(A context)" was ignored. Cannot find following "--\%endcontext".%' + ,'\' + ); + ut.expect(l_actual).to_be_like( + '' || '%' || '' || '%suite_level_testIn suitesome_package.suite_level_test' || @@ -564,7 +567,6 @@ create or replace package body test_suite_builder is '%' || '' || '' - ,'\' ); end; @@ -587,8 +589,11 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%' || - '%Annotation "--\%endcontext" was ignored. Cannot find preceding "--\%context".' || + '%Annotation "--\%endcontext" was ignored. Cannot find preceding "--\%context".%' + ,'\' + ); + ut.expect(l_actual).to_be_like( + '' || '%' || '' || '%context_1A contextsome_package.context_1' || @@ -611,7 +616,6 @@ create or replace package body test_suite_builder is '%' || '' || '' - ,'\' ); end; From bfbfa43c357dfe66188ebcd8c1ccee6cf36e2bbd Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sat, 7 Apr 2018 22:36:39 +0100 Subject: [PATCH 15/28] Updated documentation. Pivoted matchers / types matrix. --- docs/userguide/expectations.md | 35 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md index 6e49c8cc1..365f0adbf 100644 --- a/docs/userguide/expectations.md +++ b/docs/userguide/expectations.md @@ -780,24 +780,23 @@ Since NULL is neither *true* nor *false*, both expectations will report failure. The matrix below illustrates the data types supported by different matchers. -| | be_between | be_empty | be_false | be_greater_than | be_greater_or_equal | be_less_or_equal | be_less_than | be_like | be_not_null | be_null | be_true | equal | have_count | match | -|:------------------------------|:----------:|:--------:|:--------:|:---------------:|:-------------------:|:----------------:|:------------:|:-------:|:-----------:|:-------:|:-------:|:-----:|:----------:|:-----:| -| blob | | | | | | | | | X | X | | X | | | -| boolean | | | X | | | | | | X | X | X | X | | | -| clob | | | | | | | | X | X | X | | X | | X | -| date | X | | | X | X | X | X | | X | X | | X | | | -| number | X | | | X | X | X | X | | X | X | | X | | | -| timestamp | X | | | X | X | X | X | | X | X | | X | | | -| timestamp with timezone | X | | | X | X | X | X | | X | X | | X | | | -| timestamp with local timezone | X | | | X | X | X | X | | X | X | | X | | | -| varchar2 | X | | | | | | | X | X | X | | X | | X | -| interval year to month | X | | | X | X | X | X | | X | X | | X | | | -| interval day to second | X | | | X | X | X | X | | X | X | | X | | | -| refcursor | | X | | | | | | | X | X | | X | X | | -| nested table (as anydata) | | X | | | | | | | X | X | | X | X | | -| varray (as anydata) | | X | | | | | | | X | X | | X | X | | -| object (as anydata) | | | | | | | | | X | X | | X | | | - +| |blob |boolean|clob |date |number|timestamp|timestamp
with
timezone|timestamp
with
local
timezone|varchar2|interval
year
to
month|interval
day
to
second|cursor|nested
table
/ varray|object| +|:------------------------------|:---:|:-----:|:---:|:---:|:----:|:-------:|:---------------------------:|:------------------------------------:|:------:|:-----------------------------:|:-----------------------------:|:----:|:-------------------------:|:----:| +| be_not_null | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +| be_null | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +| be_false | | X | | | | | | | | | | | | | +| be_true | | X | | | | | | | | | | | | | +| be_greater_than | | | | X | X | X | X | X | | X | X | | | | +| be_greater_or_equal | | | | X | X | X | X | X | | X | X | | | | +| be_less_or_equal | | | | X | X | X | X | X | | X | X | | | | +| be_less_than | | | | X | X | X | X | X | | X | X | | | | +| be_between | | | | X | X | X | X | X | X | X | X | | | | +| equal | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +| match | | | X | | | | | | X | | | | | | +| be_like | | | X | | | | | | X | | | | | | +| be_empty | | | | | | | | | | | | X | X | | +| have_count | | | | | | | | | | | | X | X | | + From 9cd61bfd51773087eff41d92cf748fed1defc8a1 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sat, 7 Apr 2018 22:41:39 +0100 Subject: [PATCH 16/28] Marking first column as bold --- docs/userguide/expectations.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/userguide/expectations.md b/docs/userguide/expectations.md index 365f0adbf..ae1176be5 100644 --- a/docs/userguide/expectations.md +++ b/docs/userguide/expectations.md @@ -780,22 +780,22 @@ Since NULL is neither *true* nor *false*, both expectations will report failure. The matrix below illustrates the data types supported by different matchers. -| |blob |boolean|clob |date |number|timestamp|timestamp
with
timezone|timestamp
with
local
timezone|varchar2|interval
year
to
month|interval
day
to
second|cursor|nested
table
/ varray|object| -|:------------------------------|:---:|:-----:|:---:|:---:|:----:|:-------:|:---------------------------:|:------------------------------------:|:------:|:-----------------------------:|:-----------------------------:|:----:|:-------------------------:|:----:| -| be_not_null | X | X | X | X | X | X | X | X | X | X | X | X | X | X | -| be_null | X | X | X | X | X | X | X | X | X | X | X | X | X | X | -| be_false | | X | | | | | | | | | | | | | -| be_true | | X | | | | | | | | | | | | | -| be_greater_than | | | | X | X | X | X | X | | X | X | | | | -| be_greater_or_equal | | | | X | X | X | X | X | | X | X | | | | -| be_less_or_equal | | | | X | X | X | X | X | | X | X | | | | -| be_less_than | | | | X | X | X | X | X | | X | X | | | | -| be_between | | | | X | X | X | X | X | X | X | X | | | | -| equal | X | X | X | X | X | X | X | X | X | X | X | X | X | X | -| match | | | X | | | | | | X | | | | | | -| be_like | | | X | | | | | | X | | | | | | -| be_empty | | | | | | | | | | | | X | X | | -| have_count | | | | | | | | | | | | X | X | | +| Matcher |blob |boolean|clob |date |number|timestamp|timestamp
with
timezone|timestamp
with
local
timezone|varchar2|interval
year
to
month|interval
day
to
second|cursor|nested
table
/ varray|object| +|:----------------------|:---:|:-----:|:---:|:---:|:----:|:-------:|:---------------------------:|:------------------------------------:|:------:|:-----------------------------:|:-----------------------------:|:----:|:-------------------------:|:----:| +|**be_not_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +|**be_null** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +|**be_false** | | X | | | | | | | | | | | | | +|**be_true** | | X | | | | | | | | | | | | | +|**be_greater_than** | | | | X | X | X | X | X | | X | X | | | | +|**be_greater_or_equal**| | | | X | X | X | X | X | | X | X | | | | +|**be_less_or_equal** | | | | X | X | X | X | X | | X | X | | | | +|**be_less_than** | | | | X | X | X | X | X | | X | X | | | | +|**be_between** | | | | X | X | X | X | X | X | X | X | | | | +|**equal** | X | X | X | X | X | X | X | X | X | X | X | X | X | X | +|**match** | | | X | | | | | | X | | | | | | +|**be_like** | | | X | | | | | | X | | | | | | +|**be_empty** | | | | | | | | | | | | X | X | | +|**have_count** | | | | | | | | | | | | X | X | | From 1adf35d70d697c020e8fbca6f181895ead042aa0 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sat, 14 Apr 2018 03:25:47 +0100 Subject: [PATCH 17/28] Changed the way reporters events are registered. Fixed issue with invalid object type passed to events `before_calling_test_execute` `after_calling_test_execute` Fixed cutting of multi-line comments when parsing-annotations. Annotation position now indicates line number of annotation in package. --- .../core/annotations/ut_annotation_parser.pkb | 18 ++-- .../coverage/ut_coverage_reporter_base.tpb | 4 +- .../coverage/ut_coverage_reporter_base.tps | 4 +- source/core/types/ut_reporter_base.tpb | 40 ++++--- source/core/types/ut_reporter_base.tps | 4 +- source/core/ut_suite_builder.pkb | 23 ++-- source/core/ut_utils.pkb | 101 +++++++++++++++++- source/core/ut_utils.pks | 5 + test/api/test_ut_runner.pkb | 8 +- .../annotations/test_annotation_manager.pkb | 20 ++-- .../annotations/test_annotation_parser.pkb | 92 ++++++++-------- test/core/test_suite_builder.pkb | 4 +- test/core/test_suite_builder.pks | 2 +- test/core/test_ut_utils.pkb | 57 ++++++++++ test/core/test_ut_utils.pks | 4 + 15 files changed, 280 insertions(+), 106 deletions(-) diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index 47f31cb32..0674461c5 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -22,7 +22,6 @@ create or replace package body ut_annotation_parser as type tt_comment_list is table of varchar2(32767) index by pls_integer; gc_annotation_qualifier constant varchar2(1) := '%'; - gc_multiline_comment_pattern constant varchar2(50) := '/\*.*?\*/'; gc_annot_comment_pattern constant varchar2(30) := '^( |'||chr(09)||')*-- *('||gc_annotation_qualifier||'.*?)$'; -- chr(09) is a tab character gc_comment_replacer_patter constant varchar2(50) := '{COMMENT#%N%}'; gc_comment_replacer_regex_ptrn constant varchar2(25) := '{COMMENT#(\d+)}'; @@ -32,13 +31,6 @@ create or replace package body ut_annotation_parser as gc_annotation_pattern constant varchar2(50) := gc_annotation_qualifier || gc_regexp_identifier || '[ '||chr(9)||']*(\(.*?\)\s*?$)?'; - function delete_multiline_comments(a_source in clob) return clob is - begin - return regexp_replace(srcstr => a_source - ,pattern => gc_multiline_comment_pattern - ,modifier => 'n'); - end; - procedure add_annotation( a_annotations in out nocopy ut_annotations, a_position positiven, @@ -147,7 +139,8 @@ create or replace package body ut_annotation_parser as 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_pos binary_integer; + l_comment_line binary_integer; l_comment_replacer varchar2(50); l_source clob := a_source; begin @@ -165,14 +158,15 @@ create or replace package body ut_annotation_parser as -- position index is shifted by 1 because gc_annot_comment_pattern contains ^ as first sign -- but after instr index already points to the char on that line l_comment_pos := l_comment_pos-1; - l_comments(l_comments.count + 1) := trim(regexp_substr(srcstr => a_source + l_comment_line := regexp_count(substr(a_source,1,l_comment_pos),chr(10),1,'m')+1; + l_comments(l_comment_line) := trim(regexp_substr(srcstr => a_source ,pattern => gc_annot_comment_pattern ,occurrence => 1 ,position => l_comment_pos ,modifier => 'm' ,subexpression => 2)); - l_comment_replacer := replace(gc_comment_replacer_patter, '%N%', l_comments.count); + l_comment_replacer := replace(gc_comment_replacer_patter, '%N%', l_comment_line); l_source := regexp_replace(srcstr => a_source ,pattern => gc_annot_comment_pattern @@ -203,7 +197,7 @@ create or replace package body ut_annotation_parser as l_comment_index positive; begin - l_source := delete_multiline_comments(l_source); + l_source := ut_utils.replace_multiline_comments(l_source); -- replace all single line comments with {COMMENT#12} element and store it's content for easier processing -- this call modifies l_source diff --git a/source/core/coverage/ut_coverage_reporter_base.tpb b/source/core/coverage/ut_coverage_reporter_base.tpb index 16c93aa67..022df6f91 100644 --- a/source/core/coverage/ut_coverage_reporter_base.tpb +++ b/source/core/coverage/ut_coverage_reporter_base.tpb @@ -49,11 +49,11 @@ create or replace type body ut_coverage_reporter_base is ut_coverage.coverage_pause(); end; - overriding final member procedure before_calling_test_execute(self in out nocopy ut_coverage_reporter_base, a_test in ut_test) is + overriding final member procedure before_calling_test_execute(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_resume(); end; - overriding final member procedure after_calling_test_execute (self in out nocopy ut_coverage_reporter_base, a_test in ut_test) is + overriding final member procedure after_calling_test_execute (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable) is begin ut_coverage.coverage_pause(); end; diff --git a/source/core/coverage/ut_coverage_reporter_base.tps b/source/core/coverage/ut_coverage_reporter_base.tps index fbff1abad..087f4edff 100644 --- a/source/core/coverage/ut_coverage_reporter_base.tps +++ b/source/core/coverage/ut_coverage_reporter_base.tps @@ -26,8 +26,8 @@ create or replace type ut_coverage_reporter_base under ut_output_reporter_base( overriding final member procedure before_calling_before_test(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), overriding final member procedure after_calling_before_test (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), - overriding final member procedure before_calling_test_execute(self in out nocopy ut_coverage_reporter_base, a_test in ut_test), - overriding final member procedure after_calling_test_execute (self in out nocopy ut_coverage_reporter_base, a_test in ut_test), + overriding final member procedure before_calling_test_execute(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), + overriding final member procedure after_calling_test_execute (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), overriding final member procedure before_calling_after_test(self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), overriding final member procedure after_calling_after_test (self in out nocopy ut_coverage_reporter_base, a_executable in ut_executable), diff --git a/source/core/types/ut_reporter_base.tpb b/source/core/types/ut_reporter_base.tpb index 508299cd7..d86118bdd 100644 --- a/source/core/types/ut_reporter_base.tpb +++ b/source/core/types/ut_reporter_base.tpb @@ -83,11 +83,11 @@ create or replace type body ut_reporter_base is null; end; - member procedure before_calling_test_execute(self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure before_calling_test_execute(self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; - member procedure after_calling_test_execute (self in out nocopy ut_reporter_base, a_test in ut_test) is + member procedure after_calling_test_execute (self in out nocopy ut_reporter_base, a_executable in ut_executable) is begin null; end; @@ -137,16 +137,30 @@ create or replace type body ut_reporter_base is end; overriding member function get_supported_events return ut_varchar2_list is - l_events_list ut_varchar2_list; begin - select lower(replace(procedure_name,'CALLING_')) - bulk collect into l_events_list - from user_procedures - where object_name = upper(self_type) - and (procedure_name like 'BEFORE_%' or procedure_name like 'AFTER_%'); - l_events_list.extend; - l_events_list(l_events_list.last) := ut_utils.gc_finalize; - return l_events_list; + return ut_varchar2_list( + ut_utils.gc_before_run, + ut_utils.gc_before_suite, + ut_utils.gc_before_test, + ut_utils.gc_before_before_all, + ut_utils.gc_before_before_each, + ut_utils.gc_before_before_test, + ut_utils.gc_before_test_execute, + ut_utils.gc_before_after_test, + ut_utils.gc_before_after_each, + ut_utils.gc_before_after_all, + ut_utils.gc_after_run, + ut_utils.gc_after_suite, + ut_utils.gc_after_test, + ut_utils.gc_after_before_all, + ut_utils.gc_after_before_each, + ut_utils.gc_after_before_test, + ut_utils.gc_after_test_execute, + ut_utils.gc_after_after_test, + ut_utils.gc_after_after_each, + ut_utils.gc_after_after_all, + ut_utils.gc_finalize + ); end; overriding member procedure on_event( self in out nocopy ut_reporter_base, a_event_name varchar2, a_event_item ut_event_item) is @@ -165,7 +179,7 @@ create or replace type body ut_reporter_base is when ut_utils.gc_before_before_test then self.before_calling_before_test(treat(a_event_item as ut_executable)); when ut_utils.gc_before_test_execute - then self.before_calling_test_execute(treat(a_event_item as ut_test)); + then self.before_calling_test_execute(treat(a_event_item as ut_executable)); when ut_utils.gc_before_after_test then self.before_calling_after_test(treat(a_event_item as ut_executable)); when ut_utils.gc_before_after_each @@ -185,7 +199,7 @@ create or replace type body ut_reporter_base is when ut_utils.gc_after_before_test then self.after_calling_before_test(treat(a_event_item as ut_executable)); when ut_utils.gc_after_test_execute - then self.after_calling_test_execute(treat(a_event_item as ut_test)); + then self.after_calling_test_execute(treat(a_event_item as ut_executable)); when ut_utils.gc_after_after_test then self.after_calling_after_test(treat(a_event_item as ut_executable)); when ut_utils.gc_after_after_each diff --git a/source/core/types/ut_reporter_base.tps b/source/core/types/ut_reporter_base.tps index 688314e8f..2b5f8d853 100644 --- a/source/core/types/ut_reporter_base.tps +++ b/source/core/types/ut_reporter_base.tps @@ -39,8 +39,8 @@ create or replace type ut_reporter_base under ut_event_listener ( member procedure before_calling_before_test(self in out nocopy ut_reporter_base, a_executable in ut_executable), member procedure after_calling_before_test (self in out nocopy ut_reporter_base, a_executable in ut_executable), - member procedure before_calling_test_execute(self in out nocopy ut_reporter_base, a_test in ut_test), - member procedure after_calling_test_execute (self in out nocopy ut_reporter_base, a_test in ut_test), + member procedure before_calling_test_execute(self in out nocopy ut_reporter_base, a_executable in ut_executable), + member procedure after_calling_test_execute (self in out nocopy ut_reporter_base, a_executable in ut_executable), member procedure before_calling_after_test(self in out nocopy ut_reporter_base, a_executable in ut_executable), member procedure after_calling_after_test (self in out nocopy ut_reporter_base, a_executable in ut_executable), diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index ee23bbedb..c016a03f5 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -202,7 +202,7 @@ create or replace package body ut_suite_builder is end loop; end; - procedure duplicate_annotations_warning( + procedure warning_on_duplicate_annot( a_suite in out nocopy ut_suite_item, a_annoations tt_annotations_index, a_for_annotation varchar2 @@ -290,15 +290,20 @@ create or replace package body ut_suite_builder is a_before_each_list ut_executables, a_after_each_list ut_executables ) is - l_test ut_test; + l_test ut_test; + l_context ut_logical_suite; begin if a_suite.items is not null then for i in 1 .. a_suite.items.count loop if a_suite.items(i) is of (ut_test) then l_test := treat( a_suite.items(i) as ut_test); - l_test.before_each_list := a_before_each_list; - l_test.after_each_list := a_after_each_list; + l_test.before_each_list := coalesce(a_before_each_list,ut_executables()) multiset union all l_test.before_each_list; + l_test.after_each_list := l_test.after_each_list multiset union all coalesce(a_after_each_list,ut_executables()); a_suite.items(i) := l_test; + elsif a_suite.items(i) is of (ut_logical_suite) then + l_context := treat(a_suite.items(i) as ut_logical_suite); + update_before_after_list(l_context, a_before_each_list, a_after_each_list); + a_suite.items(i) := l_context; end if; end loop; end if; @@ -393,7 +398,7 @@ create or replace package body ut_suite_builder is else a_suite.put_warning('"--%suitepath" annotation requires a non-empty value. Annotation ignored.'); end if; - duplicate_annotations_warning(a_suite, a_package_ann_index, 'suitepath'); + warning_on_duplicate_annot(a_suite, a_package_ann_index, 'suitepath'); end if; a_suite.path := lower(coalesce(a_suite.path, l_object_name)); @@ -404,7 +409,7 @@ create or replace package body ut_suite_builder is else a_suite.put_warning('"--%displayname" annotation requires a non-empty value. Annotation ignored.'); end if; - duplicate_annotations_warning(a_suite, a_package_ann_index, 'displayname'); + warning_on_duplicate_annot(a_suite, a_package_ann_index, 'displayname'); end if; if a_package_ann_index.exists('rollback') then @@ -412,7 +417,7 @@ create or replace package body ut_suite_builder is if l_rollback_type is null then a_suite.put_warning('"--%rollback" annotation requires one of values: "auto" or "manual". Annotation ignored.'); end if; - duplicate_annotations_warning(a_suite, a_package_ann_index, 'rollback'); + warning_on_duplicate_annot(a_suite, a_package_ann_index, 'rollback'); end if; a_suite.disabled_flag := ut_utils.boolean_to_int(a_package_ann_index.exists('disabled')); @@ -491,7 +496,7 @@ create or replace package body ut_suite_builder is l_suite.description := l_annotations(l_package_ann_index('context').first).text; l_suite.description := l_annotations(l_context_pos).text; - duplicate_annotations_warning( l_suite, l_package_ann_index, 'suite' ); + warning_on_duplicate_annot( l_suite, l_package_ann_index, 'suite' ); populate_suite_contents( l_suite, l_annotations, l_package_ann_index, 'context_'||l_context_no ); @@ -549,7 +554,7 @@ create or replace package body ut_suite_builder is l_suite := ut_suite(a_package_annotations.owner, a_package_annotations.name); l_suite.description := l_annotations(l_package_ann_index('suite').last).text; - duplicate_annotations_warning(l_suite, l_package_ann_index, 'suite'); + warning_on_duplicate_annot(l_suite, l_package_ann_index, 'suite'); add_suite_contexts( l_suite, l_annotations, l_package_ann_index ); --by this time all contexts were consumed and l_annotations should not have any context/endcontext annotation in it. diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index 049a448c1..226c32200 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -513,7 +513,7 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab, if a_list is not null then l_trimmed_list := ut_varchar2_list(); l_index := a_list.first; - + while (l_index is not null) loop l_trimmed_list.extend; l_trimmed_list(l_trimmed_list.count) := regexp_replace(a_list(l_index), '(^['||a_regexp_to_trim||']*)|(['||a_regexp_to_trim||']*$)'); @@ -531,7 +531,7 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab, if a_list is not null then l_filtered_list := ut_varchar2_list(); l_index := a_list.first; - + while (l_index is not null) loop if regexp_like(a_list(l_index), a_regexp_filter) then l_filtered_list.extend; @@ -540,9 +540,104 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab, l_index := a_list.next(l_index); end loop; end if; - + return l_filtered_list; end; + + + function replace_multiline_comments(a_source clob) return clob is + l_result clob; + l_ml_comment_start binary_integer := 1; + l_comment_start binary_integer := 1; + l_text_start binary_integer := 1; + l_escaped_text_start binary_integer := 1; + l_escaped_text_end_char varchar2(1 char); + l_end binary_integer := 1; + l_ml_comment clob; + l_newlines_count binary_integer; + l_offset binary_integer := 1; + l_length binary_integer := coalesce(dbms_lob.getlength(a_source), 0); + function is_before(a_x binary_integer, a_y binary_integer) return boolean is + begin + return a_x < a_y or a_y = 0; + end; + begin + l_ml_comment_start := instr(a_source,'/*'); + l_comment_start := instr(a_source,'--'); + l_text_start := instr(a_source,''''); + l_escaped_text_start := instr(a_source,q'[q']'); + while l_offset > 0 and l_ml_comment_start > 0 loop + + if l_ml_comment_start > 0 and (l_ml_comment_start < l_comment_start or l_comment_start = 0) + and (l_ml_comment_start < l_text_start or l_text_start = 0)and (l_ml_comment_start < l_escaped_text_start or l_escaped_text_start = 0) + then + l_end := instr(a_source,'*/',l_ml_comment_start+2); + append_to_clob(l_result, dbms_lob.substr(a_source, l_ml_comment_start-l_offset, l_offset)); + if l_end > 0 then + l_ml_comment := substr(a_source, l_ml_comment_start, l_end-l_ml_comment_start); + l_newlines_count := length( l_ml_comment ) - length( translate( l_ml_comment, 'a'||chr(10), 'a') ); + if l_newlines_count > 0 then + append_to_clob(l_result, lpad( chr(10), l_newlines_count, chr(10) ) ); + end if; + l_end := l_end + 2; + end if; + else + + if l_comment_start > 0 and (l_comment_start < l_ml_comment_start or l_ml_comment_start = 0) + and (l_comment_start < l_text_start or l_text_start = 0) and (l_comment_start < l_escaped_text_start or l_escaped_text_start = 0) + then + l_end := instr(a_source,chr(10),l_comment_start+2); + if l_end > 0 then + l_end := l_end + 1; + end if; + elsif l_text_start > 0 and (l_text_start < l_ml_comment_start or l_ml_comment_start = 0) + and (l_text_start < l_comment_start or l_comment_start = 0) and (l_text_start < l_escaped_text_start or l_escaped_text_start = 0) + then + l_end := instr(a_source,q'[']',l_text_start+1); + + --skip double quotes while searching for end of quoted text + while l_end > 0 and l_end = instr(a_source,q'['']',l_text_start+1) loop + l_end := instr(a_source,q'[']',l_end+1); + end loop; + if l_end > 0 then + l_end := l_end + 1; + end if; + + elsif l_escaped_text_start > 0 and (l_escaped_text_start < l_ml_comment_start or l_ml_comment_start = 0) + and (l_escaped_text_start < l_comment_start or l_comment_start = 0) and (l_escaped_text_start < l_text_start or l_text_start = 0) + then + --translate char "[" from the start of quoted text "q'[someting]'" into "]" + l_escaped_text_end_char := translate( substr(a_source, l_escaped_text_start + 2, 1), '[{(<', ']})>'); + l_end := instr(a_source,l_escaped_text_end_char||'''',l_escaped_text_start + 3 ); + if l_end > 0 then + l_end := l_end + 2; + end if; + end if; + + if l_end = 0 then + append_to_clob(l_result, dbms_lob.substr(a_source, l_length-l_offset, l_offset)); + else + append_to_clob(l_result, dbms_lob.substr(a_source, l_end-l_offset, l_offset)); + end if; + end if; + l_offset := l_end; + if l_offset >= l_ml_comment_start then + l_ml_comment_start := instr(a_source,'/*',l_offset); + end if; + if l_offset >= l_comment_start then + l_comment_start := instr(a_source,'--',l_offset); + end if; + if l_offset >= l_text_start then + l_text_start := instr(a_source,'''',l_offset); + end if; + if l_offset >= l_escaped_text_start then + l_escaped_text_start := instr(a_source,q'[q']',l_offset); + end if; + end loop; + append_to_clob(l_result, dbms_lob.substr(a_source, offset=>l_end)); + return l_result; + end; + end ut_utils; / diff --git a/source/core/ut_utils.pks b/source/core/ut_utils.pks index 4d1757854..c339a5b70 100644 --- a/source/core/ut_utils.pks +++ b/source/core/ut_utils.pks @@ -322,5 +322,10 @@ create or replace package ut_utils authid definer is /*It takes a collection of type ut_varchar2_list and it only returns the elements which meets the regular expression*/ function filter_list(a_list IN ut_varchar2_list, a_regexp_filter in varchar2) return ut_varchar2_list; + /** + * Replaces multi-line comments in given source-code with empty lines + */ + function replace_multiline_comments(a_source clob) return clob; + end ut_utils; / diff --git a/test/api/test_ut_runner.pkb b/test/api/test_ut_runner.pkb index ca3201c61..a1cbd747f 100644 --- a/test/api/test_ut_runner.pkb +++ b/test/api/test_ut_runner.pkb @@ -264,11 +264,11 @@ end;'; --Arrange open l_expected for select 'UT3_TESTER' package_owner, 'DUMMY_TEST_PACKAGE' package_name, - to_char(null) procedure_name, 1 annotation_pos, 'suite' annotation_name, 'dummy_test_suite' annotation_text + to_char(null) procedure_name, 2 annotation_pos, 'suite' annotation_name, 'dummy_test_suite' annotation_text from dual union all - select 'UT3_TESTER', 'DUMMY_TEST_PACKAGE', to_char(null), 2, 'rollback', 'manual' from dual union all - select 'UT3_TESTER', 'DUMMY_TEST_PACKAGE', 'SOME_DUMMY_TEST_PROCEDURE', 3, 'test', 'dummy_test' from dual union all - select 'UT3_TESTER', 'DUMMY_TEST_PACKAGE', 'SOME_DUMMY_TEST_PROCEDURE', 4, 'beforetest', 'some_procedure' from dual; + select 'UT3_TESTER', 'DUMMY_TEST_PACKAGE', to_char(null), 3, 'rollback', 'manual' from dual union all + select 'UT3_TESTER', 'DUMMY_TEST_PACKAGE', 'SOME_DUMMY_TEST_PROCEDURE', 5, 'test', 'dummy_test' from dual union all + select 'UT3_TESTER', 'DUMMY_TEST_PACKAGE', 'SOME_DUMMY_TEST_PROCEDURE', 6, 'beforetest', 'some_procedure' from dual; --Act open l_actual for select * from table(ut3.ut_runner.get_unit_test_info('UT3_TESTER','DUMMY_TEST_PACKAGE')); --Assert diff --git a/test/core/annotations/test_annotation_manager.pkb b/test/core/annotations/test_annotation_manager.pkb index 7ac1e0870..98bea7452 100644 --- a/test/core/annotations/test_annotation_manager.pkb +++ b/test/core/annotations/test_annotation_manager.pkb @@ -147,14 +147,14 @@ create or replace package body test_annotation_manager is order by annotation_position; open l_expected for - select 1 as annotation_position, 'suite' as annotation_name, + select 2 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 + select 3, 'rollback' , 'manual', '' as subobject_name from dual union all - select 3, 'test' , 'dummy_test', 'some_dummy_test_procedure' as subobject_name + select 5, '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 + select 6, 'beforetest' , 'some_procedure', 'some_dummy_test_procedure' as subobject_name from dual; ut.expect(l_actual).to_equal(l_expected); @@ -188,10 +188,10 @@ create or replace package body test_annotation_manager is order by annotation_position; open l_expected for - select 1 as annotation_position, 'suite' as annotation_name, + select 2 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 + select 4, 'test' , 'dummy_test', 'some_dummy_test_procedure' as subobject_name from dual; ut.expect(l_actual).to_equal(l_expected); @@ -225,14 +225,14 @@ create or replace package body test_annotation_manager is order by annotation_position; open l_expected for - select 1 as annotation_position, 'suite' as annotation_name, + select 2 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 + select 3, 'rollback' , 'manual', '' as subobject_name from dual union all - select 3, 'test' , 'dummy_test', 'some_dummy_test_procedure' as subobject_name + select 5, '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 + select 6, 'beforetest' , 'some_procedure', 'some_dummy_test_procedure' as subobject_name from dual; ut.expect(l_actual).to_equal(l_expected); diff --git a/test/core/annotations/test_annotation_parser.pkb b/test/core/annotations/test_annotation_parser.pkb index f4d20c0af..6f7bdbf59 100644 --- a/test/core/annotations/test_annotation_parser.pkb +++ b/test/core/annotations/test_annotation_parser.pkb @@ -23,11 +23,11 @@ create or replace package body test_annotation_parser is --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,'ann1','Name of suite',null), - ut3.ut_annotation(5,'ann2','some_value','foo') + ut3.ut_annotation(2,'suite',null, null), + ut3.ut_annotation(3,'displayname','Name of suite',null), + ut3.ut_annotation(4,'suitepath','all.globaltests',null), + ut3.ut_annotation(6,'ann1','Name of suite',null), + ut3.ut_annotation(8,'ann2','some_value','foo') ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); @@ -61,15 +61,15 @@ create or replace package body test_annotation_parser is --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, 'ann1', 'Name of suite', null ), - ut3.ut_annotation( 5, 'ann2', 'all.globaltests', null ), - ut3.ut_annotation( 6, 'test', null, 'foo'), - ut3.ut_annotation( 7, 'ann3', 'Name of suite', null ), - ut3.ut_annotation( 8, 'ann4', 'all.globaltests', null ), - ut3.ut_annotation( 9, 'test', null, 'bar') + ut3.ut_annotation( 2, 'suite', null, null ), + ut3.ut_annotation( 3, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 4, 'suitepath', 'all.globaltests', null ), + ut3.ut_annotation( 6, 'ann1', 'Name of suite', null ), + ut3.ut_annotation( 7, 'ann2', 'all.globaltests', null ), + ut3.ut_annotation( 9, 'test', null, 'foo'), + ut3.ut_annotation( 12, 'ann3', 'Name of suite', null ), + ut3.ut_annotation( 13, 'ann4', 'all.globaltests', null ), + ut3.ut_annotation( 15, 'test', null, 'bar') ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); @@ -114,13 +114,13 @@ create or replace package body test_annotation_parser is --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' ) + ut3.ut_annotation( 2, 'suite', null, null ), + ut3.ut_annotation( 3, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 4, 'suitepath', 'all.globaltests', null ), + ut3.ut_annotation( 6, 'test', null, 'foo' ), + ut3.ut_annotation( 10, 'beforeeach', null,'foo2' ), + ut3.ut_annotation( 20, 'beforeeach', 'key=testval','foo3' ), + ut3.ut_annotation( 23, 'all', null,'foo4' ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); @@ -146,9 +146,9 @@ create or replace package body test_annotation_parser is --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( 2, 'suite', null, null ), + ut3.ut_annotation( 3, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 4, 'suitepath', 'all.globaltests', null ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); @@ -174,9 +174,9 @@ create or replace package body test_annotation_parser is --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( 2, 'suite', null, null ), + ut3.ut_annotation( 3, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 4, 'suitepath', 'all.globaltests', null ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); @@ -205,9 +205,9 @@ create or replace package body test_annotation_parser is --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, 'suite', null, null ), + ut3.ut_annotation( 6, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 7, 'suitepath', 'all.globaltests', null ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); @@ -233,9 +233,9 @@ create or replace package body test_annotation_parser is --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 ) + ut3.ut_annotation( 2, 'suite', null, null ), + ut3.ut_annotation( 3, 'displayname', 'name = Name of suite', null ), + ut3.ut_annotation( 4, 'suitepath', 'key=all.globaltests,key2=foo,"--%some text"', null ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); @@ -266,9 +266,9 @@ create or replace package body test_annotation_parser is --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( 7, 'suite', null, null ), + ut3.ut_annotation( 8, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 9, 'suitepath', 'all.globaltests', null ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); @@ -325,7 +325,7 @@ END;'; --Assert l_expected := ut3.ut_annotations( - ut3.ut_annotation( 1, 'suite', 'Name of suite (including some brackets) and some more text', null ) + ut3.ut_annotation( 2, 'suite', 'Name of suite (including some brackets) and some more text', null ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); @@ -353,8 +353,8 @@ END;'; --Assert l_expected := ut3.ut_annotations( - ut3.ut_annotation( 1, 'suite', null, null ), - ut3.ut_annotation( 2, 'suitepath', 'all.globaltests', null ) + ut3.ut_annotation( 6, 'suite', null, null ), + ut3.ut_annotation( 7, 'suitepath', 'all.globaltests', null ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); @@ -377,9 +377,9 @@ END;'; --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( 2, 'suite', null, null ), + ut3.ut_annotation( 3, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 4, 'suitepath', 'all.globaltests', null ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); @@ -405,10 +405,10 @@ END;'; --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' ) + ut3.ut_annotation( 2, 'suite', null, null ), + ut3.ut_annotation( 3, 'displayname', 'Name of suite', null ), + ut3.ut_annotation( 4, 'suitepath', 'all.globaltests', null ), + ut3.ut_annotation( 6, 'test', null, 'very_long_procedure_name_valid_for_oracle_12_so_utPLSQL_should_allow_it_definitely_well_still_not_reached_128_but_wait_we_dit_it' ) ); ut.expect(anydata.convertCollection(l_actual)).to_equal(anydata.convertCollection(l_expected)); diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb index 119c5c547..db75417c6 100644 --- a/test/core/test_suite_builder.pkb +++ b/test/core/test_suite_builder.pkb @@ -510,7 +510,9 @@ create or replace package body test_suite_builder is '%' || '%' || '%test_in_a_context' || + '%%suite_level_beforeeach%' || '%%test_in_a_context%' || + '%%suite_level_aftereach%' || '%' || '%' || '%' || @@ -525,8 +527,6 @@ create or replace package body test_suite_builder is '%%suite_level_afterall%' || '%
' ); - ut.expect(l_actual).not_to_be_like('%%%%%%'); - ut.expect(l_actual).not_to_be_like('%%%%%%'); ut.expect(l_actual).not_to_be_like('%%%%%%'); ut.expect(l_actual).not_to_be_like('%%%%%%'); end; diff --git a/test/core/test_suite_builder.pks b/test/core/test_suite_builder.pks index c723e36cd..c02c634db 100644 --- a/test/core/test_suite_builder.pks +++ b/test/core/test_suite_builder.pks @@ -59,7 +59,7 @@ create or replace package test_suite_builder is --%test(Associates before/after all/each to tests in context only) procedure before_after_in_context; - --%test(Does not propagate before/after each to context) + --%test(Propagates beforeeach/aftereach to context) procedure before_after_out_of_context; --%test(Does not create context and gives warning when endcontext is missing) diff --git a/test/core/test_ut_utils.pkb b/test/core/test_ut_utils.pkb index 7efe3b0ef..dca96011c 100644 --- a/test/core/test_ut_utils.pkb +++ b/test/core/test_ut_utils.pkb @@ -354,5 +354,62 @@ end;'; --Assert ut.expect(anydata.convertcollection(l_list_to_be_empty)).to_be_empty; end; + + procedure replace_multiline_comments + is + l_source clob; + l_actual clob; + l_expected clob; + begin + --Arrange + l_source := q'[ +create or replace package dummy as + + -- single line comment with disabled /* multi-line comment */ + gv_text0 varchar2(200) := q'{/* multi-line comment + in escaped q'multi-line + string*/}'; + gv_text1 varchar2(200) := '/* multi-line comment in a string*/'; + gv_text2 varchar2(200) := '/* multi-line comment + in a multi-line + string*/'; + -- ignored start of multi-line comment /* + -- ignored end of multi-line comment */ + /* proper + multi-line comment */ + gv_text3 varchar2(200) := 'some text'; /* multiline comment*/ --followed by single-line comment + /* multi-line comment in one line*/ + gv_text4 varchar2(200) := q'{/* multi-line comment + in escaped q'multi-line + string*/}'; +end; +]'; + l_expected := q'[ +create or replace package dummy as + + -- single line comment with disabled /* multi-line comment */ + gv_text0 varchar2(200) := q'{/* multi-line comment + in escaped q'multi-line + string*/}'; + gv_text1 varchar2(200) := '/* multi-line comment in a string*/'; + gv_text2 varchar2(200) := '/* multi-line comment + in a multi-line + string*/'; + -- ignored start of multi-line comment /* + -- ignored end of multi-line comment */ + ]'||q'[ + + gv_text3 varchar2(200) := 'some text'; --followed by single-line comment + ]'||q'[ + gv_text4 varchar2(200) := q'{/* multi-line comment + in escaped q'multi-line + string*/}'; +end; +]'; + --Act + l_actual := ut3.ut_utils.replace_multiline_comments(l_source); + --Assert + ut.expect(l_actual).to_equal(l_expected); + end; end test_ut_utils; / diff --git a/test/core/test_ut_utils.pks b/test/core/test_ut_utils.pks index 597628b23..4d5ce56af 100644 --- a/test/core/test_ut_utils.pks +++ b/test/core/test_ut_utils.pks @@ -73,5 +73,9 @@ create or replace package test_ut_utils is --%test(Filter list elements with empty collection) procedure filter_list_empty_collection; + + --%test(Replace multi-line comments with empty lines) + procedure replace_multiline_comments; + end test_ut_utils; / From 71d0b7041e0ee5b34b05daf056a5508708ccf855 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sat, 14 Apr 2018 23:25:27 +0100 Subject: [PATCH 18/28] Fixed issues with comment-replace in big package specs. --- .../RunExampleTestAnnotationsParsingTimeHugePackage.sql | 9 ++++++++- source/core/annotations/ut_annotation_parser.pkb | 7 ++----- source/core/ut_utils.pkb | 6 +++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql b/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql index 8d9ecad0c..aea43e8aa 100644 --- a/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql +++ b/examples/developer_examples/RunExampleTestAnnotationsParsingTimeHugePackage.sql @@ -1,14 +1,21 @@ --Shows that even a very large package specification can be parsed quite quickly --Clear Screen -Set Serveroutput On Size Unlimited format truncated +set serveroutput on set echo off --install the example unit test packages @@tst_pkg_huge.pks declare l_suites ut_suite_items; + l_items ut_suite_items; begin l_suites := ut_suite_manager.configure_execution_by_path(ut_varchar2_list(USER||'.TST_PKG_HUGE')); + l_items := treat( + treat( treat( l_suites( 1 ) as ut_logical_suite ).items( 1 ) as ut_logical_suite ).items( 1 ) + as ut_logical_suite + ).items; + dbms_output.put_line('Created '||l_items.count||' tests in suite'); + dbms_output.put_line(' Last test name='||l_items(l_items.last).name); end; / diff --git a/source/core/annotations/ut_annotation_parser.pkb b/source/core/annotations/ut_annotation_parser.pkb index 0674461c5..84efbaa91 100644 --- a/source/core/annotations/ut_annotation_parser.pkb +++ b/source/core/annotations/ut_annotation_parser.pkb @@ -130,10 +130,7 @@ create or replace package body ut_annotation_parser as -- parse the comment block for the syntactically correct annotations and store them as an array add_annotations(a_annotations, l_proc_comments, a_comments, l_proc_name); - l_annot_proc_ind := regexp_instr(srcstr => a_source - ,pattern => ';' - ,occurrence => 1 - ,position => l_annot_proc_ind + length(l_annot_proc_block) ); + l_annot_proc_ind := instr(a_source, ';', l_annot_proc_ind + length(l_annot_proc_block) ); end loop; end add_procedure_annotations; @@ -197,7 +194,7 @@ create or replace package body ut_annotation_parser as l_comment_index positive; begin - l_source := ut_utils.replace_multiline_comments(l_source); + l_source := ut_utils.replace_multiline_comments(l_source); -- replace all single line comments with {COMMENT#12} element and store it's content for easier processing -- this call modifies l_source diff --git a/source/core/ut_utils.pkb b/source/core/ut_utils.pkb index 226c32200..0d0a7e10a 100644 --- a/source/core/ut_utils.pkb +++ b/source/core/ut_utils.pkb @@ -616,9 +616,9 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab, end if; if l_end = 0 then - append_to_clob(l_result, dbms_lob.substr(a_source, l_length-l_offset, l_offset)); + append_to_clob(l_result, substr(a_source, l_offset, l_length-l_offset)); else - append_to_clob(l_result, dbms_lob.substr(a_source, l_end-l_offset, l_offset)); + append_to_clob(l_result, substr(a_source, l_offset, l_end-l_offset)); end if; end if; l_offset := l_end; @@ -635,7 +635,7 @@ procedure append_to_clob(a_src_clob in out nocopy clob, a_clob_table t_clob_tab, l_escaped_text_start := instr(a_source,q'[q']',l_offset); end if; end loop; - append_to_clob(l_result, dbms_lob.substr(a_source, offset=>l_end)); + append_to_clob(l_result, substr(a_source, l_end)); return l_result; end; From 8617613eb382face7afbb4fc4a7d9024d813c507 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Mon, 16 Apr 2018 21:37:09 +0100 Subject: [PATCH 19/28] Updated test for comments removal to include multi-line comments with multibyte text in it. --- test/core/test_ut_utils.pkb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/core/test_ut_utils.pkb b/test/core/test_ut_utils.pkb index dca96011c..36f849aae 100644 --- a/test/core/test_ut_utils.pkb +++ b/test/core/test_ut_utils.pkb @@ -373,10 +373,12 @@ create or replace package dummy as gv_text2 varchar2(200) := '/* multi-line comment in a multi-line string*/'; - -- ignored start of multi-line comment /* - -- ignored end of multi-line comment */ - /* proper - multi-line comment */ + -- ignored start of multi-line comment with multi-byte text � /* + -- ignored end of multi-line comment with multi-byte text � */ + /* multi-line comment + with + multi-byte characters ��� + in it */ gv_text3 varchar2(200) := 'some text'; /* multiline comment*/ --followed by single-line comment /* multi-line comment in one line*/ gv_text4 varchar2(200) := q'{/* multi-line comment @@ -395,10 +397,12 @@ create or replace package dummy as gv_text2 varchar2(200) := '/* multi-line comment in a multi-line string*/'; - -- ignored start of multi-line comment /* - -- ignored end of multi-line comment */ + -- ignored start of multi-line comment with multi-byte text � /* + -- ignored end of multi-line comment with multi-byte text � */ ]'||q'[ + + gv_text3 varchar2(200) := 'some text'; --followed by single-line comment ]'||q'[ gv_text4 varchar2(200) := q'{/* multi-line comment From 51ba943a1dd346b8ae73c8c025fe110f718921a0 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Mon, 16 Apr 2018 23:24:04 +0100 Subject: [PATCH 20/28] Updated documentation with new and changed annotations. --- docs/userguide/annotations.md | 209 ++++++++++++++++++++++------------ 1 file changed, 137 insertions(+), 72 deletions(-) diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index ef3ff83a4..06f59bdd5 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -1,14 +1,17 @@ # Annotations Annotations are used to configure tests and suites in a declarative way similar to modern OOP languages. This way, test configuration is stored along with the test logic inside the test package. -No configuration files or tables are needed. The annotation names are based on popular testing frameworks such as jUnit. +No configuration files or tables are needed. The annotation names are based on popular testing frameworks such as JUnit. The framework runner searches for all the suitable annotated packages, automatically configures suites, forms the suite hierarchy, executes it and reports results in specified formats. Annotations are interpreted only in the package specification and are case-insensitive. We strongly recommend using lower-case annotations as described in this documentation. -There are two locations where annotations can be placed: -- Package level annotations can be placed at the very top of the package specification (`--%suite`, `--%suitepath` etc.) -- Procedure level annotations can be placed right before a procedure (`--%test`, `--%beforeall`, `--%beforeeach` etc.) +There are two distinct types of annotations, identified by their location in package: +- Procedure level annotations - placed directly before a procedure (`--%test`, `--%beforeall`, `--%beforeeach` etc.). +- Package level annotations - placed at any place in package except directly before procedure (`--%suite`, `--%suitepath` etc.). + +We strongly recommend putting package level annotations at the very top of package except for the `--%context` annotations (described below) + If procedure level annotation is not placed right before procedure, it is not considered an annotation for procedure. @@ -35,10 +38,10 @@ create or replace package test_pkg is procedure yet_another_test; end test_pkg; ``` -Procedure annotations are defined right before the procedure they reference, no empty lines are allowed, no comment lines can exist between annotation and the procedure. +Procedure level annotations must be defined right before the procedure they reference, no empty lines are allowed, no comment lines can exist between annotation and the procedure. -Package level annotations need to be separated by at least one empty line from the underlying procedure annotations. +Package level annotations need to be separated by at least one empty line from a procedure procedure or procedure annotation. Example of invalid package level annotation. ```sql @@ -48,42 +51,57 @@ create or replace package test_pkg is procedure first_test; end test_pkg; ``` +In the above example, the `--%suite` annotation is ignored, as it is in fact associated with `procedure first_test`. Association with procedure takes precedence over association with package. -If a package specification contains the `--%suite` annotation, it is treated as a test package and is processed by the framework. - -Some annotations accept parameters like `--%suite`, `--%test` and `--%displayname`. The parameters for annotations need to be placed in brackets. -Values for parameters should be provided without any quotation marks. -If the parameters are placed without brackets or with incomplete brackets, they will be ignored. -Example: `--%suite(The name of suite without closing bracket` - -# Example of an annotated test package +## Order of execution ```sql -create or replace package test_pkg is +create or replace package test_employee_pkg is - --%suite(Name of suite) - --%suitepath(all.globaltests) + --%suite(Employee management) + --%suitepath(com.my_company.hr) + --%rollback(auto) + + --%beforeall + procedure setup_employees; --%beforeall - procedure global_setup; + procedure setup_departments; --%afterall - procedure global_cleanup; + procedure cleanup_log_table; - /* Such comments are allowed */ + --%context(add_employee) - --%test - --%displayname(Name of a test) - --%throws(-20145,-20146,-20189,-20563) - procedure some_test; + --%beforeeach + procedure setup_for_add_employees; - --%test(Name of another test) + --%test(Raises exception when employee already exists) + --%throws(-20145) + procedure add_existing_employee; + + --%test(Inserts employee to emp table) + procedure add_employee; + + --%endcontext + + + --%context(remove_employee) + + --%beforeall + procedure setup_for_remove_employee; + + --%test(Removed employee from emp table) + procedure del_employee; + + --%endcontext + + --%test(Test without context) --%beforetest(setup_another_test) --%aftertest(cleanup_another_test) - procedure another_test; + procedure some_test; - --%test - --%displayname(Name of test) + --%test(Name of test) --%disabled procedure disabled_test; @@ -96,15 +114,76 @@ create or replace package test_pkg is procedure cleanup_another_test; --%beforeeach - procedure test_setup; + procedure set_session_context; --%aftereach - procedure test_cleanup; + procedure cleanup_session_context; -end test_pkg; +end test_employee_pkg; ``` -# Supported annotations +When processing the test suite `test_employee_pkg` defined in [Example of annotated test package](#example), the order of execution will be as follows. + +``` + create a savepoint 'before-suite' + execute setup_employees (--%beforeall) + execute setup_departments (--%beforeall) + + create a savepoint 'before-context' + create savepoint 'before-test' + execute test_setup (--%beforeeach) + execute setup_for_add_employees (--%beforeeach from context) + execute add_existing_employee (--%test) + execute test_cleanup (--%aftereach) + rollback to savepoint 'before-test' + create savepoint 'before-test' (--%suite) + execute test_setup (--%beforeeach) + execute setup_for_add_employees (--%beforeeach from context) + execute add_employee (--%test) + execute test_cleanup (--%aftereach) + rollback to savepoint 'before-test' + rollback to savepoint 'before-context' + + create a savepoint 'before-context' + execute setup_for_remove_employee (--%beforeall from context) + create savepoint 'before-test' + execute test_setup (--%beforeeach) + execute add_existing_employee (--%test) + execute test_cleanup (--%aftereach) + rollback to savepoint 'before-test' + rollback to savepoint 'before-context' + + create savepoint 'before-test' + execute test_setup (--%beforeeach) + execute some_test (--%test) + execute test_cleanup (--%aftereach) + rollback to savepoint 'before-test' + + create savepoint 'before-test' + execute test_setup (--%beforeeach) + execute setup_another_test (--%beforetest) + execute another_test (--%test) + execute cleanup_another_test (--%aftertest) + execute test_cleanup (--%beforeeach) + rollback to savepoint 'before-test' + + mark disabled_test as disabled (--%test --%disabled) + + execute test_setup (--%beforeeach) + execute no_transaction_control_test (--%test) + execute test_cleanup (--%aftertest) + + execute global_cleanup (--%afterall) + rollback to savepoint 'before-suite' +``` + +**Note** +>utPLSQL does not guarantee ordering of tests in suite. On contrary utPLSQL might give random order of tests/contexts in suite. +> +>Order of execution within multiple occurrences of `before`/`after` procedures is determined by the order of annotations in specific block (context/suite) of package specification. + + +## Supported annotations | Annotation |Level| Description | | --- | --- | --- | @@ -121,10 +200,25 @@ end test_pkg; | `--%aftertest()` | Procedure | Denotes that mentioned procedure should be executed after the annotated `%test` procedure. | | `--%rollback()` | Package/procedure | Defines transaction control. Supported values: `auto`(default) - a savepoint is created before invocation of each "before block" is and a rollback to specific savepoint is issued after each "after" block; `manual` - rollback is never issued automatically. Property can be overridden for child element (test in suite) | | `--%disabled` | Package/procedure | Used to disable a suite or a test. Disabled suites/tests do not get executed, they are however marked and reported as disabled in a test run. | +| `--%context()` | Package | Denotes start of a nested context (sub-suite) in a suite package | +| `--%endcontext` | Package | Denotes end of a nested context (sub-suite) in a suite package | + + +**Note** +>Package is considered a test-suite only when package specification contains the `--%suite` annotation at the package level. +> +>Some annotations like `--%suite`, `--%test` and `--%displayname` accept parameters. The parameters for annotations need to be placed in brackets. +Values for parameters should be provided without any quotation marks. +If the parameters are placed without brackets or with incomplete brackets, they will be ignored. +> +>Example: `--%suite(The name of suite without closing bracket` # Suitepath concept -It is very likely that the application for which you are going to introduce tests consists of many different packages or procedures/functions. Usually procedures can be logically grouped inside a package, there also might be several logical groups of procedure in a single package or even packages themselves might relate to a common module. +It is very likely that the application for which you are going to introduce tests consists of many different packages, procedures and functions. +Usually procedures can be logically grouped inside a package, there also might be several logical groups of procedures in a single package and packages might be grouped into modules and modules into subject areas. + +As your project grows, the codebase will grow to. utPLSQL allows you to group packages into modules and modules into Let's say you have a complex insurance application that deals with policies, claims and payments. The payment module contains several packages for payment recognition, charging, planning etc. The payment recognition module among others contains a complex `recognize_payment` procedure that associates received money to the policies. @@ -135,7 +229,6 @@ If you want to create tests for your application it is recommended to structure * Payment tests * Payments recognition * Payments set off - * Payouts The `%suitepath` annotation is used for such grouping. Even though test packages are defined in a flat structure the `%suitepath` is used by the framework to form them into a hierarchical structure. Your payments recognition test package might look like: @@ -148,8 +241,7 @@ create or replace package test_payment_recognition as --%test(Recognize payment by policy number) procedure test_recognize_by_num; - --%test - --%displayname(Recognize payment by payment purpose) + --%test(Recognize payment by payment purpose) procedure test_recognize_by_purpose; --%test(Recognize payment by customer) @@ -165,12 +257,11 @@ create or replace package test_payment_set_off as --%suite(Payment set off tests) --%suitepath(payments) - --%test(Set off creation test) + --%test(Creates set off) procedure test_create_set_off; - --%test - --%displayname(Set off annulation test) - procedure test_annulate_set_off; + --%test(Cancels set off) + procedure test_cancel_set_off; end test_payment_set_off; ``` @@ -221,38 +312,12 @@ Doing so allows your tests to use the framework's automatic transaction control When you are testing code that performs explicit or implicit commits, you may set the test procedure to run as an autonomous transaction with `pragma autonomous_transaction`. Keep in mind that when your test runs as autonomous transaction it will not see the data prepared in a setup procedure unless the setup procedure committed the changes. -# Order of execution - -When processing the test suite `test_pkg` defined in [Example of annotated test package](#example), the order of execution will be as follows. - -``` - create a savepoint 'beforeall' - execute global_setup - - create savepoint 'beforeeach' - execute test_setup - execute some_test - execute test_cleanup - rollback to savepoint 'beforeeach' - - create savepoint 'beforeeach' - execute test_setup - execute setup_another_test - execute another_test - execute cleanup_another_test - execute test_cleanup - rollback to savepoint 'beforeeach' - - mark disabled_test as disabled - - execute test_setup - execute no_transaction_control_test - execute test_cleanup - - execute global_cleanup - rollback to savepoint 'beforeall' - -``` +**Note** +> The `--%suitepath` annotation, when used, must be provided with a value of path. +> The path in suitepath cannot contain spaces. Dot (.) identifies individual elements of the path. +> +> Example: `--%suitepath(org.utplsql.core.utils)` +> # Annotation cache From ad3f8304fabddeac24bc084af99341e8af9eada2 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Thu, 19 Apr 2018 01:22:22 +0100 Subject: [PATCH 21/28] Changed behaviour for duplicate annotations - first wins. Added additional info in format: `at "OWNER.PACKAGE[.PROCEDURE]", line x` to annotation warnings. --- source/core/ut_suite_builder.pkb | 130 ++++++++++++++++++++----------- test/core/test_suite_builder.pkb | 68 ++++++++-------- test/core/test_suite_builder.pks | 8 +- 3 files changed, 121 insertions(+), 85 deletions(-) diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index c016a03f5..1f855e29f 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -66,11 +66,13 @@ create or replace package body ut_suite_builder is end; function get_procedure_annotations(a_annotations ut_annotations, a_index binary_integer) return tt_procedure_annotations is - l_result tt_procedure_annotations; - l_index binary_integer := a_index; + l_result tt_procedure_annotations; + l_index binary_integer := a_index; + l_annotation_pos binary_integer; begin loop - l_result(a_annotations(l_index).name)(l_index) := a_annotations(l_index).text; + l_annotation_pos := a_annotations(l_index).position; + l_result(a_annotations(l_index).name)(l_annotation_pos) := a_annotations(l_index).text; exit when is_last_annotation_for_proc(a_annotations, l_index); l_index := a_annotations.next(l_index); end loop; @@ -78,21 +80,23 @@ create or replace package body ut_suite_builder is end; function convert_package_annotations(a_object ut_annotated_object) return t_package_annotations_info is - l_result t_package_annotations_info; - l_annotation_no binary_integer; + l_result t_package_annotations_info; + l_annotation_no binary_integer; + l_annotation_pos binary_integer; begin l_result.owner := a_object.object_owner; l_result.name := a_object.object_name; l_annotation_no := a_object.annotations.first; while l_annotation_no is not null loop + l_annotation_pos := a_object.annotations(l_annotation_no).position; if a_object.annotations(l_annotation_no).subobject_name is null then - l_result.annotations(l_annotation_no).name := a_object.annotations(l_annotation_no).name; - l_result.annotations(l_annotation_no).text := a_object.annotations(l_annotation_no).text; + l_result.annotations(l_annotation_pos).name := a_object.annotations(l_annotation_no).name; + l_result.annotations(l_annotation_pos).text := a_object.annotations(l_annotation_no).text; else - l_result.annotations(l_annotation_no).procedure_name := a_object.annotations(l_annotation_no).subobject_name; - l_result.annotations(l_annotation_no).procedure_annotations := get_procedure_annotations(a_object.annotations, l_annotation_no); - if l_result.annotations(l_annotation_no).procedure_annotations.count > 0 then - l_annotation_no := l_annotation_no + l_result.annotations(l_annotation_no).procedure_annotations.count - 1; + l_result.annotations(l_annotation_pos).procedure_name := a_object.annotations(l_annotation_no).subobject_name; + l_result.annotations(l_annotation_pos).procedure_annotations := get_procedure_annotations(a_object.annotations, l_annotation_no); + if l_result.annotations(l_annotation_pos).procedure_annotations.count > 0 then + l_annotation_no := l_annotation_no + l_result.annotations(l_annotation_pos).procedure_annotations.count - 1; end if; end if; l_annotation_no := a_object.annotations.next(l_annotation_no); @@ -101,15 +105,15 @@ create or replace package body ut_suite_builder is end; function build_annotation_index(a_annotations tt_package_annotations ) return tt_annotations_index is - l_result tt_annotations_index; - l_idx binary_integer; + l_result tt_annotations_index; + l_annotation_pos binary_integer; begin - l_idx := a_annotations.first; - while l_idx is not null loop - if a_annotations(l_idx).name is not null then - l_result(a_annotations(l_idx).name)(l_idx) := true; + l_annotation_pos := a_annotations.first; + while l_annotation_pos is not null loop + if a_annotations( l_annotation_pos ).name is not null then + l_result(a_annotations( l_annotation_pos ).name)( l_annotation_pos ) := true; end if; - l_idx := a_annotations.next(l_idx); + l_annotation_pos := a_annotations.next( l_annotation_pos ); end loop; return l_result; end; @@ -204,17 +208,23 @@ create or replace package body ut_suite_builder is procedure warning_on_duplicate_annot( a_suite in out nocopy ut_suite_item, - a_annoations tt_annotations_index, + a_annotations tt_annotations_index, a_for_annotation varchar2 ) is l_annotation_name t_annotation_name; - l_warning varchar2(32767); + line_no binary_integer; begin - if a_annoations.exists(a_for_annotation) then - if a_annoations(a_for_annotation).count > 1 then - a_suite.put_warning( - 'Multiple occurrences of annotation "--%'||a_for_annotation||'" were found. Last occurrence of annotation was used.' - ); + if a_annotations.exists(a_for_annotation) then + if a_annotations(a_for_annotation).count > 1 then + --start from second occurrence of annotation + line_no := a_annotations(a_for_annotation).next( a_annotations(a_for_annotation).first ); + while line_no is not null loop + a_suite.put_warning( + 'Duplicate annotation "--%' || a_for_annotation || '". Annotation ignored.' || chr( 10 ) + || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name ) || '", line ' || line_no + ); + line_no := a_annotations(a_for_annotation).next(line_no); + end loop; end if; end if; end; @@ -228,19 +238,23 @@ create or replace package body ut_suite_builder is ) is l_annotation_name t_annotation_name; l_warning varchar2(32767); + line_no binary_integer; begin l_annotation_name := a_proc_annotations.first; while l_annotation_name is not null loop if l_annotation_name member of a_invalid_annotations then - l_warning := l_warning ||'"--%'|| l_annotation_name || '", '; + line_no := a_proc_annotations(l_annotation_name).first; + while line_no is not null loop + a_suite.put_warning( + 'Annotation "--%' || l_annotation_name || '" cannot be used with annotation: "--%' || a_for_annotation || '"' + || chr( 10 ) || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name||'.'||a_procedure_name ) + || '", line ' || line_no + ); + line_no := a_proc_annotations(l_annotation_name).next(line_no); + end loop; end if; l_annotation_name := a_proc_annotations.next(l_annotation_name); end loop; - if l_warning is not null then - a_suite.put_warning( - 'Annotations: '||rtrim(l_warning,', ')||' were ignored for procedure "'||upper(a_procedure_name)||'".' || - ' Those annotations cannot be used with annotation: "--%'||a_for_annotation||'"'); - end if; end; procedure add_test( @@ -257,17 +271,21 @@ create or replace package body ut_suite_builder is if a_annotations.exists('displayname') then l_annotation_texts := a_annotations('displayname'); --take the last definition if more than one was provided - l_test.description := l_annotation_texts(l_annotation_texts.last); + l_test.description := l_annotation_texts(l_annotation_texts.first); --TODO if more than one - warning end if; - l_test.description := coalesce(l_test.description,a_annotations('test')(a_annotations('test').last)); + l_test.description := coalesce(l_test.description,a_annotations('test')(a_annotations('test').first)); l_test.path := a_suite.path ||'.'||a_procedure_name; if a_annotations.exists('rollback') then l_annotation_texts := a_annotations('rollback'); - l_test.rollback_type := get_rollback_type(l_annotation_texts(l_annotation_texts.last)); + l_test.rollback_type := get_rollback_type(l_annotation_texts(l_annotation_texts.first)); if l_test.rollback_type is null then - a_suite.put_warning('"--%rollback" annotation requires one of values: "auto" or "manual". Annotation ignored.'); + a_suite.put_warning( + '"--%rollback" annotation requires one of values: "auto" or "manual". Annotation ignored.' + || chr( 10 ) || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name||'.'||a_procedure_name ) + || '", line ' || l_annotation_texts.first + ); end if; end if; @@ -388,34 +406,50 @@ create or replace package body ut_suite_builder is l_object_name := a_suite.object_name; end if; if a_package_ann_index.exists('suitepath') then - l_annotation_text := trim(a_annotations(a_package_ann_index('suitepath').last).text); + l_annotation_text := trim(a_annotations(a_package_ann_index('suitepath').first).text); if l_annotation_text is not null then if regexp_like(l_annotation_text,'^((\w|[$#])+\.)*(\w|[$#])+$') then a_suite.path := l_annotation_text||'.'||l_object_name; else - a_suite.put_warning('Invalid path value in annotation "--%suitepath('||l_annotation_text||')". Annotation ignored.'); + a_suite.put_warning( + 'Invalid path value in annotation "--%suitepath('||l_annotation_text||')". Annotation ignored.' + || chr( 10 ) || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name ) + || '", line ' || a_package_ann_index('suitepath').first + ); end if; else - a_suite.put_warning('"--%suitepath" annotation requires a non-empty value. Annotation ignored.'); + a_suite.put_warning( + '"--%suitepath" annotation requires a non-empty value. Annotation ignored.' + || chr( 10 ) || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name ) + || '", line ' || a_package_ann_index('suitepath').first + ); end if; warning_on_duplicate_annot(a_suite, a_package_ann_index, 'suitepath'); end if; a_suite.path := lower(coalesce(a_suite.path, l_object_name)); if a_package_ann_index.exists('displayname') then - l_annotation_text := trim(a_annotations(a_package_ann_index('displayname').last).text); + l_annotation_text := trim(a_annotations(a_package_ann_index('displayname').first).text); if l_annotation_text is not null then a_suite.description := l_annotation_text; else - a_suite.put_warning('"--%displayname" annotation requires a non-empty value. Annotation ignored.'); + a_suite.put_warning( + '"--%displayname" annotation requires a non-empty value. Annotation ignored.' + || chr( 10 ) || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name ) + || '", line ' || a_package_ann_index('displayname').first + ); end if; warning_on_duplicate_annot(a_suite, a_package_ann_index, 'displayname'); end if; if a_package_ann_index.exists('rollback') then - l_rollback_type := get_rollback_type(a_annotations(a_package_ann_index('rollback').last).text); + l_rollback_type := get_rollback_type(a_annotations(a_package_ann_index('rollback').first).text); if l_rollback_type is null then - a_suite.put_warning('"--%rollback" annotation requires one of values: "auto" or "manual". Annotation ignored.'); + a_suite.put_warning( + '"--%rollback" annotation requires one of values: "auto" or "manual". Annotation ignored.' + || chr( 10 ) || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name ) + || '", line ' || a_package_ann_index('rollback').first + ); end if; warning_on_duplicate_annot(a_suite, a_package_ann_index, 'rollback'); end if; @@ -526,7 +560,10 @@ create or replace package body ut_suite_builder is l_annotation_pos := a_package_ann_index('context').first; while l_annotation_pos is not null loop a_suite.put_warning( - 'Annotation "--%context('||a_annotations(l_annotation_pos).text||')" was ignored. Cannot find following "--%endcontext".'); + 'Annotation "--%context('||a_annotations(l_annotation_pos).text||')" was ignored. Cannot find following "--%endcontext".' + || chr( 10 ) || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name ) + || '", line ' || a_package_ann_index('context').first + ); l_annotation_pos := a_package_ann_index('context').next(l_annotation_pos); end loop; end if; @@ -534,7 +571,10 @@ create or replace package body ut_suite_builder is l_annotation_pos := a_package_ann_index('endcontext').first; while l_annotation_pos is not null loop a_suite.put_warning( - 'Annotation "--%endcontext" was ignored. Cannot find preceding "--%context".'); + 'Annotation "--%endcontext" was ignored. Cannot find preceding "--%context".' + || chr( 10 ) || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name ) + || '", line ' || a_package_ann_index('endcontext').first + ); l_annotation_pos := a_package_ann_index('endcontext').next(l_annotation_pos); end loop; end if; @@ -553,7 +593,7 @@ create or replace package body ut_suite_builder is --create an incomplete suite l_suite := ut_suite(a_package_annotations.owner, a_package_annotations.name); - l_suite.description := l_annotations(l_package_ann_index('suite').last).text; + l_suite.description := l_annotations(l_package_ann_index('suite').first).text; warning_on_duplicate_annot(l_suite, l_package_ann_index, 'suite'); add_suite_contexts( l_suite, l_annotations, l_package_ann_index ); diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb index db75417c6..5807c25d8 100644 --- a/test/core/test_suite_builder.pkb +++ b/test/core/test_suite_builder.pkb @@ -56,7 +56,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%some_packageAnother description%' + '%some_packageSome description%' ); end; @@ -67,8 +67,8 @@ create or replace package body test_suite_builder is --Arrange l_annotations := ut3.ut_annotations( ut3.ut_annotation(1, 'suite',null, null), - ut3.ut_annotation(2, 'suitepath','dummy.utplsql.some', null), - ut3.ut_annotation(3, 'suitepath','org.utplsql.some', null) + ut3.ut_annotation(2, 'suitepath','org.utplsql.some', null), + ut3.ut_annotation(3, 'suitepath','dummy.utplsql.some', null) ); --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); @@ -93,7 +93,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%some_packageNewest description%' + '%some_packageNew description%' ); end; @@ -104,8 +104,8 @@ create or replace package body test_suite_builder is --Arrange l_annotations := ut3.ut_annotations( ut3.ut_annotation(1, 'suite',null, null), - ut3.ut_annotation(2, 'rollback','bad', null), - ut3.ut_annotation(3, 'rollback','manual', null) + ut3.ut_annotation(2, 'rollback','manual', null), + ut3.ut_annotation(3, 'rollback','bad', null) ); --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); @@ -122,14 +122,14 @@ create or replace package body test_suite_builder is --Arrange l_annotations := ut3.ut_annotations( ut3.ut_annotation(1, 'suite',null, null), - ut3.ut_annotation(2, 'rollback','bad', null), - ut3.ut_annotation(3, 'rollback','manual', null) + ut3.ut_annotation(2, 'rollback','manual', null), + ut3.ut_annotation(3, 'rollback','bad', null) ); --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%%"--%rollback"%%' + '%%"--%rollback"%%UT3_TESTER.SOME_PACKAGE%3%%' ); end; @@ -139,14 +139,14 @@ create or replace package body test_suite_builder is begin --Arrange l_annotations := ut3.ut_annotations( - ut3.ut_annotation(1, 'suite','Blah', null), - ut3.ut_annotation(2, 'suite','Cool', null) + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'suite','bad', null) ); --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%Cool%%"--%suite"%%' + '%Cool%%"--%suite"%UT3_TESTER.SOME_PACKAGE%line 8%%' ); end; @@ -156,15 +156,15 @@ create or replace package body test_suite_builder is begin --Arrange l_annotations := ut3.ut_annotations( - ut3.ut_annotation(1, 'suite','Cool', null), - ut3.ut_annotation(2, 'suitepath','dummy.utplsql.some', null), - ut3.ut_annotation(3, 'suitepath','org.utplsql.some', null) + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(3, 'suitepath','dummy.utplsql.some', null), + ut3.ut_annotation(4, 'suitepath','org.utplsql.some', null) ); --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%%"--%suitepath"%%' + '%%"--%suitepath"%line 4%%' ); end; @@ -174,15 +174,15 @@ create or replace package body test_suite_builder is begin --Arrange l_annotations := ut3.ut_annotations( - ut3.ut_annotation(1, 'suite','Cool', null), - ut3.ut_annotation(3, 'displayname','New description', null), - ut3.ut_annotation(4, 'displayname','Newest description', null) + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(4, 'displayname','New description', null), + ut3.ut_annotation(5, 'displayname','Newest description', null) ); --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%%"--%displayname"%%' + '%%"--%displayname"%line 5%%' ); end; @@ -286,11 +286,11 @@ create or replace package body test_suite_builder is ut3.ut_annotation(7, 'aftereach',null, 'another_after_each'), ut3.ut_annotation(8, 'afterall',null, 'first_after_all'), ut3.ut_annotation(9, 'afterall',null, 'another_after_all'), - ut3.ut_annotation(10, 'test','A test', 'some_test'), - ut3.ut_annotation(11, 'beforetest','before_test_proc', 'some_test'), - ut3.ut_annotation(12, 'beforetest','before_test_proc2', 'some_test'), - ut3.ut_annotation(13, 'aftertest','after_test_proc', 'some_test'), - ut3.ut_annotation(14, 'aftertest','after_test_proc2', 'some_test') + ut3.ut_annotation(14, 'test','A test', 'some_test'), + ut3.ut_annotation(15, 'beforetest','before_test_proc', 'some_test'), + ut3.ut_annotation(16, 'beforetest','before_test_proc2', 'some_test'), + ut3.ut_annotation(18, 'aftertest','after_test_proc', 'some_test'), + ut3.ut_annotation(20, 'aftertest','after_test_proc2', 'some_test') ); --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); @@ -375,15 +375,11 @@ create or replace package body test_suite_builder is --Act l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert - ut.expect(l_actual).to_be_like( - 'UT_SUITEUT3_TESTER' || - 'some_packagesome_packageCool' || - '%Annotations: "--\%afterall", "--\%aftereach", "--\%beforeall", "--\%beforeeach"' || - ' were ignored for procedure "DO_STUFF".' || - ' Those annotations cannot be used with annotation: "--\%test"'|| - '%%some_package%do_stuff%' - ,'\' - ); + ut.expect(l_actual).to_be_like('%%Annotation "--\%beforeall"%line 2%%', '\'); + ut.expect(l_actual).to_be_like('%%Annotation "--\%beforeeach"%line 3%%', '\'); + ut.expect(l_actual).to_be_like('%%Annotation "--\%aftereach"%line 4%%', '\'); + ut.expect(l_actual).to_be_like('%%Annotation "--\%afterall" cannot be used with annotation: "--\%test"' + ||'%at "UT3_TESTER.SOME_PACKAGE.DO_STUFF", line 5%%', '\'); ut.expect(l_actual).not_to_be_like('%%'); ut.expect(l_actual).not_to_be_like('%%'); ut.expect(l_actual).not_to_be_like('%%'); @@ -548,7 +544,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%Annotation "--\%context(A context)" was ignored. Cannot find following "--\%endcontext".%' + '%Annotation "--\%context(A context)" was ignored. Cannot find following "--\%endcontext".%at "UT3_TESTER.SOME_PACKAGE", line 4%' ,'\' ); ut.expect(l_actual).to_be_like( @@ -589,7 +585,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%Annotation "--\%endcontext" was ignored. Cannot find preceding "--\%context".%' + '%Annotation "--\%endcontext" was ignored. Cannot find preceding "--\%context".%at "UT3_TESTER.SOME_PACKAGE", line 9%' ,'\' ); ut.expect(l_actual).to_be_like( diff --git a/test/core/test_suite_builder.pks b/test/core/test_suite_builder.pks index c02c634db..8444c81b8 100644 --- a/test/core/test_suite_builder.pks +++ b/test/core/test_suite_builder.pks @@ -5,16 +5,16 @@ create or replace package test_suite_builder is --%test(Sets suite name from package name and leaves description empty) procedure no_suite_description; - --%test(Sets suite description using last --%suite annotation) + --%test(Sets suite description using first --%suite annotation) procedure suite_description_from_suite; - --%test(Sets suite path using last --%suitepath annotation) + --%test(Sets suite path using first --%suitepath annotation) procedure suitepath_from_non_empty_path; - --%test(Overrides suite description using last --%displayname annotation) + --%test(Overrides suite description using first --%displayname annotation) procedure suite_descr_from_displayname; - --%test(Sets rollback type using last --%rollback annotation) + --%test(Sets rollback type using first --%rollback annotation) procedure rollback_type_valid; --%test(Gives warning if more than one --%rollback annotation used) From 4346b578c0ca384b35776640196b5bb63493d8ac Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Fri, 20 Apr 2018 02:29:55 +0100 Subject: [PATCH 22/28] Updates to documentation. Added additional warnings. --- docs/userguide/annotations.md | 670 ++++++++++++++++++++++++------- source/core/ut_suite_builder.pkb | 71 ++-- test/core/test_suite_builder.pkb | 18 + test/core/test_suite_builder.pks | 3 + 4 files changed, 601 insertions(+), 161 deletions(-) diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index 06f59bdd5..d97be8b5b 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -13,6 +13,536 @@ There are two distinct types of annotations, identified by their location in pac We strongly recommend putting package level annotations at the very top of package except for the `--%context` annotations (described below) +## Supported annotations + +| Annotation |Level| Description | +| --- | --- | --- | +| `--%suite()` | Package | Mandatory. Marks package as a test suite. Optional suite description can be provided (see `displayname`). | +| `--%suitepath()` | Package | Similar to java package. The annotation allows logical grouping of suites into hierarchies. | +| `--%displayname()` | Package/procedure | Human-readable and meaningful description of a suite/test. `%displayname(Name of the suite/test)`. The annotation is provided for flexibility and convenience only. It has exactly the same meaning as `` in `test` and `suite` annotations. If description is provided using both `suite`/`test` and `displayname`, then the one defined as last takes precedence. | +| `--%test()` | Procedure | Denotes that the annotated procedure is a unit test procedure. Optional test description can by provided (see `displayname`). | +| `--%throws([,[,...]])`| Procedure | Denotes that the annotated procedure must throw one of the exception numbers provided. If no valid numbers were provided as annotation parameters the annotation is ignored. Applicable to test procedures only. | +| `--%beforeall` | Procedure | Denotes that the annotated procedure should be executed once before all elements of the suite. | +| `--%afterall` | Procedure | Denotes that the annotated procedure should be executed once after all elements of the suite. | +| `--%beforeeach` | Procedure | Denotes that the annotated procedure should be executed before each `%test` procedure in the suite. | +| `--%aftereach` | Procedure | Denotes that the annotated procedure should be executed after each `%test` procedure in the suite. | +| `--%beforetest()` | Procedure | Denotes that mentioned procedure should be executed before the annotated `%test` procedure. | +| `--%aftertest()` | Procedure | Denotes that mentioned procedure should be executed after the annotated `%test` procedure. | +| `--%rollback()` | Package/procedure | Defines transaction control. Supported values: `auto`(default) - a savepoint is created before invocation of each "before block" is and a rollback to specific savepoint is issued after each "after" block; `manual` - rollback is never issued automatically. Property can be overridden for child element (test in suite) | +| `--%disabled` | Package/procedure | Used to disable a suite or a test. Disabled suites/tests do not get executed, they are however marked and reported as disabled in a test run. | +| `--%context()` | Package | Denotes start of a nested context (sub-suite) in a suite package | +| `--%endcontext` | Package | Denotes end of a nested context (sub-suite) in a suite package | + +### Suite + +The `--%suite` annotation denotes PLSQL package as a unit test suite. +It accepts an optional description that will be visible when running the tests. +When description is not provided, package name is displayed on report. + +Suite package without description. +```sql +create or replace package test_package as + --%suite +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +test_package + +Finished in .002415 seconds +0 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + +Suite package with description. +```sql +create or replace package test_package as + --%suite(Tests for a package) +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + +Finished in .001646 seconds +0 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + +When multiple `--%suite` annotations are specified in package, the first annotation will be used and a warning message will appear indicating duplicate annotation. +```sql +create or replace package test_package as + --%suite(Tests for a package) + --%suite(Bad annotation) +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + + +Warnings: + + 1) test_package + Duplicate annotation "--%suite". Annotation ignored. + at "TESTS_OWNER.TEST_PACKAGE", line 3 + +Finished in .003318 seconds +0 tests, 0 failed, 0 errored, 0 disabled, 1 warning(s) +``` + +When `--%suite` annotation is bound to procedure, it is ignored and results in package not getting recognized as test suite. +```sql +create or replace package test_package as + --%suite(Tests for a package) + procedure some_proc; +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +ORA-20204: Suite package TESTS_OWNER.test_package not found +ORA-06512: at "UT3.UT_RUNNER", line 106 +ORA-06512: at "UT3.UT", line 115 +ORA-06512: at "UT3.UT", line 306 +ORA-06512: at "UT3.UT", line 364 +ORA-06512: at line 1 +``` + + +### Test + +The `--%suite` annotation denotes procedure withing test suite as a unit test. +It accepts an optional description that will be reported when the test is executed. +When description is not provided, procedure name is displayed on report. + + +Test procedure without description. +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test + procedure some_test; +end; +/ +create or replace package body test_package as + procedure some_test is + begin + null; + end; +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + some_test [.003 sec] + +Finished in .004109 seconds +1 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + +Test procedure with description. +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; +end; +/ +create or replace package body test_package as + procedure some_test is + begin + null; + end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.005 sec] + +Finished in .006828 seconds +1 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + +When multiple `--%test` annotations are specified for a procedure, the first annotation will be used and a warning message will appear indicating duplicate annotation. +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + --%test(Duplicate description) + procedure some_test; +end; +/ +create or replace package body test_package as + procedure some_test is + begin + null; + end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.007 sec] + + +Warnings: + + 1) test_package + Duplicate annotation "--%test". Annotation ignored. + at "TESTS_OWNER.TEST_PACKAGE.SOME_TEST", line 5 + +Finished in .008815 seconds +1 tests, 0 failed, 0 errored, 0 disabled, 1 warning(s) +``` + +### Disabled +Marks a suite package or test procedure as disabled. + +Disabling suite. +```sql +create or replace package test_package as + --%suite(Tests for a package) + --%disabled + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; +end; +/ +create or replace package body test_package as + procedure some_test is + begin + null; + end; + procedure other_test is + begin + null; + end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [0 sec] (DISABLED) + Description of another behavior [0 sec] (DISABLED) + +Finished in .001441 seconds +2 tests, 0 failed, 0 errored, 2 disabled, 0 warning(s) +``` + +Disabling individual test(s). +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + --%disabled + procedure other_test; +end; +/ +create or replace package body test_package as + procedure some_test is + begin + null; + end; + procedure other_test is + begin + null; + end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.004 sec] + Description of another behavior [0 sec] (DISABLED) + +Finished in .005868 seconds +2 tests, 0 failed, 0 errored, 1 disabled, 0 warning(s) +``` + +### Beforeall + +Marks a procedure to be executed before all test procedures in a suite. + +Single beforeall procedure. +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + --%beforeall + procedure setup_stuff; + +end; +/ +create or replace package body test_package as + procedure setup_stuff is + begin + dbms_output.put_line('--- SETUP_STUFF invoked ---'); + end; + procedure some_test is + begin + null; + end; + procedure other_test is + begin + null; + end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + --- SETUP_STUFF invoked --- + Description of tesed behavior [.004 sec] + Description of another behavior [.003 sec] + +Finished in .012292 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + + +When you define multiple beforeall procedures, all of them will get executed before invoking any test in package. +Order of execution for beforeall procedures is defined by the position of the `--%beforeall` annotation in the package specification. + ```sql + create or replace package test_package as + --%suite(Tests for a package) + + --%beforeall + procedure initial_setup; + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + --%beforeall + procedure another_setup; + + end; + / + create or replace package body test_package as + procedure another_setup is + begin + dbms_output.put_line('--- ANOTHER_SETUP invoked ---'); + end; + procedure initial_setup is + begin + dbms_output.put_line('--- INITIAL_SETUP invoked ---'); + end; + procedure some_test is + begin + null; + end; + procedure other_test is + begin + null; + end; + end; + / +``` + + ```sql + exec ut.run('test_package'); + ``` + ``` +Tests for a package + --- INITIAL_SETUP invoked --- + --- ANOTHER_SETUP invoked --- + Description of tesed behavior [.004 sec] + Description of another behavior [.004 sec] + +Finished in .016672 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) + ``` + + +### Afterall +Marks a procedure to be executed after all test procedures in a suite. + +### Beforeeach + +### Aftereach + +### Beforetest + +### Aftertest + +### Suitepath + +### Context + +### Rollback + + + + +**Note** +>Package is considered a test-suite only when package specification contains the `--%suite` annotation at the package level. +> +>Some annotations like `--%suite`, `--%test` and `--%displayname` accept parameters. The parameters for annotations need to be placed in brackets. +Values for parameters should be provided without any quotation marks. +If the parameters are placed without brackets or with incomplete brackets, they will be ignored. +> +>Example: `--%suite(The name of suite without closing bracket` + +# Suitepath concept + +It is very likely that the application for which you are going to introduce tests consists of many different packages, procedures and functions. +Usually procedures can be logically grouped inside a package, there also might be several logical groups of procedures in a single package and packages might be grouped into modules and modules into subject areas. + +As your project grows, the codebase will grow to. utPLSQL allows you to group packages into modules and modules into + +Let's say you have a complex insurance application that deals with policies, claims and payments. The payment module contains several packages for payment recognition, charging, planning etc. The payment recognition module among others contains a complex `recognize_payment` procedure that associates received money to the policies. + +If you want to create tests for your application it is recommended to structure your tests similarly to the logical structure of your application. So you end up with something like: +* Integration tests + * Policy tests + * Claim tests + * Payment tests + * Payments recognition + * Payments set off + +The `%suitepath` annotation is used for such grouping. Even though test packages are defined in a flat structure the `%suitepath` is used by the framework to form them into a hierarchical structure. Your payments recognition test package might look like: + +```sql +create or replace package test_payment_recognition as + + --%suite(Payment recognition tests) + --%suitepath(payments) + + --%test(Recognize payment by policy number) + procedure test_recognize_by_num; + + --%test(Recognize payment by payment purpose) + procedure test_recognize_by_purpose; + + --%test(Recognize payment by customer) + procedure test_recognize_by_customer; + +end test_payment_recognition; +``` + +And payments set off test package: +```sql +create or replace package test_payment_set_off as + + --%suite(Payment set off tests) + --%suitepath(payments) + + --%test(Creates set off) + procedure test_create_set_off; + + --%test(Cancels set off) + procedure test_cancel_set_off; + +end test_payment_set_off; +``` + +When you execute tests for your application, the framework constructs a test suite for each test package. Then it combines suites into grouping suites by the `%suitepath` annotation value so that the fully qualified path to the `recognize_by_num` procedure is `USER:payments.test_payment_recognition.test_recognize_by_num`. If any of its expectations fails then the test is marked as failed, also the `test_payment_recognition` suite, the parent suite `payments` and the whole run is marked as failed. +The test report indicates which expectation has failed on the payments module. The payments recognition submodule is causing the failure as `recognize_by_num` has not met the expectations of the test. Grouping tests into modules and submodules using the `%suitepath` annotation allows you to logically organize your project's flat structure of packages into functional groups. + +An additional advantage of such grouping is the fact that every element level of the grouping can be an actual unit test package containing a common module level setup for all of the submodules. So in addition to the packages mentioned above you could have the following package. +```sql +create or replace package payments as + + --%suite(Payments) + + --%beforeall + procedure set_common_payments_data; + + --%afterall + procedure reset_common_paymnets_data; + +end payments; +``` +A `%suitepath` can be provided in three ways: +* schema - execute all tests in the schema +* [schema]:suite1[.suite2][.suite3]...[.procedure] - execute all tests in all suites from suite1[.suite2][.suite3]...[.procedure] path. If schema is not provided, then the current schema is used. Example: `:all.rooms_tests` +* [schema.]package[.procedure] - execute all tests in the specified test package. The whole hierarchy of suites in the schema is built before all before/after hooks or part suites for the provided suite package are executed as well. Example: `tests.test_contact.test_last_name_validator` or simply `test_contact.test_last_name_validator` if `tests` is the current schema. + +# Using automatic rollback in tests + +By default, changes performed by every setup, cleanup and test procedure are isolated by savepoints. +This solution is suitable for use-cases where the code that is being tested as well as the unit tests themselves do not use transaction control (commit/rollback) or DDL commands. + +In general, your unit tests should not use transaction control as long as the code you are testing is not using it too. +Keeping the transactions uncommitted allows your changes to be isolated and the execution of tests does not impact others who might be using a shared development database. + +If you are in a situation where the code you are testing uses transaction control (common case with ETL code), then your tests probably should not use the default automatic transaction control. +In that case use the annotation `--%rollback(manual)` on the suite level to disable automatic transaction control for the entire suite. +If you are using nested suites, you need to make sure that the entire suite all the way to the root is using manual transaction control. + +It is possible with utPLSQL to change the transaction control on individual suites or tests that are part of complex suite. +It is strongly recommended not to have mixed transaction control in a suite. +Mixed transaction control settings will not work properly when your suites are using shared setup/cleanup with beforeall, afterall, beforeeach or aftereach annotations. +Your suite will most likely fail with error or warning on execution. Some of the automatic rollbacks will probably fail to execute depending on the configuration you have. + +In some cases it is necessary to perform DDL as part of setup or cleanup for the tests. +It is recommended to move such DDL statements to a procedure with `pragma autonomous_transaction` to eliminate implicit commits in the main session that is executing all your tests. +Doing so allows your tests to use the framework's automatic transaction control and releases you from the burden of manual cleanup of data that was created or modified by test execution. + +When you are testing code that performs explicit or implicit commits, you may set the test procedure to run as an autonomous transaction with `pragma autonomous_transaction`. +Keep in mind that when your test runs as autonomous transaction it will not see the data prepared in a setup procedure unless the setup procedure committed the changes. + +**Note** +> The `--%suitepath` annotation, when used, must be provided with a value of path. +> The path in suitepath cannot contain spaces. Dot (.) identifies individual elements of the path. +> +> Example: `--%suitepath(org.utplsql.core.utils)` +> + +## Invalid annotations + If procedure level annotation is not placed right before procedure, it is not considered an annotation for procedure. Example of invalid procedure level annotations @@ -183,143 +713,7 @@ When processing the test suite `test_employee_pkg` defined in [Example of annota >Order of execution within multiple occurrences of `before`/`after` procedures is determined by the order of annotations in specific block (context/suite) of package specification. -## Supported annotations - -| Annotation |Level| Description | -| --- | --- | --- | -| `--%suite()` | Package | Mandatory. Marks package as a test suite. Optional suite description can be provided (see `displayname`). | -| `--%suitepath()` | Package | Similar to java package. The annotation allows logical grouping of suites into hierarchies. | -| `--%displayname()` | Package/procedure | Human-readable and meaningful description of a suite/test. `%displayname(Name of the suite/test)`. The annotation is provided for flexibility and convenience only. It has exactly the same meaning as `` in `test` and `suite` annotations. If description is provided using both `suite`/`test` and `displayname`, then the one defined as last takes precedence. | -| `--%test()` | Procedure | Denotes that the annotated procedure is a unit test procedure. Optional test description can by provided (see `displayname`). | -| `--%throws([,[,...]])`| Procedure | Denotes that the annotated procedure must throw one of the exception numbers provided. If no valid numbers were provided as annotation parameters the annotation is ignored. Applicable to test procedures only. | -| `--%beforeall` | Procedure | Denotes that the annotated procedure should be executed once before all elements of the suite. | -| `--%afterall` | Procedure | Denotes that the annotated procedure should be executed once after all elements of the suite. | -| `--%beforeeach` | Procedure | Denotes that the annotated procedure should be executed before each `%test` procedure in the suite. | -| `--%aftereach` | Procedure | Denotes that the annotated procedure should be executed after each `%test` procedure in the suite. | -| `--%beforetest()` | Procedure | Denotes that mentioned procedure should be executed before the annotated `%test` procedure. | -| `--%aftertest()` | Procedure | Denotes that mentioned procedure should be executed after the annotated `%test` procedure. | -| `--%rollback()` | Package/procedure | Defines transaction control. Supported values: `auto`(default) - a savepoint is created before invocation of each "before block" is and a rollback to specific savepoint is issued after each "after" block; `manual` - rollback is never issued automatically. Property can be overridden for child element (test in suite) | -| `--%disabled` | Package/procedure | Used to disable a suite or a test. Disabled suites/tests do not get executed, they are however marked and reported as disabled in a test run. | -| `--%context()` | Package | Denotes start of a nested context (sub-suite) in a suite package | -| `--%endcontext` | Package | Denotes end of a nested context (sub-suite) in a suite package | - - -**Note** ->Package is considered a test-suite only when package specification contains the `--%suite` annotation at the package level. -> ->Some annotations like `--%suite`, `--%test` and `--%displayname` accept parameters. The parameters for annotations need to be placed in brackets. -Values for parameters should be provided without any quotation marks. -If the parameters are placed without brackets or with incomplete brackets, they will be ignored. -> ->Example: `--%suite(The name of suite without closing bracket` - -# Suitepath concept - -It is very likely that the application for which you are going to introduce tests consists of many different packages, procedures and functions. -Usually procedures can be logically grouped inside a package, there also might be several logical groups of procedures in a single package and packages might be grouped into modules and modules into subject areas. - -As your project grows, the codebase will grow to. utPLSQL allows you to group packages into modules and modules into - -Let's say you have a complex insurance application that deals with policies, claims and payments. The payment module contains several packages for payment recognition, charging, planning etc. The payment recognition module among others contains a complex `recognize_payment` procedure that associates received money to the policies. - -If you want to create tests for your application it is recommended to structure your tests similarly to the logical structure of your application. So you end up with something like: -* Integration tests - * Policy tests - * Claim tests - * Payment tests - * Payments recognition - * Payments set off - -The `%suitepath` annotation is used for such grouping. Even though test packages are defined in a flat structure the `%suitepath` is used by the framework to form them into a hierarchical structure. Your payments recognition test package might look like: - -```sql -create or replace package test_payment_recognition as - - --%suite(Payment recognition tests) - --%suitepath(payments) - - --%test(Recognize payment by policy number) - procedure test_recognize_by_num; - - --%test(Recognize payment by payment purpose) - procedure test_recognize_by_purpose; - - --%test(Recognize payment by customer) - procedure test_recognize_by_customer; - -end test_payment_recognition; -``` - -And payments set off test package: -```sql -create or replace package test_payment_set_off as - - --%suite(Payment set off tests) - --%suitepath(payments) - - --%test(Creates set off) - procedure test_create_set_off; - - --%test(Cancels set off) - procedure test_cancel_set_off; - -end test_payment_set_off; -``` - -When you execute tests for your application, the framework constructs a test suite for each test package. Then it combines suites into grouping suites by the `%suitepath` annotation value so that the fully qualified path to the `recognize_by_num` procedure is `USER:payments.test_payment_recognition.test_recognize_by_num`. If any of its expectations fails then the test is marked as failed, also the `test_payment_recognition` suite, the parent suite `payments` and the whole run is marked as failed. -The test report indicates which expectation has failed on the payments module. The payments recognition submodule is causing the failure as `recognize_by_num` has not met the expectations of the test. Grouping tests into modules and submodules using the `%suitepath` annotation allows you to logically organize your project's flat structure of packages into functional groups. - -An additional advantage of such grouping is the fact that every element level of the grouping can be an actual unit test package containing a common module level setup for all of the submodules. So in addition to the packages mentioned above you could have the following package. -```sql -create or replace package payments as - - --%suite(Payments) - - --%beforeall - procedure set_common_payments_data; - - --%afterall - procedure reset_common_paymnets_data; - -end payments; -``` -A `%suitepath` can be provided in three ways: -* schema - execute all tests in the schema -* [schema]:suite1[.suite2][.suite3]...[.procedure] - execute all tests in all suites from suite1[.suite2][.suite3]...[.procedure] path. If schema is not provided, then the current schema is used. Example: `:all.rooms_tests` -* [schema.]package[.procedure] - execute all tests in the specified test package. The whole hierarchy of suites in the schema is built before all before/after hooks or part suites for the provided suite package are executed as well. Example: `tests.test_contact.test_last_name_validator` or simply `test_contact.test_last_name_validator` if `tests` is the current schema. - -# Using automatic rollback in tests - -By default, changes performed by every setup, cleanup and test procedure are isolated by savepoints. -This solution is suitable for use-cases where the code that is being tested as well as the unit tests themselves do not use transaction control (commit/rollback) or DDL commands. - -In general, your unit tests should not use transaction control as long as the code you are testing is not using it too. -Keeping the transactions uncommitted allows your changes to be isolated and the execution of tests does not impact others who might be using a shared development database. - -If you are in a situation where the code you are testing uses transaction control (common case with ETL code), then your tests probably should not use the default automatic transaction control. -In that case use the annotation `--%rollback(manual)` on the suite level to disable automatic transaction control for the entire suite. -If you are using nested suites, you need to make sure that the entire suite all the way to the root is using manual transaction control. - -It is possible with utPLSQL to change the transaction control on individual suites or tests that are part of complex suite. -It is strongly recommended not to have mixed transaction control in a suite. -Mixed transaction control settings will not work properly when your suites are using shared setup/cleanup with beforeall, afterall, beforeeach or aftereach annotations. -Your suite will most likely fail with error or warning on execution. Some of the automatic rollbacks will probably fail to execute depending on the configuration you have. - -In some cases it is necessary to perform DDL as part of setup or cleanup for the tests. -It is recommended to move such DDL statements to a procedure with `pragma autonomous_transaction` to eliminate implicit commits in the main session that is executing all your tests. -Doing so allows your tests to use the framework's automatic transaction control and releases you from the burden of manual cleanup of data that was created or modified by test execution. - -When you are testing code that performs explicit or implicit commits, you may set the test procedure to run as an autonomous transaction with `pragma autonomous_transaction`. -Keep in mind that when your test runs as autonomous transaction it will not see the data prepared in a setup procedure unless the setup procedure committed the changes. - -**Note** -> The `--%suitepath` annotation, when used, must be provided with a value of path. -> The path in suitepath cannot contain spaces. Dot (.) identifies individual elements of the path. -> -> Example: `--%suitepath(org.utplsql.core.utils)` -> - -# Annotation cache +## Annotation cache utPLSQL needs to scan the source of package specifications to identify and parse annotations. To improve framework startup time, especially when dealing with database users owning large amounts of packages, the framework has a built-in persistent cache for annotations. @@ -340,7 +734,7 @@ Example: exec ut_runner.purge_cache('HR', 'PACKAGE'); ``` -# Throws annotation +## Throws annotation The `--%throws` annotation allows you to specify a list of exception numbers that can be expected from a test. diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index 1f855e29f..d4283c443 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -60,21 +60,21 @@ create or replace package body ut_suite_builder is --index used to lookup package level annotations by package-level annotation name type tt_annotations_index is table of tt_package_annot_positions index by t_annotation_name; - function is_last_annotation_for_proc(a_annotations ut_annotations, a_index binary_integer) return boolean is - begin - return a_index = a_annotations.count or a_annotations(a_index).subobject_name != nvl(a_annotations(a_index+1).subobject_name, ' '); - end; - function get_procedure_annotations(a_annotations ut_annotations, a_index binary_integer) return tt_procedure_annotations is + function get_procedure_annotations(a_annotations ut_annotations, a_index in out nocopy binary_integer) return tt_procedure_annotations is l_result tt_procedure_annotations; l_index binary_integer := a_index; l_annotation_pos binary_integer; + function is_last_annotation_for_proc(a_annotations ut_annotations, a_index binary_integer) return boolean is + begin + return a_index = a_annotations.count or a_annotations(a_index).subobject_name != nvl(a_annotations(a_index+1).subobject_name, ' '); + end; begin loop - l_annotation_pos := a_annotations(l_index).position; - l_result(a_annotations(l_index).name)(l_annotation_pos) := a_annotations(l_index).text; - exit when is_last_annotation_for_proc(a_annotations, l_index); - l_index := a_annotations.next(l_index); + l_annotation_pos := a_annotations(a_index).position; + l_result(a_annotations(a_index).name)(l_annotation_pos) := a_annotations(a_index).text; + exit when is_last_annotation_for_proc(a_annotations, a_index); + a_index := a_annotations.next(a_index); end loop; return l_result; end; @@ -95,9 +95,6 @@ create or replace package body ut_suite_builder is else l_result.annotations(l_annotation_pos).procedure_name := a_object.annotations(l_annotation_no).subobject_name; l_result.annotations(l_annotation_pos).procedure_annotations := get_procedure_annotations(a_object.annotations, l_annotation_no); - if l_result.annotations(l_annotation_pos).procedure_annotations.count > 0 then - l_annotation_no := l_annotation_no + l_result.annotations(l_annotation_pos).procedure_annotations.count - 1; - end if; end if; l_annotation_no := a_object.annotations.next(l_annotation_no); end loop; @@ -212,23 +209,47 @@ create or replace package body ut_suite_builder is a_for_annotation varchar2 ) is l_annotation_name t_annotation_name; - line_no binary_integer; + l_line_no binary_integer; begin if a_annotations.exists(a_for_annotation) then if a_annotations(a_for_annotation).count > 1 then --start from second occurrence of annotation - line_no := a_annotations(a_for_annotation).next( a_annotations(a_for_annotation).first ); - while line_no is not null loop + l_line_no := a_annotations(a_for_annotation).next( a_annotations(a_for_annotation).first ); + while l_line_no is not null loop a_suite.put_warning( 'Duplicate annotation "--%' || a_for_annotation || '". Annotation ignored.' || chr( 10 ) - || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name ) || '", line ' || line_no + || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name ) || '", line ' || l_line_no ); - line_no := a_annotations(a_for_annotation).next(line_no); + l_line_no := a_annotations(a_for_annotation).next( l_line_no ); end loop; end if; end if; end; + procedure warning_on_duplicate_annot( + a_suite in out nocopy ut_suite_item, + a_procedure_name t_object_name, + a_annotations tt_procedure_annotations, + a_for_annotation varchar2 + ) is + l_annotation_name t_annotation_name; + l_line_no binary_integer; + begin + if a_annotations.exists(a_for_annotation) then + if a_annotations(a_for_annotation).count > 1 then + --start from second occurrence of annotation + l_line_no := a_annotations(a_for_annotation).next( a_annotations(a_for_annotation).first ); + while l_line_no is not null loop + a_suite.put_warning( + 'Duplicate annotation "--%' || a_for_annotation || '". Annotation ignored.' || chr( 10 ) + || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name ||'.'||a_procedure_name) || '", line ' || l_line_no + ); + l_line_no := a_annotations(a_for_annotation).next( l_line_no ); + end loop; + end if; + end if; + end; + procedure warning_on_extra_annotations( a_suite in out nocopy ut_suite_item, a_procedure_name t_object_name, @@ -238,19 +259,19 @@ create or replace package body ut_suite_builder is ) is l_annotation_name t_annotation_name; l_warning varchar2(32767); - line_no binary_integer; + l_line_no binary_integer; begin l_annotation_name := a_proc_annotations.first; while l_annotation_name is not null loop if l_annotation_name member of a_invalid_annotations then - line_no := a_proc_annotations(l_annotation_name).first; - while line_no is not null loop + l_line_no := a_proc_annotations(l_annotation_name).first; + while l_line_no is not null loop a_suite.put_warning( 'Annotation "--%' || l_annotation_name || '" cannot be used with annotation: "--%' || a_for_annotation || '"' || chr( 10 ) || 'at "' || upper( a_suite.object_owner || '.' || a_suite.object_name||'.'||a_procedure_name ) - || '", line ' || line_no + || '", line ' || l_line_no ); - line_no := a_proc_annotations(l_annotation_name).next(line_no); + l_line_no := a_proc_annotations(l_annotation_name).next(l_line_no); end loop; end if; l_annotation_name := a_proc_annotations.next(l_annotation_name); @@ -268,13 +289,17 @@ create or replace package body ut_suite_builder is begin l_test := ut_test(a_suite.object_owner, a_suite.object_name, a_procedure_name); + warning_on_duplicate_annot(a_suite, a_procedure_name, a_annotations, 'test'); +-- warning_on_duplicate_annot(a_suite, a_procedure_name, a_annotations, 'displayname'); +-- warning_on_duplicate_annot(a_suite, a_procedure_name, a_annotations, 'rollback'); if a_annotations.exists('displayname') then l_annotation_texts := a_annotations('displayname'); --take the last definition if more than one was provided l_test.description := l_annotation_texts(l_annotation_texts.first); --TODO if more than one - warning + else + l_test.description := a_annotations('test')(a_annotations('test').first); end if; - l_test.description := coalesce(l_test.description,a_annotations('test')(a_annotations('test').first)); l_test.path := a_suite.path ||'.'||a_procedure_name; if a_annotations.exists('rollback') then diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb index 5807c25d8..3e349ef98 100644 --- a/test/core/test_suite_builder.pkb +++ b/test/core/test_suite_builder.pkb @@ -150,6 +150,24 @@ create or replace package body test_suite_builder is ); end; + procedure test_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'test','Some test', 'test_procedure'), + ut3.ut_annotation(9, 'test','Dup', 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%test"%UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE%line 9%%' + ); + end; + procedure suitepath_annot_duplicated is l_actual clob; l_annotations ut3.ut_annotations; diff --git a/test/core/test_suite_builder.pks b/test/core/test_suite_builder.pks index 8444c81b8..82fd60457 100644 --- a/test/core/test_suite_builder.pks +++ b/test/core/test_suite_builder.pks @@ -23,6 +23,9 @@ create or replace package test_suite_builder is --%test(Gives warning if more than one --%suite annotation used) procedure suite_annot_duplicated; + --%test(Gives warning if more than one --%test annotation used) + procedure test_annot_duplicated; + --%test(Gives warning if more than one --%suitepath annotation used) procedure suitepath_annot_duplicated; From b1ff0fc57edd96675efbe2e70475745c5ec70458 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Fri, 20 Apr 2018 09:23:29 +0100 Subject: [PATCH 23/28] Updates to documentation. Added additional warnings. --- docs/userguide/annotations.md | 110 +++++++++++++++++++------------ source/core/ut_suite_builder.pkb | 10 ++- test/core/test_suite_builder.pkb | 72 ++++++++++++++++++++ test/core/test_suite_builder.pks | 12 ++++ 4 files changed, 158 insertions(+), 46 deletions(-) diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index d97be8b5b..9e045229c 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -136,10 +136,7 @@ create or replace package test_package as end; / create or replace package body test_package as - procedure some_test is - begin - null; - end; + procedure some_test is begin null; end; end; / ``` @@ -164,10 +161,7 @@ create or replace package test_package as end; / create or replace package body test_package as - procedure some_test is - begin - null; - end; + procedure some_test is begin null; end; end; / ``` @@ -194,10 +188,7 @@ create or replace package test_package as end; / create or replace package body test_package as - procedure some_test is - begin - null; - end; + procedure some_test is begin null; end; end; / ``` @@ -237,14 +228,10 @@ create or replace package test_package as end; / create or replace package body test_package as - procedure some_test is - begin - null; - end; - procedure other_test is - begin - null; - end; + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; end; / ``` @@ -275,14 +262,10 @@ create or replace package test_package as end; / create or replace package body test_package as - procedure some_test is - begin - null; - end; - procedure other_test is - begin - null; - end; + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; end; / ``` @@ -324,14 +307,10 @@ create or replace package body test_package as begin dbms_output.put_line('--- SETUP_STUFF invoked ---'); end; - procedure some_test is - begin - null; - end; - procedure other_test is - begin - null; - end; + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; end; / ``` @@ -351,7 +330,8 @@ Finished in .012292 seconds When you define multiple beforeall procedures, all of them will get executed before invoking any test in package. -Order of execution for beforeall procedures is defined by the position of the `--%beforeall` annotation in the package specification. +Order of execution for beforeall procedures is defined by the position of the `--%beforeall` annotation in the package specification. +Note that procedure `another_setup` is also invoked before any test, though it's located at the end of package specification. ```sql create or replace package test_package as --%suite(Tests for a package) @@ -375,18 +355,62 @@ Order of execution for beforeall procedures is defined by the position of the `- begin dbms_output.put_line('--- ANOTHER_SETUP invoked ---'); end; + procedure initial_setup is begin dbms_output.put_line('--- INITIAL_SETUP invoked ---'); end; - procedure some_test is - begin - null; - end; - procedure other_test is + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; + end; + / +``` + + ```sql + exec ut.run('test_package'); + ``` + ``` +Tests for a package + --- INITIAL_SETUP invoked --- + --- ANOTHER_SETUP invoked --- + Description of tesed behavior [.004 sec] + Description of another behavior [.004 sec] + +Finished in .016672 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) + ``` + +When multiple `--%beforeall` annotations are specified for a procedure, the first annotation will be used and a warning message will appear indicating duplicate annotation. +When procedure is annotated as both `--%beforeall` and `--%test`, the procedure will become a test and a warning message will appear indicating invalid annotation combination. +```sql + create or replace package test_package as + --%suite(Tests for a package) + + --%beforeall + --%beforeall + procedure initial_setup; + + --%test(Description of tesed behavior) + --%beforeall + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + end; + / + create or replace package body test_package as + + procedure initial_setup is begin - null; + dbms_output.put_line('--- INITIAL_SETUP invoked ---'); end; + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; end; / ``` diff --git a/source/core/ut_suite_builder.pkb b/source/core/ut_suite_builder.pkb index d4283c443..3ef2dfe80 100644 --- a/source/core/ut_suite_builder.pkb +++ b/source/core/ut_suite_builder.pkb @@ -289,9 +289,6 @@ create or replace package body ut_suite_builder is begin l_test := ut_test(a_suite.object_owner, a_suite.object_name, a_procedure_name); - warning_on_duplicate_annot(a_suite, a_procedure_name, a_annotations, 'test'); --- warning_on_duplicate_annot(a_suite, a_procedure_name, a_annotations, 'displayname'); --- warning_on_duplicate_annot(a_suite, a_procedure_name, a_annotations, 'rollback'); if a_annotations.exists('displayname') then l_annotation_texts := a_annotations('displayname'); --take the last definition if more than one was provided @@ -360,6 +357,13 @@ create or replace package body ut_suite_builder is a_after_each_list in out nocopy ut_executables ) is begin + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, 'test'); + -- warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, 'displayname'); + -- warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, 'rollback'); + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, 'beforeall'); + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, 'beforeeach'); + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, 'afterall'); + warning_on_duplicate_annot(a_suite, a_procedure_name, a_proc_annotations, 'aftereach'); if a_proc_annotations.exists('test') then add_test( a_suite, a_procedure_name, a_proc_annotations); diff --git a/test/core/test_suite_builder.pkb b/test/core/test_suite_builder.pkb index 3e349ef98..5133ac834 100644 --- a/test/core/test_suite_builder.pkb +++ b/test/core/test_suite_builder.pkb @@ -168,6 +168,78 @@ create or replace package body test_suite_builder is ); end; + procedure beforeall_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'beforeall', null, 'test_procedure'), + ut3.ut_annotation(9, 'beforeall', null, 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%beforeall"%UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE%line 9%%' + ); + end; + + procedure beforeeach_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'beforeeach', null, 'test_procedure'), + ut3.ut_annotation(9, 'beforeeach', null, 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%beforeeach"%UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE%line 9%%' + ); + end; + + procedure afterall_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'afterall', null, 'test_procedure'), + ut3.ut_annotation(9, 'afterall', null, 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%afterall"%UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE%line 9%%' + ); + end; + + procedure aftereach_annot_duplicated is + l_actual clob; + l_annotations ut3.ut_annotations; + begin + --Arrange + l_annotations := ut3.ut_annotations( + ut3.ut_annotation(2, 'suite','Cool', null), + ut3.ut_annotation(8, 'aftereach', null, 'test_procedure'), + ut3.ut_annotation(9, 'aftereach', null, 'test_procedure') + ); + --Act + l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); + --Assert + ut.expect(l_actual).to_be_like( + '%Cool%%"--%aftereach"%UT3_TESTER.SOME_PACKAGE.TEST_PROCEDURE%line 9%%' + ); + end; + procedure suitepath_annot_duplicated is l_actual clob; l_annotations ut3.ut_annotations; diff --git a/test/core/test_suite_builder.pks b/test/core/test_suite_builder.pks index 82fd60457..d8bf40164 100644 --- a/test/core/test_suite_builder.pks +++ b/test/core/test_suite_builder.pks @@ -26,6 +26,18 @@ create or replace package test_suite_builder is --%test(Gives warning if more than one --%test annotation used) procedure test_annot_duplicated; + --%test(Gives warning if more than one --%beforeall annotation used) + procedure beforeall_annot_duplicated; + + --%test(Gives warning if more than one --%beforeeach annotation used) + procedure beforeeach_annot_duplicated; + + --%test(Gives warning if more than one --%afterall annotation used) + procedure afterall_annot_duplicated; + + --%test(Gives warning if more than one --%aftereach annotation used) + procedure aftereach_annot_duplicated; + --%test(Gives warning if more than one --%suitepath annotation used) procedure suitepath_annot_duplicated; From ab5a1ca0ab2953884801094d0bc748d166d1d028 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sun, 22 Apr 2018 20:21:43 +0100 Subject: [PATCH 24/28] Updated and extended documentation for annotations. --- docs/userguide/annotations.md | 421 ++++++++++++++++++++++++++++++++-- 1 file changed, 407 insertions(+), 14 deletions(-) diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index 9e045229c..f3029cfb4 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -121,11 +121,17 @@ ORA-06512: at line 1 ### Test -The `--%suite` annotation denotes procedure withing test suite as a unit test. +The `--%test` annotation denotes procedure withing test suite as a unit test. It accepts an optional description that will be reported when the test is executed. When description is not provided, procedure name is displayed on report. +If `--%test` raises an unhandled exception the following will happen: +- the test will be marked as errored and exception stack trace will be captured and reported +- the `--%aftertest`, `--%aftereach` procedures **will be executed** for the errored test +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for rest of the suite + Test procedure without description. ```sql create or replace package test_package as @@ -212,7 +218,7 @@ Finished in .008815 seconds ``` ### Disabled -Marks a suite package or test procedure as disabled. +Marks annotated suite package or test procedure as disabled. Disabling suite. ```sql @@ -284,9 +290,21 @@ Finished in .005868 seconds ### Beforeall -Marks a procedure to be executed before all test procedures in a suite. +Marks annotated procedure to be executed before all test procedures in a suite. + +If `--%beforeall` raises an exception, suite content cannot be safely executed as the setup was not executed successfully for the suite. + +If `--%beforeall` raises an exception the following will happen: +- the `--%beforeall` procedures that follow the failed one, **will not be executed** +- all `--%test` procedures and their `--%beforeeach`, `--%aftereach`, `--%beforetest` and `--%aftertest` procedures within suite package **will not be executed** +- all `--%test` procedures **will be marked as failed** +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for other suite packages + +When multiple `--%beforeall` procedures are defined in a suite package, all of them will be executed before invoking any test. + +For multiple `--%beforeall` procedures order of execution is defined by annotation position in the package specification. -Single beforeall procedure. ```sql create or replace package test_package as --%suite(Tests for a package) @@ -328,10 +346,8 @@ Finished in .012292 seconds 2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) ``` - -When you define multiple beforeall procedures, all of them will get executed before invoking any test in package. -Order of execution for beforeall procedures is defined by the position of the `--%beforeall` annotation in the package specification. -Note that procedure `another_setup` is also invoked before any test, though it's located at the end of package specification. +In the below example, procedure `another_setup` is invoked after `initial_setup`. +The `another_setup` still gets invoked before any test from that suite package is executed. ```sql create or replace package test_package as --%suite(Tests for a package) @@ -421,26 +437,403 @@ When procedure is annotated as both `--%beforeall` and `--%test`, the procedure ``` Tests for a package --- INITIAL_SETUP invoked --- - --- ANOTHER_SETUP invoked --- - Description of tesed behavior [.004 sec] + Description of tesed behavior [.003 sec] Description of another behavior [.004 sec] -Finished in .016672 seconds -2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) - ``` + +Warnings: + + 1) test_package + Duplicate annotation "--%beforeall". Annotation ignored. + at "UT3_TESTER.TEST_PACKAGE.INITIAL_SETUP", line 5 + 2) test_package + Annotation "--%beforeall" cannot be used with annotation: "--%test" + at "UT3_TESTER.TEST_PACKAGE.SOME_TEST", line 9 + +Finished in .012158 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 2 warning(s) +``` ### Afterall -Marks a procedure to be executed after all test procedures in a suite. + +Marks annotated procedure to be executed after all test procedures in a suite. + +If `--%afterall` raises an exception the following will happen: +- a warning will be raised, indicating that `--%afterall` procedure has failed +- execution will continue uninterrupted for rest of the suite + +If `--%afterall` raises an exception, it can have negative impact on other tests, as the environment was not cleaned-up after the tests. +This however doesn't have direct impact on test execution within current suite, as the tests are already complete by the time `--%afterall` is called. + +When multiple `--%afterall` procedures are defined in a suite, all of them will be executed after invoking all tests from the suite. + +For multiple `--%afterall` procedures order of execution is defined by annotation position in the package specification. + +All rules defined for `--%beforeall` also apply for `--%afterall` annotation. See [beforeall](#Beforeall) for more details. + +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + --%afterall + procedure cleanup_stuff; + +end; +/ +create or replace package body test_package as + procedure cleanup_stuff is + begin + dbms_output.put_line('---CLEANUP_STUFF invoked ---'); + end; + + procedure some_test is begin null; end; + + procedure other_test is begin null; end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.003 sec] + Description of another behavior [.005 sec] + ---CLEANUP_STUFF invoked --- + +Finished in .014161 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` ### Beforeeach +Marks annotated procedure to be executed before each test procedure in a suite. + +The procedure annotated as `--%beforeeach` is getting executed before each test in a suite. +That means that the procedure will be executed as many times as there are test in suite package. + +If a test is marked as disabled the `--%beforeeach` procedure is not invoked for that test. + +If `--%beforeeach` raises an unhandled exception the following will happen: +- the following `--%beforeeach` as well as all `--%beforetest` for that test **will not be executed** +- the test will be marked as errored and exception stack trace will be captured and reported +- the `--%aftertest`, `--%aftereach` procedures **will be executed** for the errored test +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for rest of the suite + +As a rule, the `--%beforeeach` execution gets aborted if preceding `--%beforeeach` failed. + +When multiple `--%beforeeach` procedures are defined in a suite, all of them will be executed before invoking each test. + +For multiple `--%beforeeach` procedures order of execution is defined by annotation position in the package specification. + +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + --%beforeeach + procedure setup_for_test; + + --%beforeall + procedure setup_stuff; +end; +/ +create or replace package body test_package as + procedure setup_stuff is + begin + dbms_output.put_line('---SETUP_STUFF invoked ---'); + end; + + procedure setup_for_test is + begin + dbms_output.put_line('---SETUP_FOR_TEST invoked ---'); + end; + + procedure some_test is + begin + dbms_output.put_line('---SOME_TEST invoked ---'); + end; + + procedure other_test is + begin + dbms_output.put_line('---OTHER_TEST invoked ---'); + end; +end; +/ +``` + +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + ---SETUP_STUFF invoked --- + Description of tesed behavior [.004 sec] + ---SETUP_FOR_TEST invoked --- + ---SOME_TEST invoked --- + Description of another behavior [.006 sec] + ---SETUP_FOR_TEST invoked --- + ---OTHER_TEST invoked --- + +Finished in .014683 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + + ### Aftereach +Marks annotated procedure to be executed after each test procedure in a suite. + +The procedure annotated as `--%aftereach` is getting executed after each test in a suite. +That means that the procedure will be executed as many times as there are test in suite package. + +If a test is marked as disabled the `--%aftereach` procedure is not invoked for that test. + +If `--%aftereach` raises an unhandled exception the following will happen: +- the test will be marked as errored and exception stack trace will be captured and reported +- the `--%aftertest`, `--%aftereach` procedures **will be executed** for the errored test +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for rest of the suite + +When multiple `--%aftereach` procedures are defined in a suite, all of them will be executed after invoking each test. + +For multiple `--%aftereach` procedures order of execution is defined by the annotation position in the package specification. + +As a rule, the `--%aftereach` gets executed even if the associated `--%beforeeach`, `--%beforetest`, `--%test` or other `--%aftereach` procedures have raised unhandled exceptions. + +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + procedure some_test; + + --%test(Description of another behavior) + procedure other_test; + + --%aftereach + procedure cleanup_for_test; + + --%afterall + procedure cleanup_stuff; +end; +/ +create or replace package body test_package as + procedure cleanup_stuff is + begin + dbms_output.put_line('---CLEANUP_STUFF invoked ---'); + end; + + procedure cleanup_for_test is + begin + dbms_output.put_line('---CLEANUP_FOR_TEST invoked ---'); + end; + + procedure some_test is + begin + dbms_output.put_line('---SOME_TEST invoked ---'); + end; + + procedure other_test is + begin + dbms_output.put_line('---OTHER_TEST invoked ---'); + end; +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.006 sec] + ---SOME_TEST invoked --- + ---CLEANUP_FOR_TEST invoked --- + Description of another behavior [.006 sec] + ---OTHER_TEST invoked --- + ---CLEANUP_FOR_TEST invoked --- + ---CLEANUP_STUFF invoked --- + +Finished in .018115 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + ### Beforetest +Indicates a specific setup to be executed for a test. +Used alongside `--%test` annotation. Indicates procedure name to be executed before specific test. + +The `--%beforetest` procedures are executed after invoking all `--%beforeeach` for a test. + +If a test is marked as disabled the `--%beforetest` procedure is not invoked for that test. + +If `--%beforetest` raises an unhandled exception the following will happen: +- the following `--%beforetest` for that test **will not be executed** +- the test will be marked as errored and exception stack trace will be captured and reported +- the `--%aftertest`, `--%aftereach` procedures **will be executed** for the errored test +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for rest of the suite + +When multiple `--%beforetest` procedures are defined for a test, all of them will be executed before invoking the test. + +For multiple `--%beforetest` procedures order of execution is defined by annotation position in the package specification. + +As a rule, the `--%beforetest` execution gets aborted if preceding `--%beforeeach` or `--%beforetest` failed. + +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + --%beforetest(setup_for_a_test) + --%beforetest(another_setup_for_a_test) + procedure some_test; + + --%test(Description of another behavior) + --%beforetest(setup_for_a_test) + procedure other_test; + + procedure another_setup_for_a_test; + + procedure setup_for_a_test; + +end; +/ +create or replace package body test_package as + procedure setup_for_a_test is + begin + dbms_output.put_line('---SETUP_FOR_A_TEST invoked ---'); + end; + + procedure another_setup_for_a_test is + begin + dbms_output.put_line('---ANOTHER_SETUP_FOR_A_TEST invoked ---'); + end; + + procedure some_test is + begin + dbms_output.put_line('---SOME_TEST invoked ---'); + end; + + procedure other_test is + begin + dbms_output.put_line('---OTHER_TEST invoked ---'); + end; +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.011 sec] + ---SETUP_FOR_A_TEST invoked --- + ---ANOTHER_SETUP_FOR_A_TEST invoked --- + ---SOME_TEST invoked --- + Description of another behavior [.005 sec] + ---SETUP_FOR_A_TEST invoked --- + ---OTHER_TEST invoked --- + +Finished in .018446 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + + ### Aftertest +Indicates a specific cleanup to be executed for a test. +Used alongside `--%test` annotation. Indicates procedure name to be executed after specific test. + +The `--%aftertest` procedures are executed before invoking any `--%aftereach` for a test. + +If a test is marked as disabled the `--%aftertest` procedure is not invoked for that test. + +If `--%aftertest` raises an unhandled exception the following will happen: +- the test will be marked as errored and exception stack trace will be captured and reported +- the following `--%aftertest` and all `--%aftereach` procedures **will be executed** for the errored test +- the `--%afterall` procedures **will be executed** +- test execution will continue uninterrupted for rest of the suite + +When multiple `--%aftertest` procedures are defined for a test, all of them will be executed before invoking the test. + +For multiple `--%aftertest` procedures order of execution is defined by annotation position in the package specification. + +As a rule, the `--%aftertest` gets executed even if the associated `--%beforeeach`, `--%beforetest`, `--%test` or other `--%aftertest` procedures have raised unhandled exceptions. + +```sql +create or replace package test_package as + --%suite(Tests for a package) + + --%test(Description of tesed behavior) + --%aftertest(cleanup_for_a_test) + --%aftertest(another_cleanup_for_a_test) + procedure some_test; + + --%test(Description of another behavior) + --%aftertest(cleanup_for_a_test) + procedure other_test; + + procedure another_cleanup_for_a_test; + + procedure cleanup_for_a_test; + +end; +/ +create or replace package body test_package as + procedure cleanup_for_a_test is + begin + dbms_output.put_line('---CLEANUP_FOR_A_TEST invoked ---'); + end; + + procedure another_cleanup_for_a_test is + begin + dbms_output.put_line('---ANOTHER_CLEANUP_FOR_A_TEST invoked ---'); + end; + + procedure some_test is + begin + dbms_output.put_line('---SOME_TEST invoked ---'); + end; + + procedure other_test is + begin + dbms_output.put_line('---OTHER_TEST invoked ---'); + end; +end; +/ +``` +```sql +exec ut.run('test_package'); +``` +``` +Tests for a package + Description of tesed behavior [.01 sec] + ---SOME_TEST invoked --- + ---CLEANUP_FOR_A_TEST invoked --- + ---ANOTHER_CLEANUP_FOR_A_TEST invoked --- + Description of another behavior [.006 sec] + ---OTHER_TEST invoked --- + ---CLEANUP_FOR_A_TEST invoked --- + +Finished in .018691 seconds +2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` + ### Suitepath ### Context From e672ef00516a4f4754a1de7b711afa253ca0b912 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Sun, 22 Apr 2018 23:30:33 +0100 Subject: [PATCH 25/28] Added description of `context` to annotations documentation. Reorganized order of sections in annotations documentation. --- docs/userguide/annotations.md | 438 ++++++++++++++++++++++++---------- 1 file changed, 309 insertions(+), 129 deletions(-) diff --git a/docs/userguide/annotations.md b/docs/userguide/annotations.md index f3029cfb4..c39ec4f0b 100644 --- a/docs/userguide/annotations.md +++ b/docs/userguide/annotations.md @@ -39,6 +39,17 @@ The `--%suite` annotation denotes PLSQL package as a unit test suite. It accepts an optional description that will be visible when running the tests. When description is not provided, package name is displayed on report. +**Note** +>Package is considered a test-suite only when package specification contains the `--%suite` annotation at the package level. +> +>Some annotations like `--%suite`, `--%test` and `--%displayname` accept parameters. The parameters for annotations need to be placed in brackets. +Values for parameters should be provided without any quotation marks. +If the parameters are placed without brackets or with incomplete brackets, they will be ignored. +> +>Example: `--%suite(The name of suite without closing bracket` +>Example: `--%suite The name of suite without brackets` + + Suite package without description. ```sql create or replace package test_package as @@ -834,30 +845,238 @@ Finished in .018691 seconds 2 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) ``` -### Suitepath +### Rollback ### Context -### Rollback +In most of the cases, the code to be tested is consisting of PLSQL packages containing procedures and functions. +When creating test suites, it's quite common to maintain `one to one` relationship between test suite packages and tested code. +When it comes to test procedures themselves, it is best practice to have one test procedure for one tested behavior of the code that is tested. +The relationship between test procedure and tested procedure/function will be therefore `many to one` in most of the cases. +With this comes a challenge. How to group tests, related to one tested procedure, so that it is obvious that they relate to the same code. +This is where utPLSQL contexts come handy. + +Contexts allow for creating sub-suites within a suite package and they allow for grouping of tests that are somehow related. + +In essence, context behaves like a suite within a suite. + +Context have following characteristics: +- start with the `--%context` annotation and ends with `--%endcontext` +- can have a name provided a parameter for example `--%context(Remove rooms by name)` +- when no name is provided for context, the context is names `context_N` where `N` is the number of the context in suite +- can have their own `--%beforeall`, `--%beforeeach`, `--%afterall` and `--%aftereach` procedures +- `--%beforeall`, `--%beforeeach`, `--%afterall` and `--%aftereach` procedures defined at suite level, propagate to context +- test suite package can have multiple contexts in it +- contexts cannot be nested + + +The below example illustrates usage of `--%context` for separating tests for individual procedures of package. + +Tested tables and code +```sql +create table rooms ( + room_key number primary key, + name varchar2(100) not null +); + +create table room_contents ( + contents_key number primary key, + room_key number not null, + name varchar2(100) not null, + create_date timestamp default current_timestamp not null, + constraint fk_rooms foreign key (room_key) references rooms (room_key) +); + +create or replace package rooms_management is + + procedure remove_rooms_by_name( a_name rooms.name%type ); + + procedure add_rooms_content( + a_room_name rooms.name%type, + a_content_name room_contents.name%type + ); + +end; +/ + +create or replace package body rooms_management is + procedure remove_rooms_by_name( a_name rooms.name%type ) is + begin + if a_name is null then + raise program_error; + end if; + delete from rooms where name like a_name; + end; + + procedure add_rooms_content( + a_room_name rooms.name%type, + a_content_name room_contents.name%type + ) is + l_room_key rooms.room_key%type; + begin + + select room_key into l_room_key + from rooms where name = a_room_name; + + insert into room_contents + (contents_key, room_key, name) + select nvl(max(contents_key)+1, 1) as contents_key, + l_room_key, + a_content_name + from room_contents; + end; +end; +/ +``` + +Below test suite defines: +- `--%beforeall` outside of context, that will be executed before all tests +- `--%context(remove_rooms_by_name)` to group tests for `remove_rooms_by_name` procedure +- `--%context(add_rooms_content)` to group tests for `add_rooms_content` procedure + +```sql +create or replace package test_rooms_management is + + --%suite(Rooms management) + + --%beforeall + procedure setup_rooms; + + + --%context(remove_rooms_by_name) + + --%test(Removes a room without content in it) + procedure remove_empty_room; + + --%test(Raises exception when null room name given) + --%throws(-6501) + procedure null_room_name; + + --%endcontext + + + --%context(add_rooms_content) + + --%test(Fails when room name is not valid) + --%throws(-1403) + procedure fails_on_room_name_invalid; + + --%test(Fails when content name is null) + --%throws(-1400) + procedure fails_on_content_null; + + --%test(Adds a content to existing room) + procedure add_content_success; + + --%endcontext + +end; +/ + +create or replace package body test_rooms_management is + + procedure setup_rooms is + begin + insert all + into rooms values(1, 'Dining Room') + into rooms values(2, 'Living Room') + into rooms values(3, 'Bathroom') + select 1 from dual; + + insert all + into room_contents values(1, 1, 'Table', sysdate) + into room_contents values(3, 1, 'Chair', sysdate) + into room_contents values(4, 2, 'Sofa', sysdate) + into room_contents values(5, 2, 'Lamp', sysdate) + select 1 from dual; + + dbms_output.put_line('---SETUP_ROOMS invoked ---'); + end; + + procedure remove_empty_room is + l_rooms_not_named_b sys_refcursor; + l_remaining_rooms sys_refcursor; + begin + open l_rooms_not_named_b for select * from rooms where name not like 'B%'; + + remove_rooms_by_name('B%'); + + open l_remaining_rooms for select * from rooms; + ut.expect( l_remaining_rooms ).to_equal(l_rooms_not_named_b); + end; + + procedure room_with_content is + begin + remove_rooms_by_name('Living Room'); + end; + + procedure null_room_name is + begin + remove_rooms_by_name(NULL); + end; + + procedure fails_on_room_name_invalid is + begin + add_rooms_content('bad room name','Chair'); + end; + + procedure fails_on_content_null is + begin + --Act + add_rooms_content('Dining Room',null); + --Assert by --%throws annotation + end; + + procedure add_content_success is + l_expected room_contents.name%type; + l_actual room_contents.name%type; + begin + --Arrange + l_expected := 'Table'; + + --Act + add_rooms_content( 'Dining Room', l_expected ); + --Assert + select name into l_actual from room_contents + where contents_key = (select max(contents_key) from room_contents); + + ut.expect( l_actual ).to_equal( l_expected ); + end; + +end; +/ +``` + +When te tests are executed +```sql +exec ut.run('test_package'); +``` +The following report is displayed +``` +Rooms management + ---SETUP_ROOMS invoked --- + remove_rooms_by_name + Removes a room without content in it [.015 sec] + Raises exception when null room name given [.002 sec] + add_rooms_content + Fails when room name is not valid [.003 sec] + Fails when content name is null [.003 sec] + Adds a content to existing room [.003 sec] + +Finished in .035261 seconds +5 tests, 0 failed, 0 errored, 0 disabled, 0 warning(s) +``` -**Note** ->Package is considered a test-suite only when package specification contains the `--%suite` annotation at the package level. -> ->Some annotations like `--%suite`, `--%test` and `--%displayname` accept parameters. The parameters for annotations need to be placed in brackets. -Values for parameters should be provided without any quotation marks. -If the parameters are placed without brackets or with incomplete brackets, they will be ignored. -> ->Example: `--%suite(The name of suite without closing bracket` -# Suitepath concept +### Suitepath It is very likely that the application for which you are going to introduce tests consists of many different packages, procedures and functions. Usually procedures can be logically grouped inside a package, there also might be several logical groups of procedures in a single package and packages might be grouped into modules and modules into subject areas. -As your project grows, the codebase will grow to. utPLSQL allows you to group packages into modules and modules into +As your project grows, the codebase will grow to. utPLSQL allows you to group packages into modules and also allows for nesting modules. Let's say you have a complex insurance application that deals with policies, claims and payments. The payment module contains several packages for payment recognition, charging, planning etc. The payment recognition module among others contains a complex `recognize_payment` procedure that associates received money to the policies. @@ -927,7 +1146,8 @@ A `%suitepath` can be provided in three ways: * [schema]:suite1[.suite2][.suite3]...[.procedure] - execute all tests in all suites from suite1[.suite2][.suite3]...[.procedure] path. If schema is not provided, then the current schema is used. Example: `:all.rooms_tests` * [schema.]package[.procedure] - execute all tests in the specified test package. The whole hierarchy of suites in the schema is built before all before/after hooks or part suites for the provided suite package are executed as well. Example: `tests.test_contact.test_last_name_validator` or simply `test_contact.test_last_name_validator` if `tests` is the current schema. -# Using automatic rollback in tests + +### Rollback By default, changes performed by every setup, cleanup and test procedure are isolated by savepoints. This solution is suitable for use-cases where the code that is being tested as well as the unit tests themselves do not use transaction control (commit/rollback) or DDL commands. @@ -958,47 +1178,94 @@ Keep in mind that when your test runs as autonomous transaction it will not see > Example: `--%suitepath(org.utplsql.core.utils)` > -## Invalid annotations -If procedure level annotation is not placed right before procedure, it is not considered an annotation for procedure. +### Throws + +The `--%throws` annotation allows you to specify a list of exception numbers that can be expected from a test. -Example of invalid procedure level annotations +If `--%throws(-20001,-20002)` is specified and no exception is raised or the exception raised is not on the list of provided exception numbers, the test is marked as failed. + +The framework ignores bad arguments. `--%throws(7894562, operaqk, -=1, -20496, pow74d, posdfk3)` will be interpreted as `--%throws(-20496)`. +The annotation is ignored, when no valid arguments are provided `--%throws()`,`--%throws`, `--%throws(abe, 723pf)`. + +Example: ```sql -create or replace package test_pkg is +create or replace package example_pgk as - --%suite(Name of suite) + --%suite(Example Throws Annotation) - --%test - -- this single-line comment makes the TEST annotation no longer associated with the procedure - procedure first_test; + --%test(Throws one of the listed exceptions) + --%throws(-20145,-20146, -20189 ,-20563) + procedure raised_one_listed_exception; - --%test - --procedure some_test; /* This TEST annotation is not associated with any procedure*/ + --%test(Throws different exception than expected) + --%throws(-20144) + procedure raised_different_exception; - --%test(Name of another test) - procedure another_test; + --%test(Throws different exception than listed) + --%throws(-20144,-00001,-20145) + procedure raised_unlisted_exception; - --%test - /** - * this multi-line comment makes the TEST annotation no longer associated with the procedure - */ - procedure yet_another_test; -end test_pkg; -``` -Procedure level annotations must be defined right before the procedure they reference, no empty lines are allowed, no comment lines can exist between annotation and the procedure. + --%test(Gives failure when an exception is expected and nothing is thrown) + --%throws(-20459, -20136, -20145) + procedure nothing_thrown; + +end; +/ +create or replace package body example_pgk is + procedure raised_one_listed_exception is + begin + raise_application_error(-20189, 'Test error'); + end; + procedure raised_different_exception is + begin + raise_application_error(-20143, 'Test error'); + end; -Package level annotations need to be separated by at least one empty line from a procedure procedure or procedure annotation. + procedure raised_unlisted_exception is + begin + raise_application_error(-20143, 'Test error'); + end; -Example of invalid package level annotation. -```sql -create or replace package test_pkg is - --%suite(Name of suite) - --%test - procedure first_test; -end test_pkg; + procedure nothing_thrown is + begin + ut.expect(1).to_equal(1); + end; +end; +/ + +exec ut.run('example_pgk'); +``` + +Running the test will give report: +``` +Example Throws Annotation + Throws one of the listed exceptions [.018 sec] + Throws different exception than expected [.008 sec] (FAILED - 1) + Throws different exception than listed [.007 sec] (FAILED - 2) + Gives failure when an exception is expected and nothing is thrown [.002 sec] (FAILED - 3) + +Failures: + + 1) raised_different_exception + Actual: -20143 was expected to equal: -20144 + ORA-20143: Test error + ORA-06512: at "UT3.EXAMPLE_PGK", line 9 + ORA-06512: at line 6 + + 2) raised_unlisted_exception + Actual: -20143 was expected to be one of: (-20144, -1, -20145) + ORA-20143: Test error + ORA-06512: at "UT3.EXAMPLE_PGK", line 14 + ORA-06512: at line 6 + + 3) nothing_thrown + Expected one of exceptions (-20459, -20136, -20145) but nothing was raised. + +Finished in .038692 seconds +4 tests, 3 failed, 0 errored, 0 disabled, 0 warning(s) ``` -In the above example, the `--%suite` annotation is ignored, as it is in fact associated with `procedure first_test`. Association with procedure takes precedence over association with package. ## Order of execution @@ -1151,90 +1418,3 @@ Example: exec ut_runner.purge_cache('HR', 'PACKAGE'); ``` -## Throws annotation - -The `--%throws` annotation allows you to specify a list of exception numbers that can be expected from a test. - -If `--%throws(-20001,-20002)` is specified and no exception is raised or the exception raised is not on the list of provided exception numbers, the test is marked as failed. - -The framework ignores bad arguments. `--%throws(7894562, operaqk, -=1, -20496, pow74d, posdfk3)` will be interpreted as `--%throws(-20496)`. -The annotation is ignored, when no valid arguments are provided `--%throws()`,`--%throws`, `--%throws(abe, 723pf)`. - -Example: -```sql -create or replace package example_pgk as - - --%suite(Example Throws Annotation) - - --%test(Throws one of the listed exceptions) - --%throws(-20145,-20146, -20189 ,-20563) - procedure raised_one_listed_exception; - - --%test(Throws different exception than expected) - --%throws(-20144) - procedure raised_different_exception; - - --%test(Throws different exception than listed) - --%throws(-20144,-00001,-20145) - procedure raised_unlisted_exception; - - --%test(Gives failure when an exception is expected and nothing is thrown) - --%throws(-20459, -20136, -20145) - procedure nothing_thrown; - -end; -/ -create or replace package body example_pgk is - procedure raised_one_listed_exception is - begin - raise_application_error(-20189, 'Test error'); - end; - - procedure raised_different_exception is - begin - raise_application_error(-20143, 'Test error'); - end; - - procedure raised_unlisted_exception is - begin - raise_application_error(-20143, 'Test error'); - end; - - procedure nothing_thrown is - begin - ut.expect(1).to_equal(1); - end; -end; -/ - -exec ut.run('example_pgk'); -``` - -Running the test will give report: -``` -Example Throws Annotation - Throws one of the listed exceptions [.018 sec] - Throws different exception than expected [.008 sec] (FAILED - 1) - Throws different exception than listed [.007 sec] (FAILED - 2) - Gives failure when an exception is expected and nothing is thrown [.002 sec] (FAILED - 3) - -Failures: - - 1) raised_different_exception - Actual: -20143 was expected to equal: -20144 - ORA-20143: Test error - ORA-06512: at "UT3.EXAMPLE_PGK", line 9 - ORA-06512: at line 6 - - 2) raised_unlisted_exception - Actual: -20143 was expected to be one of: (-20144, -1, -20145) - ORA-20143: Test error - ORA-06512: at "UT3.EXAMPLE_PGK", line 14 - ORA-06512: at line 6 - - 3) nothing_thrown - Expected one of exceptions (-20459, -20136, -20145) but nothing was raised. - -Finished in .038692 seconds -4 tests, 3 failed, 0 errored, 0 disabled, 0 warning(s) -``` From b2df93ec0fe9b508031a89cade6b959b55dd3390 Mon Sep 17 00:00:00 2001 From: Jacek Gebal Date: Mon, 23 Apr 2018 00:29:34 +0100 Subject: [PATCH 26/28] Integrated with develop branch changes. --- source/reporters/ut_junit_reporter.tpb | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/source/reporters/ut_junit_reporter.tpb b/source/reporters/ut_junit_reporter.tpb index 5d2fd54df..e1a6d7dca 100644 --- a/source/reporters/ut_junit_reporter.tpb +++ b/source/reporters/ut_junit_reporter.tpb @@ -77,15 +77,7 @@ create or replace type body ut_junit_reporter is else self.print_text(''); end if; - if a_test.before_test.get_error_stack_trace() is not null or a_test.after_test.get_error_stack_trace() is not null then - self.print_text(''); - self.print_text(c_cddata_tag_start); - self.print_text(trim(a_test.before_test.get_error_stack_trace()) || trim(chr(10) || chr(10) || a_test.after_test.get_error_stack_trace())); - self.print_text(c_cddata_tag_end); - self.print_text(''); - else - self.print_text(''); - end if; + self.print_text(''); self.print_text(''); end; @@ -94,6 +86,8 @@ create or replace type body ut_junit_reporter is a_suite.results_count.failure_count + a_suite.results_count.errored_count; l_suite ut_suite; l_tests ut_suite_items := ut_suite_items(); + l_data clob; + l_errors ut_varchar2_list; begin a_suite_id := a_suite_id + 1; self.print_text('%"--%suitepath" annotation requires a non-empty value.%%' + '%%"--%suitepath" annotation requires a non-empty parameter value.%%' ); end; @@ -323,7 +323,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%%"--%displayname" annotation requires a non-empty value.%%' + '%%"--%displayname" annotation requires a non-empty parameter value.%%' ); end; @@ -340,7 +340,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%%"--%rollback" annotation requires one of values:%%' + '%%"--%rollback" annotation requires one of values as parameter:%%' ); end; @@ -357,7 +357,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%%"--%rollback" annotation requires one of values:%%' + '%%"--%rollback" annotation requires one of values as parameter: "auto" or "manual". Annotation ignored.%%' ); end; @@ -468,7 +468,7 @@ create or replace package body test_suite_builder is ut.expect(l_actual).to_be_like('%%Annotation "--\%beforeall"%line 2%%', '\'); ut.expect(l_actual).to_be_like('%%Annotation "--\%beforeeach"%line 3%%', '\'); ut.expect(l_actual).to_be_like('%%Annotation "--\%aftereach"%line 4%%', '\'); - ut.expect(l_actual).to_be_like('%%Annotation "--\%afterall" cannot be used with annotation: "--\%test"' + ut.expect(l_actual).to_be_like('%%Annotation "--\%afterall" cannot be used with "--\%test". Annotation ignored.' ||'%at "UT3_TESTER.SOME_PACKAGE.DO_STUFF", line 5%%', '\'); ut.expect(l_actual).not_to_be_like('%%'); ut.expect(l_actual).not_to_be_like('%%'); @@ -634,7 +634,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%Annotation "--\%context(A context)" was ignored. Cannot find following "--\%endcontext".%at "UT3_TESTER.SOME_PACKAGE", line 4%' + '%Invalid annotation "--\%context". Cannot find following "--\%endcontext". Annotation ignored.%at "UT3_TESTER.SOME_PACKAGE", line 4%' ,'\' ); ut.expect(l_actual).to_be_like( @@ -675,7 +675,7 @@ create or replace package body test_suite_builder is l_actual := invoke_builder_for_annotations(l_annotations, 'SOME_PACKAGE'); --Assert ut.expect(l_actual).to_be_like( - '%Annotation "--\%endcontext" was ignored. Cannot find preceding "--\%context".%at "UT3_TESTER.SOME_PACKAGE", line 9%' + '%Invalid annotation "--\%endcontext". Cannot find preceding "--\%context". Annotation ignored.%at "UT3_TESTER.SOME_PACKAGE", line 9%' ,'\' ); ut.expect(l_actual).to_be_like(