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

Skip to content

Feature/rework matcher #1076

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Feb 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
48abf1a
Initial checkin
lwasylow Jun 1, 2020
7041912
added expectation
lwasylow Jun 2, 2020
13d86b6
Adding more methods
lwasylow Jun 2, 2020
caeeb30
Adding contain and not contain
lwasylow Jun 2, 2020
8ff4f6b
Adding code and tests,.
lwasylow Jun 6, 2020
89cb0bc
Fixingtoo long names
lwasylow Jun 6, 2020
a5481ac
fixing issue
lwasylow Jun 6, 2020
9ee0e75
Update documentation and tests
lwasylow Jun 7, 2020
a660208
First code corrections
lwasylow Jun 14, 2020
ada1ea4
Resolving another comment
lwasylow Jun 14, 2020
fe1b0cb
Updating issues
lwasylow Jun 14, 2020
013db74
Update tble
lwasylow Jun 14, 2020
b5c73b4
Separated into two different matchers
jgebal Jun 19, 2020
2e721da
CHECKPOINT
lwasylow Jun 19, 2020
fbc0f26
Merge branch 'feature/rework_matcher' of https://github.com/utPLSQL/u…
lwasylow Jun 19, 2020
f182430
Update uninstall
lwasylow Jun 19, 2020
e3fb2d7
Update progress
lwasylow Jun 19, 2020
322e0a1
Making code a bit more readable
lwasylow Jun 19, 2020
ff15596
Added tests for `be_within_pct`
jgebal Jun 20, 2020
3cae9a2
Fixed test issue
jgebal Jun 20, 2020
22549db
Fixed stacktrace for failed expectations on chained matchers.
jgebal Jun 21, 2020
1d771a5
Update timestamps
lwasylow Jun 25, 2020
363c333
Fixed and simplified matcher
jgebal Jun 27, 2020
0ea8925
Fixed native dynamic SQL types compatibility for 11g
jgebal Jun 28, 2020
7c01afb
Fixed native dynamic SQL types compatibility for 11g
jgebal Jun 28, 2020
66d92fc
Merge branch 'develop' into feature/rework_matcher
jgebal Jan 28, 2022
590fb38
Improving test stability (flaky tests)
jgebal Jan 28, 2022
5e2642a
Removing reference to ut3_develop schema.
jgebal Jan 28, 2022
ed06e33
Relaxed sql-injection check to work with 11g2
jgebal Jan 28, 2022
38f3cbc
Fixed issues with comparison of dates&timestamps using interval year-…
jgebal Jan 30, 2022
86e84c8
Improving code coverage.
jgebal Jan 31, 2022
718ac0d
Improved test stability.
jgebal Jan 31, 2022
6dbef20
Fixing build process to build using develop branch as testing framewo…
jgebal Jan 31, 2022
25b55b4
Improving test coverage.
jgebal Jan 31, 2022
5b5a5c0
Improving matcher documentation
jgebal Jan 31, 2022
7fec0f9
Added tests for 0 value of actual and expected and actual greater tha…
jgebal Feb 2, 2022
fd7ef9c
Fixed issues with precission of distance. `NATURAL` is a subtype of i…
jgebal Feb 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:

- name: Download utPLSQL release for testing
# For PR build - test using target branch as framework, for branch build use self as testing framework
run: git clone --depth=1 --branch=${CI_HEAD_REF:-$CI_REF_NAME} https://github.com/utPLSQL/utPLSQL.git $UTPLSQL_DIR
run: git clone --depth=1 --branch=${CI_BASE_REF:-$CI_REF_NAME} https://github.com/utPLSQL/utPLSQL.git $UTPLSQL_DIR

- name: Update privileges on sources
run: chmod -R go+w ./{source,test,examples,${UTPLSQL_DIR}/source}
Expand Down
142 changes: 124 additions & 18 deletions docs/userguide/expectations.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,24 +316,25 @@ Since NULL is neither *true* nor *false*, both expectations will report failure.

The matrix below illustrates the data types supported by different matchers.

| Matcher | blob | boolean | clob | date | number | timestamp | timestamp<br>with<br>timezone | timestamp<br>with<br>local<br>timezone | varchar2 | interval<br>year<br>to<br>month | interval<br>day<br>to<br>second | cursor | nested<br>table<br>/ varray | object | json |
| :---------------------: | :--: | :-----: | :--: | :--: | :----: | :-------: | :---------------------------: | :------------------------------------: | :------: | :-----------------------------: | :-----------------------------: | :----: | :-------------------------: | :----: | :--: |
| **be_not_null** | X | 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 | 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 | X |
| **contain** | | | | | | | | | | | | X | X | X | |
| **match** | | | X | | | | | | X | | | | | | |
| **be_like** | | | X | | | | | | X | | | | | | |
| **be_empty** | X | | X | | | | | | | | | X | X | | X |
| **have_count** | | | | | | | | | | | | X | X | | X |

