@@ -31,7 +31,7 @@ create or replace type body ut_executable_test as
3131
3232 member procedure do_execute(
3333 self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item,
34- a_expected_error_codes in ut_integer_list
34+ a_expected_error_codes in ut_varchar2_rows
3535 ) is
3636 l_completed_without_errors boolean;
3737 begin
@@ -40,10 +40,114 @@ create or replace type body ut_executable_test as
4040
4141 member function do_execute(
4242 self in out nocopy ut_executable_test, a_item in out nocopy ut_suite_item,
43- a_expected_error_codes in ut_integer_list
43+ a_expected_error_codes in ut_varchar2_rows
4444 ) return boolean is
4545 l_expected_except_message varchar2(4000);
46+ l_expected_error_numbers ut_integer_list;
4647
48+ function build_exception_numbers_list(
49+ a_item in out nocopy ut_suite_item,
50+ a_expected_error_codes in ut_varchar2_rows
51+ ) return ut_integer_list is
52+ l_exception_number integer;
53+ l_exception_number_list ut_integer_list := ut_integer_list();
54+ c_regexp_for_exception_no constant varchar2(30) := '^-?[[:digit:]]{1,5}$';
55+
56+ c_integer_exception constant varchar2(1) := 'I';
57+ c_named_exception constant varchar2(1) := 'N';
58+
59+ function is_valid_qualified_name (a_name varchar2) return boolean is
60+ l_name varchar2(500);
61+ begin
62+ l_name := dbms_assert.qualified_sql_name(a_name);
63+ return true;
64+ exception when others then
65+ return false;
66+ end;
67+
68+ function check_exception_type(a_exception_name in varchar2) return varchar2 is
69+ l_exception_type varchar2(50);
70+ begin
71+ --check if it is a predefined exception
72+ begin
73+ execute immediate 'begin null; exception when '||a_exception_name||' then null; end;';
74+ l_exception_type := c_named_exception;
75+ exception
76+ when others then
77+ if dbms_utility.format_error_stack() like '%PLS-00485%' then
78+ declare
79+ e_invalid_number exception;
80+ pragma exception_init ( e_invalid_number, -6502 );
81+ begin
82+ execute immediate 'declare x integer := '||a_exception_name||'; begin null; end;';
83+ l_exception_type := c_integer_exception;
84+ exception
85+ when others then
86+ null;
87+ end;
88+ end if;
89+ end;
90+ return l_exception_type;
91+ end;
92+
93+ function get_exception_number (a_exception_var in varchar2) return integer is
94+ l_exc_no integer;
95+ l_exc_type varchar2(50);
96+ function remap_no_data_found (a_number integer) return integer is
97+ begin
98+ return case a_number when 100 then -1403 else a_number end;
99+ end;
100+ begin
101+ l_exc_type := check_exception_type(a_exception_var);
102+
103+ execute immediate
104+ case l_exc_type
105+ when c_integer_exception then
106+ 'declare l_exception number; begin :l_exception := '||a_exception_var||'; end;'
107+ when c_named_exception then
108+ 'begin raise '||a_exception_var||'; exception when others then :l_exception := sqlcode; end;'
109+ else
110+ 'begin :l_exception := null; end;'
111+ end
112+ using out l_exc_no;
113+
114+ return remap_no_data_found(l_exc_no);
115+ end;
116+
117+ begin
118+ if a_expected_error_codes is not empty then
119+ for i in 1 .. a_expected_error_codes.count loop
120+ /**
121+ * Check if its a valid qualified name and if so try to resolve name to an exception number
122+ */
123+ if is_valid_qualified_name(a_expected_error_codes(i)) then
124+ l_exception_number := get_exception_number(a_expected_error_codes(i));
125+ elsif regexp_like(a_expected_error_codes(i), c_regexp_for_exception_no) then
126+ l_exception_number := a_expected_error_codes(i);
127+ end if;
128+
129+ if l_exception_number is null then
130+ a_item.put_warning(
131+ 'Invalid parameter value "'||a_expected_error_codes(i)||'" for "--%throws" annotation. Parameter ignored.',
132+ self.procedure_name,
133+ a_item.line_no
134+ );
135+ elsif l_exception_number >= 0 then
136+ a_item.put_warning(
137+ 'Invalid parameter value "'||a_expected_error_codes(i)||'" for "--%throws" annotation. Exception value must be a negative integer. Parameter ignored.',
138+ self.procedure_name,
139+ a_item.line_no
140+ );
141+ else
142+ l_exception_number_list.extend;
143+ l_exception_number_list(l_exception_number_list.last) := l_exception_number;
144+ end if;
145+ l_exception_number := null;
146+ end loop;
147+ end if;
148+
149+ return l_exception_number_list;
150+ end;
47151 function failed_expec_errnum_message(a_expected_error_codes in ut_integer_list) return varchar is
48152 l_actual_error_no integer;
49153 l_expected_error_codes varchar2(4000);
@@ -72,9 +176,9 @@ create or replace type body ut_executable_test as
72176 begin
73177 --Create a ut_executable object and call do_execute after that get the data to know the test's execution result
74178 self.do_execute(a_item);
75-
76- if a_expected_error_codes is not null and a_expected_error_codes is not empty then
77- l_expected_except_message := failed_expec_errnum_message(a_expected_error_codes );
179+ l_expected_error_numbers := build_exception_numbers_list(a_item, a_expected_error_codes);
180+ if l_expected_error_numbers is not null and l_expected_error_numbers is not empty then
181+ l_expected_except_message := failed_expec_errnum_message( l_expected_error_numbers );
78182
79183 if l_expected_except_message is not null then
80184 ut_expectation_processor.add_expectation_result(
0 commit comments