1create or replace package ut_utils authid definer is
2 /*
3 utPLSQL - Version 3
4 Copyright 2016 - 2017 utPLSQL Project
5
6 Licensed under the Apache License, Version 2.0 (the "License"):
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 */
18
19 /**
20 * Common utilities and constants used throughout utPLSQL framework
21 *
22 */
23
24 gc_version constant varchar2(50) := 'v3.0.4.1461-develop';
25
26 /* Constants: Event names */
27 gc_run constant varchar2(12) := 'run';
28 gc_suite constant varchar2(12) := 'suite';
29 gc_before_all constant varchar2(12) := 'before_all';
30 gc_before_each constant varchar2(12) := 'before_each';
31 gc_before_test constant varchar2(12) := 'before_test';
32 gc_test constant varchar2(12) := 'test';
33 gc_test_execute constant varchar2(12) := 'test_execute';
34 gc_after_test constant varchar2(10) := 'after_test';
35 gc_after_each constant varchar2(12) := 'after_each';
36 gc_after_all constant varchar2(12) := 'after_all';
37
38 /* Constants: Test Results */
39 tr_disabled constant number(1) := 0; -- test/suite was disabled
40 tr_success constant number(1) := 1; -- test passed
41 tr_failure constant number(1) := 2; -- one or more expectations failed
42 tr_error constant number(1) := 3; -- exception was raised
43
44 tr_disabled_char constant varchar2(8) := 'Disabled'; -- test/suite was disabled
45 tr_success_char constant varchar2(7) := 'Success'; -- test passed
46 tr_failure_char constant varchar2(7) := 'Failure'; -- one or more expectations failed
47 tr_error_char constant varchar2(5) := 'Error'; -- exception was raised
48
49 /*
50 Constants: Rollback type for ut_test_object
51 */
52 gc_rollback_auto constant number(1) := 0; -- rollback after each test and suite
53 gc_rollback_manual constant number(1) := 1; -- leave transaction control manual
54 --gc_rollback_on_error constant number(1) := 2; -- rollback tests only on error
55
56 ex_unsupported_rollback_type exception;
57 gc_unsupported_rollback_type constant pls_integer := -20200;
58 pragma exception_init(ex_unsupported_rollback_type, -20200);
59
60 ex_path_list_is_empty exception;
61 gc_path_list_is_empty constant pls_integer := -20201;
62 pragma exception_init(ex_path_list_is_empty, -20201);
63
64 ex_invalid_path_format exception;
65 gc_invalid_path_format constant pls_integer := -20202;
66 pragma exception_init(ex_invalid_path_format, -20202);
67
68 ex_suite_package_not_found exception;
69 gc_suite_package_not_found constant pls_integer := -20204;
70 pragma exception_init(ex_suite_package_not_found, -20204);
71
72 -- Reporting event time not supported
73 ex_invalid_rep_event_time exception;
74 gc_invalid_rep_event_time constant pls_integer := -20210;
75 pragma exception_init(ex_invalid_rep_event_time, -20210);
76
77 -- Reporting event name not supported
78 ex_invalid_rep_event_name exception;
79 gc_invalid_rep_event_name constant pls_integer := -20211;
80 pragma exception_init(ex_invalid_rep_event_name, -20211);
81
82 -- Any of tests failed
83 ex_some_tests_failed exception;
84 gc_some_tests_failed constant pls_integer := -20213;
85 pragma exception_init(ex_some_tests_failed, -20213);
86
87 -- Any of tests failed
88 ex_invalid_version_no exception;
89 gc_invalid_version_no constant pls_integer := -20214;
90 pragma exception_init(ex_invalid_version_no, -20214);
91
92 gc_max_storage_varchar2_len constant integer := 4000;
93 gc_max_output_string_length constant integer := 4000;
94 gc_max_input_string_length constant integer := gc_max_output_string_length - 2; --we need to remove 2 chars for quotes around string
95 gc_more_data_string constant varchar2(5) := '[...]';
96 gc_overflow_substr_len constant integer := gc_max_input_string_length - length(gc_more_data_string);
97 gc_number_format constant varchar2(100) := 'TM9';
98 gc_date_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ss';
99 gc_timestamp_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ssxff';
100 gc_timestamp_tz_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ssxff tzh:tzm';
101 gc_null_string constant varchar2(4) := 'NULL';
102
103 type t_version is record(
104 major natural,
105 minor natural,
106 bugfix natural,
107 build natural
108 );
109
110
111 /**
112 * Converts test results into strings
113 *
114 * @param a_test_result numeric representation of test result
115 *
116 * @return a string representation of a test_result.
117 */
118 function test_result_to_char(a_test_result integer) return varchar2;
119
120 function to_test_result(a_test boolean) return integer;
121
122 /**
123 * Generates a unique name for a savepoint
124 * Uses sys_guid, as timestamp gives only miliseconds on Windows and is not unique
125 * Issue: #506 for details on the implementation approach
126 */
127 function gen_savepoint_name return varchar2;
128
129 procedure debug_log(a_message varchar2);
130
131 procedure debug_log(a_message clob);
132
133 function to_string(a_value varchar2, a_qoute_char varchar2 := '''') return varchar2;
134
135 function to_string(a_value clob, a_qoute_char varchar2 := '''') return varchar2;
136
137 function to_string(a_value blob, a_qoute_char varchar2 := '''') return varchar2;
138
139 function to_string(a_value boolean) return varchar2;
140
141 function to_string(a_value number) return varchar2;
142
143 function to_string(a_value date) return varchar2;
144
145 function to_string(a_value timestamp_unconstrained) return varchar2;
146
147 function to_string(a_value timestamp_tz_unconstrained) return varchar2;
148
149 function to_string(a_value timestamp_ltz_unconstrained) return varchar2;
150
151 function to_string(a_value yminterval_unconstrained) return varchar2;
152
153 function to_string(a_value dsinterval_unconstrained) return varchar2;
154
155 function boolean_to_int(a_value boolean) return integer;
156
157 function int_to_boolean(a_value integer) return boolean;
158
159 /**
160 * Validates passed value against supported rollback types
161 */
162 procedure validate_rollback_type(a_rollback_type number);
163
164
165 /**
166 *
167 * Splits a given string into table of string by delimiter.
168 * The delimiter gets removed.
169 * If null passed as any of the parameters, empty table is returned.
170 * If no occurence of a_delimiter found in a_text then text is returned as a single row of the table.
171 * If no text between delimiters found then an empty row is returned, example:
172 * string_to_table( 'a,,b', ',' ) gives table ut_varchar2_list( 'a', null, 'b' );
173 *
174 * @param a_string the text to be split.
175 * @param a_delimiter the delimiter character or string
176 * @param a_skip_leading_delimiter determines if the leading delimiter should be ignored, used by clob_to_table
177 *
178 * @return table of varchar2 values
179 */
180 function string_to_table(a_string varchar2, a_delimiter varchar2:= chr(10), a_skip_leading_delimiter varchar2 := 'N') return ut_varchar2_list;
181
182 /**
183 * Splits a given string into table of string by delimiter.
184 * Default value of a_max_amount is 8191 because of code can contains multibyte character.
185 * The delimiter gets removed.
186 * If null passed as any of the parameters, empty table is returned.
187 * If split text is longer than a_max_amount it gets split into pieces of a_max_amount.
188 * If no text between delimiters found then an empty row is returned, example:
189 * string_to_table( 'a,,b', ',' ) gives table ut_varchar2_list( 'a', null, 'b' );
190 *
191 * @param a_clob the text to be split.
192 * @param a_delimiter the delimiter character or string (default chr(10) )
193 * @param a_max_amount the maximum length of returned string (default 8191)
194 * @return table of varchar2 values
195 */
196 function clob_to_table(a_clob clob, a_max_amount integer := 8191, a_delimiter varchar2:= chr(10)) return ut_varchar2_list;
197
198 function table_to_clob(a_text_table ut_varchar2_list, a_delimiter varchar2:= chr(10)) return clob;
199
200 /**
201 * Returns time difference in seconds (with miliseconds) between given timestamps
202 */
203 function time_diff(a_start_time timestamp with time zone, a_end_time timestamp with time zone) return number;
204
205 /**
206 * Returns a text indented with spaces except the first line.
207 */
208 function indent_lines(a_text varchar2, a_indent_size integer := 4, a_include_first_line boolean := false) return varchar2;
209
210
211 /**
212 * Returns a list of object that are part of utPLSQL framework
213 */
214 function get_utplsql_objects_list return ut_object_names;
215
216 /**
217 * Append a line to the end of ut_varchar2_lst
218 */
219 procedure append_to_varchar2_list(a_list in out nocopy ut_varchar2_list, a_line varchar2);
220
221 procedure append_to_clob(a_src_clob in out nocopy clob, a_new_data clob);
222 procedure append_to_clob(a_src_clob in out nocopy clob, a_new_data varchar2);
223
224 function convert_collection(a_collection ut_varchar2_list) return ut_varchar2_rows;
225
226 /**
227 * Set session's action and module using dbms_application_info
228 */
229 procedure set_action(a_text in varchar2);
230
231 /**
232 * Set session's client info using dbms_application_info
233 */
234 procedure set_client_info(a_text in varchar2);
235
236 function to_xpath(a_list varchar2, a_ancestors varchar2 := '/*/') return varchar2;
237
238 function to_xpath(a_list ut_varchar2_list, a_ancestors varchar2 := '/*/') return varchar2;
239
240 procedure cleanup_temp_tables;
241
242 /**
243 * Converts version string into version record
244 *
245 * @param a_version_no string representation of version in format vX.X.X.X where X is a positive integer
246 * @return t_version record with up to four positive numbers containing version
247 * @throws 20214 if passed version string is not matching version pattern
248 */
249 function to_version(a_version_no varchar2) return t_version;
250
251
252 /**
253 * Saves data from dbms_output buffer into a global temporary table (cache)
254 * used to store dbms_output buffer captured before the run
255 *
256 */
257 procedure save_dbms_output_to_cache;
258
259 /**
260 * Reads data from global temporary table (cache) abd puts it back into dbms_output
261 * used to recover dbms_output buffer data after a run is complete
262 *
263 */
264 procedure read_cache_to_dbms_output;
265
266
267 /**
268 * Function is used to reference to utPLSQL owned objects in dynamic sql statements executed from packages with invoker rights
269 *
270 * @return the name of the utPSQL schema owner
271 */
272 function ut_owner return varchar2;
273
274
275 /**
276 * Used in dynamic sql select statements to maintain balance between
277 * number of hard-parses and optimiser accurancy for cardinality of collections
278 *
279 *
280 * @return 3, for inputs of: 1-9; 33 for input of 10 - 99; 333 for (100 - 999)
281 */
282 function scale_cardinality(a_cardinality natural) return natural;
283
284end ut_utils;
285/
286
287create or replace package body ut_suite_builder is
288 /*
289 utPLSQL - Version 3
290 Copyright 2016 - 2017 utPLSQL Project
291
292 Licensed under the Apache License, Version 2.0 (the "License"):
293 you may not use this file except in compliance with the License.
294 You may obtain a copy of the License at
295
296 http://www.apache.org/licenses/LICENSE-2.0
297
298 Unless required by applicable law or agreed to in writing, software
299 distributed under the License is distributed on an "AS IS" BASIS,
300 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
301 See the License for the specific language governing permissions and
302 limitations under the License.
303 */
304
305 ------------------
306
307 function create_suite(a_object ut_annotated_object) return ut_logical_suite is
308 l_is_suite boolean := false;
309 l_is_test boolean := false;
310 l_suite_disabled boolean := false;
311 l_test_disabled boolean := false;
312 l_suite_items ut_suite_items := ut_suite_items();
313 l_suite_name varchar2(4000);
314
315 l_default_setup_proc varchar2(250 char);
316 l_default_teardown_proc varchar2(250 char);
317 l_suite_setup_proc varchar2(250 char);
318 l_suite_teardown_proc varchar2(250 char);
319 l_suite_path varchar2(4000 char);
320
321 l_proc_name varchar2(250 char);
322
323 l_suite ut_logical_suite;
324 l_test ut_test;
325
326 l_suite_rollback integer;
327
328 l_beforetest_procedure varchar2(250 char);
329 l_aftertest_procedure varchar2(250 char);
330 l_rollback_type integer;
331 l_displayname varchar2(4000);
332 function is_last_annotation_for_proc(a_annotations ut_annotations, a_index binary_integer) return boolean is
333 begin
334 return a_index = a_annotations.count or a_annotations(a_index).subobject_name != nvl(a_annotations(a_index+1).subobject_name, ' ');
335 end;
336 begin
337 l_suite_rollback := ut_utils.gc_rollback_auto;
338 for i in 1 .. a_object.annotations.count loop
339
340 if a_object.annotations(i).subobject_name is null then
341
342 if a_object.annotations(i).name in ('suite','displayname') then
343 l_suite_name := a_object.annotations(i).text;
344 if a_object.annotations(i).name = 'suite' then
345 l_is_suite := true;
346 end if;
347 elsif a_object.annotations(i).name = 'disabled' then
348 l_suite_disabled := true;
349 elsif a_object.annotations(i).name = 'suitepath' and a_object.annotations(i).text is not null then
350 l_suite_path := a_object.annotations(i).text || '.' || lower(a_object.object_name);
351 elsif a_object.annotations(i).name = 'rollback' then
352 if lower(a_object.annotations(i).text) = 'manual' then
353 l_suite_rollback := ut_utils.gc_rollback_manual;
354 else
355 l_suite_rollback := ut_utils.gc_rollback_auto;
356 end if;
357 end if;
358
359 elsif l_is_suite then
360
361 l_proc_name := a_object.annotations(i).subobject_name;
362
363 if a_object.annotations(i).name = 'beforeeach' and l_default_setup_proc is null then
364 l_default_setup_proc := l_proc_name;
365 elsif a_object.annotations(i).name = 'aftereach' and l_default_teardown_proc is null then
366 l_default_teardown_proc := l_proc_name;
367 elsif a_object.annotations(i).name = 'beforeall' and l_suite_setup_proc is null then
368 l_suite_setup_proc := l_proc_name;
369 elsif a_object.annotations(i).name = 'afterall' and l_suite_teardown_proc is null then
370 l_suite_teardown_proc := l_proc_name;
371
372
373 elsif a_object.annotations(i).name = 'disabled' then
374 l_test_disabled := true;
375 elsif a_object.annotations(i).name = 'beforetest' then
376 l_beforetest_procedure := a_object.annotations(i).text;
377 elsif a_object.annotations(i).name = 'aftertest' then
378 l_aftertest_procedure := a_object.annotations(i).text;
379 elsif a_object.annotations(i).name in ('displayname','test') then
380 l_displayname := a_object.annotations(i).text;
381 if a_object.annotations(i).name = 'test' then
382 l_is_test := true;
383 end if;
384 elsif a_object.annotations(i).name = 'rollback' then
385 if lower(a_object.annotations(i).text) = 'manual' then
386 l_rollback_type := ut_utils.gc_rollback_manual;
387 elsif lower(a_object.annotations(i).text) = 'auto' then
388 l_rollback_type := ut_utils.gc_rollback_auto;
389 end if;
390 end if;
391
392 if l_is_test and is_last_annotation_for_proc(a_object.annotations, i) then
393 l_suite_items.extend;
394 l_suite_items(l_suite_items.last) :=
395 ut_test(a_object_owner => a_object.object_owner
396 ,a_object_name => a_object.object_name
397 ,a_name => l_proc_name
398 ,a_description => l_displayname
399 ,a_rollback_type => coalesce(l_rollback_type, l_suite_rollback)
400 ,a_disabled_flag => l_test_disabled
401 ,a_before_test_proc_name => l_beforetest_procedure
402 ,a_after_test_proc_name => l_aftertest_procedure);
403
404 l_is_test := false;
405 l_test_disabled := false;
406 l_aftertest_procedure := null;
407 l_beforetest_procedure := null;
408 l_rollback_type := null;
409 end if;
410
411 end if;
412 end loop;
413
414 if l_is_suite then
415 l_suite := ut_suite (
416 a_object_owner => a_object.object_owner,
417 a_object_name => a_object.object_name,
418 a_name => a_object.object_name, --this could be different for sub-suite (context)
419 a_path => l_suite_path, --a patch for this suite (excluding the package name of current suite)
420 a_description => l_suite_name,
421 a_rollback_type => l_suite_rollback,
422 a_disabled_flag => l_suite_disabled,
423 a_before_all_proc_name => l_suite_setup_proc,
424 a_after_all_proc_name => l_suite_teardown_proc
425 );
426 for i in 1 .. l_suite_items.count loop
427 l_test := treat(l_suite_items(i) as ut_test);
428 l_test.set_beforeeach(l_default_setup_proc);
429 l_test.set_aftereach(l_default_teardown_proc);
430 l_test.path := l_suite.path || '.' || l_test.name;
431 l_suite.add_item(l_test);
432 end loop;
433 end if;
434
435 return l_suite;
436
437 end create_suite;
438
439 function build_suites_hierarchy(a_suites_by_path tt_schema_suites) return tt_schema_suites is
440 l_result tt_schema_suites;
441 l_suite_path varchar2(4000 char);
442 l_parent_path varchar2(4000 char);
443 l_name varchar2(4000 char);
444 l_suites_by_path tt_schema_suites;
445 begin
446 l_suites_by_path := a_suites_by_path;
447 --were iterating in reverse order of the index by path table
448 -- so the first paths will be the leafs of hierarchy and next will their parents
449 l_suite_path := l_suites_by_path.last;
450 ut_utils.debug_log('Input suites to process = '||l_suites_by_path.count);
451
452 while l_suite_path is not null loop
453 l_parent_path := substr( l_suite_path, 1, instr(l_suite_path,'.',-1)-1);
454 ut_utils.debug_log('Processing l_suite_path = "'||l_suite_path||'", l_parent_path = "'||l_parent_path||'"');
455 --no parent => I'm a root element
456 if l_parent_path is null then
457 ut_utils.debug_log(' suite "'||l_suite_path||'" is a root element - adding to return list.');
458 l_result(l_suite_path) := l_suites_by_path(l_suite_path);
459 -- not a root suite - need to add it to a parent suite
460 else
461 --parent does not exist and needs to be added
462 if not l_suites_by_path.exists(l_parent_path) then
463 l_name := substr( l_parent_path, instr(l_parent_path,'.',-1)+1);
464 ut_utils.debug_log(' Parent suite "'||l_parent_path||'" not found in the list - Adding suite "'||l_name||'"');
465 l_suites_by_path(l_parent_path) :=
466 ut_logical_suite(
467 a_object_owner => l_suites_by_path(l_suite_path).object_owner,
468 a_object_name => l_name, a_name => l_name, a_path => l_parent_path
469 );
470 else
471 ut_utils.debug_log(' Parent suite "'||l_parent_path||'" found in list of suites');
472 end if;
473 ut_utils.debug_log(' adding suite "'||l_suite_path||'" to "'||l_parent_path||'" items');
474 l_suites_by_path(l_parent_path).add_item( l_suites_by_path(l_suite_path) );
475 end if;
476 l_suite_path := l_suites_by_path.prior(l_suite_path);
477 end loop;
478 ut_utils.debug_log(l_result.count||' root suites created.');
479 return l_result;
480 end;
481
482 function build_suites(a_annotated_objects sys_refcursor) return t_schema_suites_info is
483 l_suite ut_logical_suite;
484 l_annotated_objects ut_annotated_objects;
485 l_all_suites tt_schema_suites;
486 l_result t_schema_suites_info;
487 begin
488 fetch a_annotated_objects bulk collect into l_annotated_objects;
489 close a_annotated_objects;
490
491 for i in 1 .. l_annotated_objects.count loop
492 l_suite := create_suite(l_annotated_objects(i));
493 if l_suite is not null then
494 l_all_suites(l_suite.path) := l_suite;
495 l_result.suite_paths(l_suite.object_name) := l_suite.path;
496 end if;
497 end loop;
498
499 --build hierarchical structure of the suite
500 -- Restructure single-dimension list into hierarchy of suites by the value of %suitepath attribute value
501 l_result.schema_suites := build_suites_hierarchy(l_all_suites);
502
503 return l_result;
504 end;
505
506 function build_schema_suites(a_owner_name varchar2) return t_schema_suites_info is
507 l_annotations_cursor sys_refcursor;
508 begin
509 -- form the single-dimension list of suites constructed from parsed packages
510 open l_annotations_cursor for
511 q'[select value(x)
512 from table(
513 ]'||ut_utils.ut_owner||q'[.ut_annotation_manager.get_annotated_objects(:a_owner_name, 'PACKAGE')
514 )x ]'
515 using a_owner_name;
516
517 return build_suites(l_annotations_cursor);
518 end;
519
520end ut_suite_builder;
521/
522/*http://example.com.*/
523/* comment /* comment */
524comment
525*/
526