| Matcher | blob | boolean | clob | date | number | timestamp | timestamp<br>with<br>timezone | timestamp<br>with<br>local<br>timezone | varchar2 | interval<br>year<br>to<br>month | interval<br>day<br>to<br>second | cursor | nested<br>table<br>/ varray | object | json |
| :-----------------------: | :--: | :-----: | :--: | :--: | :----: | :-------: | :---------------------------: | :------------------------------------: | :------: | :-----------------------------: | :-----------------------------: | :----: | :-------------------------: | :----: | :--: |
| **be_not_null** | X | 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 | 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 | X |
| **contain** | | | | | | | | | | | | X | X | X | |
| **match** | | | X | | | | | | X | | | | | | |
| **be_like** | | | X | | | | | | X | | | | | | |
| **be_empty** | X | | X | | | | | | | | | X | X | | X |
| **have_count** | | | | | | | | | | | | X | X | | X |
| **be_within().of_()** | | | | X | X | X | X | X | | | | | | | |
| **be_within_pct().of_()** | | | | | X | | | | | | | | | | |

# Expecting exceptions

Expand Down Expand Up @@ -1093,6 +1094,111 @@ SUCCESS
<ROW><UT_VARCHAR2_LIST>D</UT_VARCHAR2_LIST></ROW><ROW><UT_VARCHAR2_LIST>E</UT_VARCHAR2_LIST></ROW><ROW><UT_VARCHAR2_LIST>F</UT_VARCHAR2_LIST></ROW>
```

## to_be_within of

Determines wheter expected value is within range (tolerance) from another value.

The logical formual used for calcuating the matcher is:
```
result := ( abs( expected - actual ) <= distance )
```
The actual formula used for calculation is more complex to handle different data-types of expected/actual values as well as differnet types of distance value.
The matcher will fail if the `expected` and `actual` are more than `distance` apart from each other.
The matcher will fail if the dataypes of `expected` and `actual` are not the same.

The matcher works with data-types: `number`, `date`, `timestamp`, `timestamp with time zone`, `timestamp with local time zone`
The data-types of compared values must match exactly and if type does not match, the expectation will fail.

| expected/actual<br>data-type | distance data-type |
|:------------------------------:|:----------------------:|
| number | number |
| date | interval day to second |
| date | interval year to month |
| timestamp | interval day to second |
| timestamp | interval year to month |
| timestamp with time zone | interval day to second |
| timestamp with time zone | interval year to month |
| timestamp with local time zone | interval day to second |
| timestamp with local time zone | interval year to month |


The distance must be expressed as a non-negative number or non-negative interval.

>Note:
> Interval year-to-moth as a distance is giving sucess if the distance between the given dates/timestamps evaluates to value less or equal of the specified interval
> Keep in mind that a checking for distance of `interval '0-1' year to month` will actuall be successful if the distance is less than a month and 15 days.
> This is due to how oracle evaluates conversion between timestamp difference converted to `year to month interval`.
> The behavior is similar to a call to `months_between()` function with results rounded to full monts ie. round(months_between(date, date))

**Example 1.**
```sql
begin
ut.expect(3).to_be_within(1).of_(4);
end;
/
```

**Example 2.**
```sql
begin
ut.expect(3).to_be_within(1).of_(5);
end;
/
```

Returns following output via DBMS_OUTPUT:
```
Failures:

1) wihtin_test
Actual: 3 (number) was expected to be within 1 of 5 (number)
at "UT3_DEVELOP.UT_BE_WITHIN.OF_", line 48 l_result.expectation.to_(l_result );
at "UT3_DEVELOP.TEST_BETWNSTR.WIHTIN_TEST", line 5
```

**Example 3.**
```sql
begin
ut.expect(sysdate).to_be_within(interval '1' day).of_(sysdate+2);
end;
/
```

Returns following output via DBMS_OUTPUT:
```
Failures:

