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

Skip to content

Commit 5bf992c

Browse files
committed
Added readme for coverage.
Reworked coverage to exclude all unit tests from a given schema (regardless if they get executed or not). Updated unit test runner with new static file list. Added placeholders for documentation of ut.run and reporters. Improved parametrization of coverage reporting API. Added ability to provide object_owner, when passing file names list to be matched using regex.
1 parent 8797c84 commit 5bf992c

14 files changed

Lines changed: 536 additions & 236 deletions

docs/userguide/coverage.md

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
# Coverage
2+
utPLSQL comes with build-in coverage reporting engine. The code coverage reporting is based off DBMS_PROFILER package. Code coverage is gathered for the following source types:
3+
* package bodies
4+
* type bodies
5+
* triggers
6+
7+
Note:
8+
9+
> The package specifications and type specifications are explicitly excluded from code coverage analysis.This limitation is introduced to avoid false-negatives. Most of the package specifications don't contain executable code. The only exception is initialization of global constants and variables in package specification.Since, most of package specifications are not executable at all, there is no information available on the number of lines covered and those would eb reported as 0% covered, which is not desired.
10+
11+
To obtain information about code coverage of your Unit Tests, all you need to do is run your unit tests with one of build-in code coverage reporters.
12+
Following code coverage reporters are supplied with utPLSQL:
13+
* `ut_coverage_html_reporter` - generates a HTML coverage report providing summary and detailed information on code coverage. The html reporter is based on open-source [simplecov-html](https://github.com/colszowka/simplecov-html) reporter for Ruby. It includes source code of the code that was covered (if possible)
14+
* `ut_coverage_json_reporter` - generates a JSON coverage report providing detailed information on code coverage with line numbers. This coverage report is designed to be consumed by cloud services like [coveralls](https://coveralls.io)
15+
* `ut_coverage_xml_reporter` - generates a JSON coverage report providing detailed information on code coverage with line numbers. This coverage report is designed to be consumed by local services like [sonarqube](https://about.sonarqube.com/)
16+
17+
##Security model
18+
Code coverage is using DBMS_PROFILER to gather information about execution of code under test and therefore follows the [DBMS_PROFILER's Security Model](https://docs.oracle.com/database/121/ARPLS/d_profil.htm#ARPLS67465)
19+
In order to be able to gather coverage information, user executing unit tests needs to be either:
20+
* Owner of the code that is tested
21+
* Have the following privileges to be able to gather coverage on code owned by other users:
22+
* `create any procedure` system privilege
23+
* `execute` privilege on the code that is tested (not only the unit tests) or `execute any procedure` system privilege
24+
25+
If you have `execute` privilege on the code that are tested, but do not have `create any procedure` system privilege, the code that is tested will be reported as not covered (coverage = 0%).
26+
If you have `execute` privilege only on the unit tests, but do not have `execute` privilege on the code that is tested, the code will not be reported by coverage - as if it did not exist in the database.
27+
If the code that is testes is complied as NATIVE, the code coverage will not be reported as well.
28+
29+
##Running unite tests with coverage
30+
Using code coverage functionality is as easy as using any other [reporter](reporters.md) for utPLSQL project. All you need to do is run your tests from your preferred SQL tool and save the outcomes of reporter to a file.
31+
All you need to do, is pass the constructor of the reporter to your `ut.run`
32+
```sql
33+
begin
34+
ut.run(ut_coverage_html_reporter());
35+
end;
36+
/
37+
```
38+
Executes all unit tests in current schema, gather information about code coverage and output the html text into DBMS_OUTPUT.
39+
The `ut_coverage_html_reporter` will produce a interactive HTML report. You may see a sample of code coverage for utPLSQL project [here](https://jgebal.github.io/utPLSQL-coverage-html/)
40+
41+
The report provides a summary information with list of source code that was expected to be covered.
42+
43+
![Coverage Summary page](images/coverage_html_summary.png)
44+
45+
The report allow to navigate to every source and inspect line by line coverage.
46+
47+
![Coverage Details page](images/coverage_html_details.png)
48+
49+
50+
##Coverage reporting options
51+
By default the database schema/schemes containing the tests that were executed during the run, are fully reported by coverage reporter.
52+
All valid unit tests are excluded from the report regardless if they were invoked or not. This way the coverage report is not affected by presence of tests and contains only the tested code.
53+
54+
The default behavior of coverage reporters can be altered, depending on your needs.
55+
56+
###Including/excluding objects in coverage reports
57+
The most basic options are the include/exclude objects lists.
58+
You may specify both include and exclude objects lists to specify which objects are to be included in the report and which are to be excluded.
59+
Both of those options are meant to be used to narrow down the scope of unit test runs, that is broad by default.
60+
61+
Example:
62+
```sql
63+
exec ut.run('ut3_user.test_award_bonus', ut_coverage_html_reporter(a_include_object_list=>ut_varchar2_list('ut3_user.award_bonus')));
64+
```
65+
Executes test `test_award_bonus` and gather coverage only on object `ut3_user.award_bonus`
66+
67+
Alternatively you could run:
68+
```sql
69+
exec ut.run('ut3_user.test_award_bonus', ut_coverage_html_reporter(a_exclude_object_list=>ut_varchar2_list('ut3_user.betwnstr')));
70+
```
71+
Executes test `test_award_bonus` and gather on all objects in schema `ut3_user` except valid unit test objects and object `betwnstr` that schema.
72+
73+
You can also combine the parameters and both will be applied.
74+
75+
###Defining different schema name(s)
76+
In some architectures, you might end up in a situation, where your unit tests exist in a different schema than the tested code.
77+
This is not the default or recommended approach but is supporter by utPLSQL.
78+
In such scenarios, you would probably have a separate database schema to hold unit tests and a separate schema/schemes to hold the tested code.
79+
Since by default, coverage reporting is done on the schema/schemes that the invoked tests are on, the code will not be included in coverage report as it is in a different schema than the invoked tests.
80+
81+
In this situation you need to provide list of schema names that the tested code is in. This option overrides the default schema names for coverage.
82+
83+
Example:
84+
```sql
85+
exec ut.run('ut3_user.test_award_bonus', ut_coverage_html_reporter(a_schema_names=>ut_varchar2_list('usr')));
86+
```
87+
Executes test `test_award_bonus` in schema `ut3_user` and gather coverage for that execution on all non `unit-test` objects from schema `usr`.
88+
89+
You can combine schema names with include/exclude parameters and all will be applied.
90+
The `a_schema_names` parameter takes precedence however, so if include list contains objects from other schemes, that will not be considered.
91+
92+
Example:
93+
```sql
94+
begin
95+
ut.run(
96+
'ut3_user.test_award_bonus',
97+
ut_coverage_html_reporter(
98+
a_schema_names => ut_varchar2_list('usr'),
99+
a_include_object_list => ut_varchar2_list('usr.award_bonus'),
100+
a_exclude_object_list => ut_varchar2_list('usr.betwnstr')
101+
)
102+
);
103+
end;
104+
```
105+
Executes test `test_award_bonus` in schema `ut3_user` and gather coverage for that execution on `award_bonus` object from schema `usr`. The exclude list is of no relevance as it is not overlapping with include list.
106+
107+
###Working with projects and project files
108+
Both `sonar` and `coveralls` are utilities that are more project-oriented than database-centric. The report statistics and coverage for project files in version control system.
109+
Nowadays, most of database projects are moving away from database-centric approach towards project/product-centric approach.
110+
Coverage reporting of utPLSQL allows you to perform code coverage analysis for your project files.
111+
This feature is supported by all build-in coverage reporting formats.
112+
113+
When using this invocation syntax, coverage is only reported for the provided files, so using project files as input for coverage is also a way of limiting the scope of coverage analysis.
114+
This syntax also allows usage of `a_include_object_list` and `a_exclude_object_list` as optional parameters to filter the scope of analysis.
115+
116+
117+
**Reporting using externally provided file mapping**
118+
One of ways to perform coverage reporting on your project files is to provide to the coverage reporter a list of file path/names along with mapping to corresponding object name and object type.
119+
120+
Example:
121+
```sql
122+
begin
123+
ut.run(
124+
'usr',
125+
ut_coverage_html_reporter(
126+
a_file_mappings=>ut_coverage_file_mappings(
127+
ut_coverage_file_mapping(
128+
file_name => 'sources/hr/award_bonus.prc',
129+
object_owner => 'usr',
130+
object_name => 'award_bonus',
131+
object_type => 'procedure'
132+
),
133+
ut_coverage_file_mapping(
134+
file_name => 'sources/hr/betwnstr.fnc',
135+
object_owner => 'usr',
136+
object_name => 'betwnstr',
137+
object_type => 'function'
138+
)
139+
)
140+
)
141+
);
142+
end;
143+
```
144+
Executes all tests in schema `usr` and reports coverage for that execution on procedure `award_bonus` and function `betwnstr`. The coverage report is mapped-back to file-system object names with paths.
145+
146+
**Reporting using regex file mapping rule**
147+
If file names and paths in your project follow a well established naming conventions,
148+
then you can you can use the predefined rule for mapping file names to object names or you can define your own rule and pass it to the coverage reporter at runtime.
149+
150+
Example of running with predefined regex mapping rule.
151+
```sql
152+
begin
153+
ut.run(
154+
'usr',
155+
ut_coverage_html_reporter(
156+
a_file_paths => ut_varchar2_list('sources/hr/award_bonus.prc','sources/hr/betwnstr.fnc')
157+
)
158+
);
159+
end;
160+
```
161+
162+
The predefined rule is based on the following default values for parameters:
163+
* `a_regex_pattern => '.*(\\|\/)((\w+)\.)?(\w+)\.(\w{3})'`
164+
* `a_object_owner_subexpression => 3`
165+
* `a_object_name_subexpression => 4`
166+
* `a_object_type_subexpression => 5`
167+
* `a_file_to_object_type_mapping` - defined in table below
168+
169+
The predefined file extension to object type mappings
170+
171+
| file extension | object type |
172+
| -------------- | ----------- |
173+
| tpb | type body |
174+
| pkb | package body |
175+
| trg | trigger |
176+
| fnc | function |
177+
| prc | procedure |
178+
179+
Below are examples of filename forms taht will be mapped correctly using predefined rules.
180+
* `[...]directory[/subdirectory[/...]]/object_name.(tpb|pkb|trg|fnc|prc)`
181+
* `[...]directory[/subdirectory[/...]]/schema_name.object_name.(tpb|pkb|trg|fnc|prc)`
182+
* `[...]directory[\subdirectory[\...]]\object_name.(tpb|pkb|trg|fnc|prc)`
183+
* `[...]directory[\subdirectory[\...]]\schema_name.object_name.(tpb|pkb|trg|fnc|prc)`
184+
185+
If file names in your project structure are not prefixed with schema name (like above), the coverage report will look for objects to match the file names in the `current schema` of the connection that was used to execute tests with coverage.
186+
If for whatever reasons you use a user and current schema that is different then schem that holds your project code, you should use `a_schema_name` parameter to inform coverage reporter about database schema to be used for object lookup.
187+
188+
Example:
189+
```sql
190+
begin
191+
ut.run(
192+
'usr',
193+
ut_coverage_html_reporter(
194+
a_schema_name => 'hr',
195+
a_file_paths => ut_varchar2_list('sources/hr/award_bonus.prc','sources/hr/betwnstr.fnc')
196+
)
197+
);
198+
end;
199+
```
200+
201+
If your project structure is different, you may define your own mapping rule using regex.
202+
203+
Example:
204+
```sql
205+
begin
206+
ut.run(
207+
'usr',
208+
ut_coverage_html_reporter(
209+
a_file_paths => ut_varchar2_list('sources/hr/procedures/award_bonus.sql','sources/hr/functions/betwnstr.sql'),
210+
a_regex_pattern => '.*(\\|\/)(\w+)\.(\w+)\.(\w{3})',
211+
a_object_owner_subexpression => 2,
212+
a_object_type_subexpression => 3,
213+
a_object_name_subexpression => 4,
214+
a_file_to_object_type_mapping => ut_key_value_pairs(
215+
ut_key_value_pair('functions', 'function'),
216+
ut_key_value_pair('procedures', 'procedure')
217+
)
218+
);
219+
end;
220+
```
221+
29.6 KB
Loading
42.2 KB
Loading

docs/userguide/reporters.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#Unit Test reporters
2+
TODO
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#Running unit tests
2+
TODO

readme.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,10 @@ end;
158158

159159
# Running tests
160160

161-
To execute using IDE ()TOAD/SQLDeveloper/PLSQLDeveloper/other) just run the following.
161+
To execute using development IDE (TOAD/SQLDeveloper/PLSQLDeveloper/other) just run the following.
162162
```sql
163163
begin
164-
ut_runner.run();
164+
ut.run();
165165
end;
166166
/
167167
```

source/core/coverage/ut_coverage.pkb

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,42 +45,48 @@ create or replace package body ut_coverage is
4545
function default_file_to_obj_type_map return ut_key_value_pairs is
4646
begin
4747
return ut_key_value_pairs(
48+
ut_key_value_pair('fnc', 'FUNCTION'),
49+
ut_key_value_pair('prc', 'PROCEDURE'),
50+
ut_key_value_pair('trg', 'TRIGGER'),
4851
ut_key_value_pair('tpb', 'TYPE BODY'),
4952
ut_key_value_pair('pkb', 'PACKAGE BODY'),
5053
ut_key_value_pair('trg', 'TRIGGER')
5154
);
5255
end;
5356

5457
function build_file_mappings(
55-
a_file_paths ut_varchar2_list, a_file_to_object_type_mapping ut_key_value_pairs,
56-
a_regex_pattern varchar2, a_object_owner_subexpression positive, a_object_name_subexpression positive, a_object_type_subexpression positive
58+
a_object_owner varchar2,
59+
a_file_paths ut_varchar2_list,
60+
a_file_to_object_type_mapping ut_key_value_pairs := default_file_to_obj_type_map(),
61+
a_regex_pattern varchar2 := gc_file_mapping_regex,
62+
a_object_owner_subexpression positive := gc_regex_owner_subexpression,
63+
a_object_name_subexpression positive := gc_regex_name_subexpression,
64+
a_object_type_subexpression positive := gc_regex_type_subexpression
5765
) return ut_coverage_file_mappings is
5866
type tt_key_values is table of varchar2(4000) index by varchar2(4000);
5967
l_key_values tt_key_values;
6068
l_mappings ut_coverage_file_mappings;
6169
l_mapping ut_coverage_file_mapping;
6270
l_object_type_key varchar2(4000);
6371
l_object_type varchar2(4000);
64-
l_file_to_object_type_mapping ut_key_value_pairs;
6572
begin
66-
l_file_to_object_type_mapping := nvl(a_file_to_object_type_mapping, default_file_to_obj_type_map());
67-
for i in 1 .. l_file_to_object_type_mapping.count loop
68-
l_key_values(upper(l_file_to_object_type_mapping(i).key)) := l_file_to_object_type_mapping(i).value;
73+
for i in 1 .. a_file_to_object_type_mapping.count loop
74+
l_key_values(upper(a_file_to_object_type_mapping(i).key)) := a_file_to_object_type_mapping(i).value;
6975
end loop;
7076
if a_file_paths is not null then
7177
l_mappings := ut_coverage_file_mappings();
7278
for i in 1 .. a_file_paths.count loop
7379
l_object_type_key := upper(regexp_substr(a_file_paths(i), a_regex_pattern,1,1,'i',a_object_type_subexpression));
7480
if l_key_values.exists(l_object_type_key) then
75-
l_object_type := l_key_values(l_object_type_key);
81+
l_object_type := upper(l_key_values(l_object_type_key));
7682
else
7783
l_object_type := null;
7884
end if;
7985
l_mapping := ut_coverage_file_mapping(
8086
file_name => a_file_paths(i),
81-
object_owner => nvl(
87+
object_owner => coalesce(
8288
upper(regexp_substr(a_file_paths(i), a_regex_pattern, 1, 1, 'i', a_object_owner_subexpression))
83-
, sys_context('USERENV', 'CURRENT_SCHEMA')
89+
, a_object_owner, sys_context('USERENV', 'CURRENT_SCHEMA')
8490
),
8591
object_name => upper(regexp_substr(a_file_paths(i), a_regex_pattern, 1, 1, 'i', a_object_name_subexpression)),
8692
object_type => l_object_type
@@ -167,13 +173,9 @@ create or replace package body ut_coverage is
167173
ut_coverage_helper.coverage_stop();
168174
end;
169175

170-
procedure skip_coverage_for(a_owner varchar2, a_name varchar2) is
176+
procedure skip_coverage_for(a_ut_objects ut_object_names) is
171177
begin
172-
if g_skipped_objects is null then
173-
g_skipped_objects := ut_object_names();
174-
end if;
175-
g_skipped_objects.extend;
176-
g_skipped_objects(g_skipped_objects.last) := ut_object_name(a_owner, a_name);
178+
g_skipped_objects := a_ut_objects;
177179
end;
178180

179181
function get_coverage_data return t_coverage is
@@ -254,7 +256,7 @@ create or replace package body ut_coverage is
254256
end as to_be_skipped
255257
from all_source s
256258
where s.type not in ('PACKAGE', 'TYPE')
257-
and s.owner in (select t.column_value from table(l_schema_names) t)
259+
and s.owner in (select upper(t.column_value) from table(l_schema_names) t)
258260
and (g_include_list is null or (s.owner, s.name) in (select il.owner, il.name from table(g_include_list) il ) )
259261
and (s.owner, s.name) not in (select el.owner, el.name from table(g_exclude_list) el)
260262
--Exclude calls to utPLSQL framework and Unit Test packages

source/core/coverage/ut_coverage.pks

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ create or replace package ut_coverage authid current_user is
1616
limitations under the License.
1717
*/
1818

19+
gc_file_mapping_regex constant varchar2(100) := '.*(\\|\/)((\w+)\.)?(\w+)\.(\w{3})';
20+
gc_regex_owner_subexpression constant positive := 3;
21+
gc_regex_name_subexpression constant positive := 4;
22+
gc_regex_type_subexpression constant positive := 5;
23+
1924
-- total run coverage information
2025
subtype t_full_name is varchar2(500);
2126
subtype t_object_name is varchar2(250);
@@ -49,12 +54,13 @@ create or replace package ut_coverage authid current_user is
4954
function default_file_to_obj_type_map return ut_key_value_pairs;
5055

5156
function build_file_mappings(
57+
a_object_owner varchar2,
5258
a_file_paths ut_varchar2_list,
53-
a_file_to_object_type_mapping ut_key_value_pairs,
54-
a_regex_pattern varchar2,
55-
a_object_owner_subexpression positive,
56-
a_object_name_subexpression positive,
57-
a_object_type_subexpression positive
59+
a_file_to_object_type_mapping ut_key_value_pairs := default_file_to_obj_type_map(),
60+
a_regex_pattern varchar2 := gc_file_mapping_regex,
61+
a_object_owner_subexpression positive := gc_regex_owner_subexpression,
62+
a_object_name_subexpression positive := gc_regex_name_subexpression,
63+
a_object_type_subexpression positive := gc_regex_type_subexpression
5864
) return ut_coverage_file_mappings;
5965

6066
function get_include_schema_names return ut_varchar2_list;
@@ -91,7 +97,7 @@ create or replace package ut_coverage authid current_user is
9197

9298
procedure coverage_flush;
9399

94-
procedure skip_coverage_for(a_owner varchar2, a_name varchar2);
100+
procedure skip_coverage_for(a_ut_objects ut_object_names);
95101

96102
function get_coverage_data return t_coverage;
97103

0 commit comments

Comments
 (0)