Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 61a46f7

Browse files
authored
Merge branch 'develop' into feature/own-docker-images
2 parents 39b7e9b + 564a328 commit 61a46f7

30 files changed

Lines changed: 464 additions & 191 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
55
# User-specific stuff:
66
.idea/
7+
.vscode/
78
.sonar/
89
site/
910
pages/

docs/userguide/expectations.md

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -211,24 +211,55 @@ end;
211211
```
212212
The `a_nulls_are_equal` parameter controls the behavior of a `null=null` comparison (**this comparison by default is true!**)
213213

214-
### Comparing cursors
214+
### Excluding columns and attributes from comparison
215215

216-
The `equal` matcher accepts an additional parameter `a_exclude varchar2` or `a_exclude ut_varchar2_list`, when used to compare `cursor` data.
216+
The `equal` matcher accepts an additional parameter `a_exclude` when used to compare data of `cursor`, `object` or `table type`.
217217

218-
These parameters enable a list of column names to be passed for exclusion from the data comparison. The list can be a comma separated `varchar2` list or a `ut_varchar2_list` collection.
219-
The column names accepted by the parameter are **case sensitive** and cannot be quoted.
220-
If the `a_exclude` parameter is not specified, all columns are included.
221-
If a column to be excluded does not exist, the column cannot be excluded and it's name is simply ignored.
222-
This is useful when testing cursors containing data that is beyond our control (like default or trigger/procedure generated sysdate values on columns).
218+
This parameter can take three forms:
219+
1. A `varchar2` containing comma separated names of columns/attributes to exclude
220+
2. A `varchar2` containing **XPath** expression that lists items to be excluded
221+
3. A `ut_varchar2_list` containing list of columns/attributes to exclude
222+
223+
The column/attribute names are **case sensitive** and cannot be quoted.
224+
If the `a_exclude` parameter is not specified, whole cursor/object/table type is compared.
225+
If a column/attribute to be excluded does not exist it is simply ignored (no error).
226+
227+
This is useful when testing elements data that cannot be determined or set up by the tests (like `sysdate` populated by default on audit columns).
223228

224229
```sql
225230
procedure test_cursors_skip_columns is
226-
x sys_refcursor;
227-
y sys_refcursor;
231+
l_expected sys_refcursor;
232+
l_actual sys_refcursor;
228233
begin
229-
open x for select 'text' ignore_me, d.* from user_tables d;
230-
open y for select sysdate "ADate", d.* from user_tables d;
231-
ut.expect( a_actual => y ).to_equal( a_expected => x, a_exclude => 'IGNORE_ME,ADate' );
234+
open l_expected for select 'text' ignore_me, d.* from user_tables d;
235+
open l_actual for select sysdate "ADate", d.* from user_tables d;
236+
ut.expect( l_actual ).to_equal( l_expected, a_exclude => 'IGNORE_ME,ADate' );
237+
end;
238+
```
239+
240+
```sql
241+
create or replace type employee as object(
242+
first_name varchar2(50),
243+
last_name varchar2(50),
244+
hire_date date,
245+
created_at timestamp,
246+
created_by varchar2(30),
247+
modified_at timestamp,
248+
modified_by varchar2(50)
249+
);
250+
251+
procedure test_object_skip_columns is
252+
l_expected employee;
253+
l_actual employee;
254+
begin
255+
l_expected := employee('John'||rownum, 'Doe', sysdate, systimestamp, 'me', systimestamp, 'me');
256+
-- the actual should normally be returned by the tested code.
257+
l_actual := employee('John'||rownum, 'Doe', sysdate, systimestamp, 'me', systimestamp, 'me');
258+
259+
-- test the data excluding attributes specified by XPath
260+
ut.expect( anydata.convertObject(l_actual) ).to_equal(
261+
anydata.convertObject(l_expected), a_exclude => '/EMPLOYEE/CREATED_AT|/EMPLOYEE/MODIFIED_AT'
262+
);
232263
end;
233264
```
234265

source/core/ut_annotations.pkb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ create or replace package body ut_annotations as
2727
--c_nonannotat_comment_pattern constant varchar2(30) := '^( |'||chr(09)||')*--+ *[^'||gc_annotation_qualifier||']*?$';
2828
c_comment_replacer_patter constant varchar2(50) := '{COMMENT#%N%}';
2929
c_comment_replacer_regex_ptrn constant varchar2(25) := '{COMMENT#(\d+)}';
30-
c_rgexp_identifier constant varchar2(50) := '[a-z][a-z0-9#_$]*';
30+
c_regexp_identifier constant varchar2(50) := '[a-z][a-z0-9#_$]*';
3131
c_annotation_block_pattern constant varchar2(200) := '(({COMMENT#.+}'||chr(10)||')+)( |'||chr(09)||')*(procedure|function)\s+(' ||
32-
c_rgexp_identifier || ')';
33-
c_annotation_pattern constant varchar2(50) := gc_annotation_qualifier || c_rgexp_identifier || '[ '||chr(9)||']*(\(.*?\)\s*?$)?';
32+
c_regexp_identifier || ')';
33+
c_annotation_pattern constant varchar2(50) := gc_annotation_qualifier || c_regexp_identifier || '[ '||chr(9)||']*(\(.*?\)\s*?$)?';
3434

3535

3636
function delete_multiline_comments(a_source in clob) return clob is
@@ -75,7 +75,7 @@ create or replace package body ut_annotations as
7575

7676
-- get the annotation name and it's parameters if present
7777
l_annotation_name := lower(regexp_substr(l_annotation_str
78-
,'%(' || c_rgexp_identifier || ')'
78+
,'%(' || c_regexp_identifier || ')'
7979
,modifier => 'i'
8080
,subexpression => 1));
8181
l_annotation_params_str := trim(regexp_substr(l_annotation_str, '\((.*?)\)\s*$', subexpression => 1));
@@ -94,7 +94,7 @@ create or replace package body ut_annotations as
9494
,subexpression => 1);
9595

9696
l_param_item.key := regexp_substr(srcstr => l_param_str
97-
,pattern => '(' || c_rgexp_identifier || ')\s*='
97+
,pattern => '(' || c_regexp_identifier || ')\s*='
9898
,modifier => 'i'
9999
,subexpression => 1);
100100
l_param_item.val := trim(regexp_substr(l_param_str, '(.+?=)?(.*$)', subexpression => 2));

source/core/ut_file_mapper.pkb

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ create or replace package body ut_file_mapper is
5050

5151
function build_file_mappings(
5252
a_file_paths ut_varchar2_list,
53-
a_file_to_object_type_mapping ut_key_value_pairs := default_file_to_obj_type_map(),
54-
a_regex_pattern varchar2 := gc_file_mapping_regex,
55-
a_object_owner_subexpression positive := gc_regex_owner_subexpression,
56-
a_object_name_subexpression positive := gc_regex_name_subexpression,
57-
a_object_type_subexpression positive := gc_regex_type_subexpression
53+
a_file_to_object_type_mapping ut_key_value_pairs := null,
54+
a_regex_pattern varchar2 := null,
55+
a_object_owner_subexpression positive := null,
56+
a_object_name_subexpression positive := null,
57+
a_object_type_subexpression positive := null
5858
) return ut_file_mappings is
5959
begin
6060
return build_file_mappings(
@@ -66,43 +66,53 @@ create or replace package body ut_file_mapper is
6666
function build_file_mappings(
6767
a_object_owner varchar2,
6868
a_file_paths ut_varchar2_list,
69-
a_file_to_object_type_mapping ut_key_value_pairs := default_file_to_obj_type_map(),
70-
a_regex_pattern varchar2 := gc_file_mapping_regex,
71-
a_object_owner_subexpression positive := gc_regex_owner_subexpression,
72-
a_object_name_subexpression positive := gc_regex_name_subexpression,
73-
a_object_type_subexpression positive := gc_regex_type_subexpression
69+
a_file_to_object_type_mapping ut_key_value_pairs := null,
70+
a_regex_pattern varchar2 := null,
71+
a_object_owner_subexpression positive := null,
72+
a_object_name_subexpression positive := null,
73+
a_object_type_subexpression positive := null
7474
) return ut_file_mappings is
75-
l_key_values tt_key_values;
76-
l_mappings ut_file_mappings;
77-
l_mapping ut_file_mapping;
75+
l_file_to_object_type_mapping ut_key_value_pairs := coalesce(a_file_to_object_type_mapping, default_file_to_obj_type_map());
76+
l_regex_pattern varchar2(4000) := coalesce(a_regex_pattern, gc_file_mapping_regex);
77+
l_object_owner_subexpression positive := coalesce(a_object_owner_subexpression, gc_regex_owner_subexpression);
78+
l_object_name_subexpression positive := coalesce(a_object_name_subexpression, gc_regex_name_subexpression);
79+
l_object_type_subexpression positive := coalesce(a_object_type_subexpression, gc_regex_type_subexpression);
80+
81+
l_key_values tt_key_values;
82+
l_mappings ut_file_mappings;
83+
l_mapping ut_file_mapping;
7884
l_object_type_key varchar2(4000);
7985
l_object_type varchar2(4000);
8086
l_object_owner varchar2(4000);
8187
begin
8288
if a_file_paths is not null then
83-
l_key_values := to_hash_table(a_file_to_object_type_mapping);
89+
l_key_values := to_hash_table(l_file_to_object_type_mapping);
8490
l_mappings := ut_file_mappings();
91+
8592
for i in 1 .. a_file_paths.count loop
86-
l_object_type_key := upper(regexp_substr(a_file_paths(i), a_regex_pattern,1,1,'i',a_object_type_subexpression));
93+
l_object_type_key := upper(regexp_substr(a_file_paths(i), l_regex_pattern, 1, 1, 'i', l_object_type_subexpression));
8794
if l_key_values.exists(l_object_type_key) then
8895
l_object_type := upper(l_key_values(l_object_type_key));
8996
else
9097
l_object_type := null;
9198
end if;
92-
l_object_owner := upper(regexp_substr(a_file_paths(i), a_regex_pattern, 1, 1, 'i', a_object_owner_subexpression));
93-
if l_object_owner is null then
94-
l_object_owner := coalesce( a_object_owner, sys_context('USERENV', 'CURRENT_SCHEMA') );
95-
end if;
99+
100+
l_object_owner := coalesce(
101+
upper(a_object_owner),
102+
upper(regexp_substr(a_file_paths(i), l_regex_pattern, 1, 1, 'i', l_object_owner_subexpression)),
103+
sys_context('USERENV', 'CURRENT_SCHEMA'));
104+
96105
l_mapping := ut_file_mapping(
97-
file_name => a_file_paths(i),
106+
file_name => a_file_paths(i),
98107
object_owner => l_object_owner,
99-
object_name => upper(regexp_substr(a_file_paths(i), a_regex_pattern, 1, 1, 'i', a_object_name_subexpression)),
100-
object_type => l_object_type
108+
object_name => upper(regexp_substr(a_file_paths(i), l_regex_pattern, 1, 1, 'i', l_object_name_subexpression)),
109+
object_type => l_object_type
101110
);
102111
l_mappings.extend();
103112
l_mappings(l_mappings.last) := l_mapping;
104113
end loop;
105114
end if;
115+
106116
return l_mappings;
107117
end;
108118

source/core/ut_file_mapper.pks

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,21 @@ create or replace package ut_file_mapper authid current_user is
2525

2626
function build_file_mappings(
2727
a_file_paths ut_varchar2_list,
28-
a_file_to_object_type_mapping ut_key_value_pairs := default_file_to_obj_type_map(),
29-
a_regex_pattern varchar2 := gc_file_mapping_regex,
30-
a_object_owner_subexpression positive := gc_regex_owner_subexpression,
31-
a_object_name_subexpression positive := gc_regex_name_subexpression,
32-
a_object_type_subexpression positive := gc_regex_type_subexpression
28+
a_file_to_object_type_mapping ut_key_value_pairs := null,
29+
a_regex_pattern varchar2 := null,
30+
a_object_owner_subexpression positive := null,
31+
a_object_name_subexpression positive := null,
32+
a_object_type_subexpression positive := null
3333
) return ut_file_mappings;
3434

3535
function build_file_mappings(
3636
a_object_owner varchar2,
3737
a_file_paths ut_varchar2_list,
38-
a_file_to_object_type_mapping ut_key_value_pairs := default_file_to_obj_type_map(),
39-
a_regex_pattern varchar2 := gc_file_mapping_regex,
40-
a_object_owner_subexpression positive := gc_regex_owner_subexpression,
41-
a_object_name_subexpression positive := gc_regex_name_subexpression,
42-
a_object_type_subexpression positive := gc_regex_type_subexpression
38+
a_file_to_object_type_mapping ut_key_value_pairs := null,
39+
a_regex_pattern varchar2 := null,
40+
a_object_owner_subexpression positive := null,
41+
a_object_name_subexpression positive := null,
42+
a_object_type_subexpression positive := null
4343
) return ut_file_mappings;
4444

4545

source/core/ut_utils.pkb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,5 +341,31 @@ create or replace package body ut_utils is
341341
return l_result;
342342
end;
343343

344+
function to_xpath(a_list varchar2, a_ancestors varchar2 := '/*/') return varchar2 is
345+
l_xpath varchar2(32767) := a_list;
346+
begin
347+
if l_xpath not like '/%' then
348+
l_xpath := to_xpath( clob_to_table(a_clob=>a_list, a_delimiter=>','), a_ancestors);
349+
end if;
350+
return l_xpath;
351+
end;
352+
353+
function to_xpath(a_list ut_varchar2_list, a_ancestors varchar2 := '/*/') return varchar2 is
354+
l_xpath varchar2(32767);
355+
l_item varchar2(32767);
356+
i integer;
357+
begin
358+
i := a_list.first;
359+
while i is not null loop
360+
l_item := trim(a_list(i));
361+
if l_item is not null then
362+
l_xpath := l_xpath || a_ancestors ||a_list(i)||'|';
363+
end if;
364+
i := a_list.next(i);
365+
end loop;
366+
l_xpath := rtrim(l_xpath,',|');
367+
return l_xpath;
368+
end;
369+
344370
end ut_utils;
345371
/

source/core/ut_utils.pks

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ create or replace package ut_utils authid definer is
7979
ex_invalid_rep_event_name exception;
8080
gc_invalid_rep_event_name constant pls_integer := -20211;
8181
pragma exception_init(ex_invalid_rep_event_name, -20211);
82-
82+
8383
-- Any of tests failed
8484
ex_some_tests_failed exception;
8585
gc_some_tests_failed constant pls_integer := -20213;
@@ -219,5 +219,9 @@ create or replace package ut_utils authid definer is
219219

220220
function convert_collection(a_collection ut_varchar2_list) return ut_varchar2_rows;
221221

222+
function to_xpath(a_list varchar2, a_ancestors varchar2 := '/*/') return varchar2;
223+
224+
function to_xpath(a_list ut_varchar2_list, a_ancestors varchar2 := '/*/') return varchar2;
225+
222226
end ut_utils;
223227
/

source/expectations/data_values/ut_data_value_anydata.tpb

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,45 +16,99 @@ create or replace type body ut_data_value_anydata as
1616
limitations under the License.
1717
*/
1818

19-
constructor function ut_data_value_anydata(self in out nocopy ut_data_value_anydata) return self as result is
20-
begin
21-
self.self_type := $$plsql_unit;
22-
self.data_type := 'undefined';
23-
return;
24-
end;
25-
2619
overriding member function is_null return boolean is
2720
begin
2821
return true;
2922
end;
3023

3124
overriding member function to_string return varchar2 is
25+
l_result varchar2(32767);
26+
l_clob clob;
3227
begin
33-
return ut_utils.to_string( to_char(null) );
28+
if self.is_null() then
29+
l_result := ut_utils.to_string( to_char(null) );
30+
else
31+
ut_expectation_processor.set_xml_nls_params();
32+
select xmlserialize(content xmltype(self.data_value) indent) into l_clob from dual;
33+
l_result := ut_utils.to_string( l_clob, null );
34+
ut_expectation_processor.reset_nls_params();
35+
end if;
36+
return self.format_multi_line( l_result );
3437
end;
3538

36-
overriding member function compare_implementation( a_other ut_data_value ) return integer is
39+
overriding member function compare_implementation(a_other ut_data_value) return integer is
40+
l_self_data xmltype;
41+
l_other_data xmltype;
42+
l_other ut_data_value_anydata;
43+
l_result integer;
44+
procedure exclude_xpaths(a_xml in out nocopy xmltype, a_xpath varchar2) is
45+
begin
46+
if a_xpath is not null then
47+
select deletexml( a_xml, a_xpath ) into a_xml from dual;
48+
end if;
49+
end;
3750
begin
38-
return null;
51+
if a_other is of (ut_data_value_anydata) then
52+
l_other := treat(a_other as ut_data_value_anydata);
53+
--needed for 11g xe as it fails on constructing XMLTYPE from null ANYDATA
54+
if not self.is_null() and not l_other.is_null() then
55+
ut_expectation_processor.set_xml_nls_params();
56+
l_self_data := xmltype.createxml(self.data_value);
57+
l_other_data := xmltype.createxml(l_other.data_value);
58+
--We use `order member function compare` to do data comparison.
59+
--Therefore, in the `ut_equals` matcher, comparison is done by simply checking
60+
-- `l_result := equal_with_nulls((self.expected = a_actual), a_actual)`
61+
--We cannot guarantee that we will always use `expected = actual ` and not `actual = expected`.
62+
--We should expect the same behaviour regardless of that is the order.
63+
-- This is why we need to coalesce `exclude_xpath` though at most one of them will always be populated
64+
exclude_xpaths(l_self_data, coalesce(self.exclude_xpath, l_other.exclude_xpath));
65+
exclude_xpaths(l_other_data, coalesce(self.exclude_xpath, l_other.exclude_xpath));
66+
ut_expectation_processor.reset_nls_params();
67+
if l_self_data is not null and l_other_data is not null then
68+
l_result := dbms_lob.compare( l_self_data.getclobval(), l_other_data.getclobval() );
69+
end if;
70+
end if;
71+
else
72+
raise value_error;
73+
end if;
74+
return l_result;
3975
end;
4076

41-
static function get_instance(a_data_value anydata) return ut_data_value_anydata is
42-
l_result ut_data_value_anydata := ut_data_value_anydata();
77+
final member procedure init(self in out nocopy ut_data_value_anydata, a_value anydata, a_self_type varchar2) is
78+
begin
79+
self.data_value := a_value;
80+
self.self_type := a_self_type;
81+
self.data_type := case when a_value is not null then lower(a_value.gettypename) else 'undefined' end;
82+
end;
83+
84+
static function get_instance(a_data_value anydata, a_exclude varchar2 := null) return ut_data_value_anydata is
85+
l_result ut_data_value_anydata := ut_data_value_object(null);
4386
l_type anytype;
4487
l_type_code integer;
4588
begin
4689
if a_data_value is not null then
4790
l_type_code := a_data_value.gettype(l_type);
4891
if l_type_code = dbms_types.typecode_object then
4992
l_result := ut_data_value_object(a_data_value);
93+
l_result.exclude_xpath := ut_utils.to_xpath(a_exclude);
5094
elsif l_type_code in (dbms_types.typecode_table, dbms_types.typecode_varray, dbms_types.typecode_namedcollection) then
5195
l_result := ut_data_value_collection(a_data_value);
96+
l_result.exclude_xpath := ut_utils.to_xpath(a_exclude,'/*/*/');
5297
else
5398
raise_application_error(-20000, 'Data type '||a_data_value.gettypename||' in ANYDATA is not supported by utPLSQL');
5499
end if;
55100
end if;
56101
return l_result;
57102
end;
58103

104+
static function get_instance(a_data_value anydata, a_exclude ut_varchar2_list) return ut_data_value_anydata is
105+
l_result ut_data_value_anydata;
106+
l_exclude varchar2(32767);
107+
begin
108+
l_exclude := substr(ut_utils.table_to_clob(a_exclude, ','), 1, 32767);
109+
l_result := ut_data_value_anydata.get_instance(a_data_value, l_exclude );
110+
return l_result;
111+
end;
112+
59113
end;
60114
/

0 commit comments

Comments
 (0)