1) wihtin_test
Actual: 2020-06-07T13:32:58 (date) was expected to be within 1 day of 2020-06-09T13:32:58 (date)
at "UT3_DEVELOP.UT_BE_WITHIN.OF_", line 55 l_result.expectation.to_(l_result );
at "UT3_DEVELOP.TEST_BETWNSTR.WIHTIN_TEST", line 5
```


## to_be_within_pct of

Determines wheter actual value is within percentage range of expected value.
The matcher only works with `number` data-type.

The percentage deviation (distance) must be expressed as a non-negative number.
The formula used for calcuation of expectation is:
```
result := ( ( distance ) * expected >= abs( expected - actual ) * 100 )
```

**Example 1.**
```sql
begin
ut.expect(9).to_be_within_pct(10).of_(10);
end;
/
```

```
SUCCESS
Actual: 9 (number) was expected to be within 10 % of 10 (number)
```


## Comparing cursors, object types, nested tables and varrays

Expand Down
1 change: 1 addition & 0 deletions source/api/be_within.syn
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
create synonym be_within for ut_be_within;
1 change: 1 addition & 0 deletions source/api/be_within_pct.syn
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
create synonym be_within_pct for ut_be_within_pct;
2 changes: 1 addition & 1 deletion source/core/ut_expectation_processor.pkb
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ create or replace package body ut_expectation_processor as
-- when 11g and 12c reports only package name
function cut_header_and_expectations( a_stack varchar2 ) return varchar2 is
begin
return regexp_substr( a_stack, '(.*\.(UT_EXPECTATION[A-Z0-9#_$]*|UT|UTASSERT2?)(\.[A-Z0-9#_$]+)?\s+)+((.|\s)*)', 1, 1, 'm', 4);
return regexp_substr( a_stack, '(.*\.(UT_EQUAL|UT_BE_WITHIN[A-Z0-9#_$]*|UT_EXPECTATION[A-Z0-9#_$]*|UT|UTASSERT2?)(\.[A-Z0-9#_$]+)?\s+)+((.|\s)*)', 1, 1, 'm', 4);
end;
function cut_address_columns( a_stack varchar2 ) return varchar2 is
begin
Expand Down
58 changes: 58 additions & 0 deletions source/core/ut_utils.pkb
Original file line number Diff line number Diff line change
Expand Up @@ -899,5 +899,63 @@ create or replace package body ut_utils is
end;
end;

function interval_to_text(a_interval dsinterval_unconstrained) return varchar2 is
l_day varchar2(100) := extract(day from a_interval);
l_hour varchar2(100) := extract(hour from a_interval);
l_minute varchar2(100) := extract(minute from a_interval);
l_second varchar2(100) := extract(second from a_interval);
l_result varchar2(32767);
begin
l_result := case
when l_day = 1 then l_day ||' day'
when l_day > 1 then l_day ||' days'
end ||
case
when l_hour = 1 then ' '|| l_hour ||' hour'
when l_hour > 1 then ' '|| l_hour ||' hours'
end ||
case
when l_minute = 1 then ' '||l_minute ||' minute'
when l_minute > 1 then ' '||l_minute ||' minutes'
end ||
case
when l_second > 1 then ' '||l_second ||' seconds'
when l_second = 1 then ' '||l_second ||' second'
when l_second > 0 then ' '||l_second ||' seconds'
end;
l_result :=
case
when a_interval is null then 'NULL'
when l_result is null then '0 seconds'
else ltrim(l_result,' ')
end;

return l_result;
end;

function interval_to_text(a_interval yminterval_unconstrained) return varchar2 is
l_year varchar2(4) := extract(year from a_interval);
l_month varchar2(20) := extract(month from a_interval);
l_result varchar2(32767);
begin
l_result := case
when l_year = 1 then l_year ||' year'
when l_year > 1 then l_year ||' years'
end ||
case
when l_month > 1 then ' '||l_month ||' months'
when l_month = 1 then ' '||l_month ||' month'
end;
l_result :=
case
when a_interval is null then 'NULL'
when l_result is null then '0 months'
else ltrim(l_result,' ')
end;

return l_result;
end;


end ut_utils;
/
18 changes: 14 additions & 4 deletions source/core/ut_utils.pks
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ create or replace package ut_utils authid definer is
gc_more_data_string constant varchar2(5) := '[...]';
gc_more_data_string_len constant integer := length( gc_more_data_string );
gc_number_format constant varchar2(100) := 'TM9';
gc_date_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ss';
gc_timestamp_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ssxff';
gc_timestamp_tz_format constant varchar2(100) := 'yyyy-mm-dd"T"hh24:mi:ssxff tzh:tzm';
gc_date_format constant varchar2(100) := 'syyyy-mm-dd"T"hh24:mi:ss';
gc_timestamp_format constant varchar2(100) := 'syyyy-mm-dd"T"hh24:mi:ssxff';
gc_timestamp_tz_format constant varchar2(100) := 'syyyy-mm-dd"T"hh24:mi:ssxff tzh:tzm';
gc_null_string constant varchar2(4) := 'NULL';
gc_empty_string constant varchar2(5) := 'EMPTY';

Expand Down Expand Up @@ -457,6 +457,16 @@ create or replace package ut_utils authid definer is
* If null value passed returns null
*/
function qualified_sql_name(a_name varchar2) return varchar2;


/*
* Return value of interval in plain english
*/
function interval_to_text(a_interval dsinterval_unconstrained) return varchar2;

/*
* Return value of interval in plain english
*/
function interval_to_text(a_interval yminterval_unconstrained) return varchar2;

end ut_utils;
/
2 changes: 2 additions & 0 deletions source/create_grants.sql
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ grant execute on &&ut3_owner..ut_be_like to &ut3_user;
grant execute on &&ut3_owner..ut_be_not_null to &ut3_user;
grant execute on &&ut3_owner..ut_be_null to &ut3_user;
grant execute on &&ut3_owner..ut_be_true to &ut3_user;
grant execute on &&ut3_owner..ut_be_within to &ut3_user;
grant execute on &&ut3_owner..ut_be_within_pct to &ut3_user;
grant execute on &&ut3_owner..ut_contain to &ut3_user;
grant execute on &&ut3_owner..ut_equal to &ut3_user;
grant execute on &&ut3_owner..ut_have_count to &ut3_user;
Expand Down
Loading