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