Skip to content

Commit d34ab05

Browse files
committed
Created code coverage reporting with html outputs.
1 parent b4594c5 commit d34ab05

50 files changed

Lines changed: 1400 additions & 65 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

examples/RunAllExamples.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,22 @@ set echo off
44
set feedback off
55
set linesize 1000
66

7+
declare
8+
l_run_number binary_integer;
9+
begin
10+
dbms_profiler.start_profiler( run_number => l_run_number);
11+
end;
12+
/
13+
714
-- Examples for users
815
@@RunUserExamples.sql
916

1017
-- Framework developer examples
1118
@@RunDeveloperExamples.sql
19+
20+
declare
21+
l_return_code binary_integer;
22+
begin
23+
l_return_code := dbms_profiler.stop_profiler();
24+
end;
25+
/
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
set trimspool on
2+
set linesize 32767
3+
set pagesize 0
4+
set long 200000000
5+
set longchunksize 1000000
6+
@@betwnstr.sql
7+
@@test_betwnstr.pkg
8+
9+
set serveroutput on size unlimited format truncated
10+
11+
set feedback off
12+
set termout off
13+
spool coverage.html
14+
exec ut.run(user, ut_coverage_html_reporter('Demo of between string function tests'));
15+
spool off
16+
17+
18+
drop package test_betwnstr;
19+
drop function betwnstr;
20+
21+
exit
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
create global temporary table plsql_profiler_runs(
2+
/*
3+
utPLSQL - Version X.X.X.X
4+
Copyright 2016 - 2017 utPLSQL Project
5+
Licensed under the Apache License, Version 2.0 (the "License"):
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
runid number primary key, -- unique run identifier,
16+
-- from plsql_profiler_runnumber
17+
related_run number, -- runid of related run (for client/
18+
-- server correlation)
19+
run_owner varchar2(4000), -- user who started run
20+
run_date date, -- start time of run
21+
run_comment varchar2(2047), -- user provided comment for this run
22+
run_total_time number, -- elapsed time for this run
23+
run_system_info varchar2(2047), -- currently unused
24+
run_comment1 varchar2(2047), -- additional comment
25+
spare1 varchar2(256) -- unused
26+
) on commit preserve rows;
27+
28+
comment on table plsql_profiler_runs is
29+
'Run-specific information for the PL/SQL profiler';
30+
31+
create global temporary table plsql_profiler_units(
32+
/*
33+
utPLSQL - Version X.X.X.X
34+
Copyright 2016 - 2017 utPLSQL Project
35+
Licensed under the Apache License, Version 2.0 (the "License"):
36+
you may not use this file except in compliance with the License.
37+
You may obtain a copy of the License at
38+
http://www.apache.org/licenses/LICENSE-2.0
39+
Unless required by applicable law or agreed to in writing, software
40+
distributed under the License is distributed on an "AS IS" BASIS,
41+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42+
See the License for the specific language governing permissions and
43+
limitations under the License.
44+
*/
45+
runid number,
46+
unit_number number, -- internally generated library unit #
47+
unit_type varchar2(4000), -- library unit type
48+
unit_owner varchar2(4000), -- library unit owner name
49+
unit_name varchar2(4000), -- library unit name
50+
-- timestamp on library unit, can be used to detect changes to
51+
-- unit between runs
52+
unit_timestamp date,
53+
total_time number DEFAULT 0 NOT NULL,
54+
spare1 number, -- unused
55+
spare2 number, -- unused
56+
--
57+
primary key (runid, unit_number)
58+
) on commit preserve rows;
59+
60+
comment on table plsql_profiler_units is
61+
'Information about each library unit in a run';
62+
63+
create global temporary table plsql_profiler_data(
64+
/*
65+
utPLSQL - Version X.X.X.X
66+
Copyright 2016 - 2017 utPLSQL Project
67+
Licensed under the Apache License, Version 2.0 (the "License"):
68+
you may not use this file except in compliance with the License.
69+
You may obtain a copy of the License at
70+
http://www.apache.org/licenses/LICENSE-2.0
71+
Unless required by applicable law or agreed to in writing, software
72+
distributed under the License is distributed on an "AS IS" BASIS,
73+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
74+
See the License for the specific language governing permissions and
75+
limitations under the License.
76+
*/
77+
runid number, -- unique (generated) run identifier
78+
unit_number number, -- internally generated library unit #
79+
line# number not null, -- line number in unit
80+
total_occur number, -- number of times line was executed
81+
total_time number, -- total time spent executing line
82+
min_time number, -- minimum execution time for this line
83+
max_time number, -- maximum execution time for this line
84+
spare1 number, -- unused
85+
spare2 number, -- unused
86+
spare3 number, -- unused
87+
spare4 number, -- unused
88+
--
89+
primary key (runid, unit_number, line#)
90+
) on commit preserve rows;
91+
92+
comment on table plsql_profiler_data is
93+
'Accumulated data from all profiler runs';
94+
95+
create sequence plsql_profiler_runnumber start with 1 nocache;
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
create or replace package body ut_coverage is
2+
/*
3+
utPLSQL - Version X.X.X.X
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+
g_skipped_objects ut_object_names;
20+
21+
function get_coverage_id return integer is
22+
begin
23+
return ut_coverage_helper.get_coverage_id;
24+
end;
25+
26+
function coverage_start return integer is
27+
begin
28+
g_skipped_objects := ut_object_names();
29+
return ut_coverage_helper.coverage_start('utPLSQL Code coverage run '||ut_utils.to_string(systimestamp));
30+
end;
31+
32+
procedure coverage_start is
33+
l_coverage_id integer;
34+
begin
35+
l_coverage_id := coverage_start;
36+
end;
37+
38+
procedure coverage_start_develop is
39+
begin
40+
g_skipped_objects := ut_object_names();
41+
ut_coverage_helper.coverage_start_develop();
42+
end;
43+
44+
procedure coverage_flush is
45+
begin
46+
ut_coverage_helper.coverage_flush();
47+
end;
48+
49+
procedure coverage_pause is
50+
begin
51+
ut_coverage_helper.coverage_pause();
52+
end;
53+
54+
procedure coverage_resume is
55+
begin
56+
ut_coverage_helper.coverage_resume();
57+
end;
58+
59+
procedure coverage_stop is
60+
begin
61+
ut_coverage_helper.coverage_stop();
62+
end;
63+
64+
procedure skip_coverage_for(a_object ut_object_name) is
65+
begin
66+
if g_skipped_objects is null then
67+
g_skipped_objects := ut_object_names();
68+
end if;
69+
g_skipped_objects.extend;
70+
g_skipped_objects(g_skipped_objects.last) := a_object;
71+
end;
72+
73+
function get_coverage_data(a_schema_names ut_varchar2_list) return t_coverage is
74+
75+
pragma autonomous_transaction;
76+
77+
type t_coverage_row is record(
78+
name varchar2(500),
79+
line_number integer,
80+
total_occur number(38,0)
81+
);
82+
type tt_coverage_rows is table of t_coverage_row;
83+
l_line_calls ut_coverage_helper.unit_line_calls;
84+
l_result t_coverage;
85+
l_new_unit t_unit_coverage;
86+
l_skipped_objects ut_object_names := ut_object_names();
87+
88+
type t_source_lines is table of binary_integer;
89+
l_source_lines t_source_lines;
90+
line_no binary_integer;
91+
begin
92+
93+
if not ut_coverage_helper.is_develop_mode() then
94+
l_skipped_objects := ut_utils.get_utplsql_objects_list() multiset union set(g_skipped_objects);
95+
end if;
96+
97+
--prepare global temp table with sources
98+
delete from ut_coverage_sources_tmp;
99+
100+
insert into ut_coverage_sources_tmp(owner,name,line,text, to_be_skipped)
101+
select s.owner,s.name,s.line,s.text,
102+
case
103+
when
104+
-- to avoid execution of regexp_like on every line
105+
-- first do a rough check for existence of search pattern keyword
106+
(lower(s.text) like '%procedure%'
107+
or lower(s.text) like '%function%'
108+
or lower(s.text) like '%begin%'
109+
or lower(s.text) like '%end%'
110+
or lower(s.text) like '%package%'
111+
) and
112+
regexp_like(
113+
s.text,
114+
'^\s*(((not)?\s*(overriding|final|instantiable)\s*)*(constructor|member)?\s*(procedure|function)|package(\s+body)|begin|end(\s+\S+)?\s*;)', 'i'
115+
)
116+
then 'Y'
117+
end as to_be_skipped
118+
from all_source s
119+
where s.type not in ('PACKAGE', 'TYPE')
120+
and s.owner in (select t.column_value from table(a_schema_names) t)
121+
--Exclude calls to utPLSQL framework and Unit Test packages
122+
and not exists(select 1 from table(l_skipped_objects) l where s.owner = l.owner AND s.name = l.name);
123+
124+
for src_object in (
125+
select o.owner, o.name, lower(o.owner||'.'||o.name) full_name, max(o.line) lines_count,
126+
cast(
127+
collect(decode(to_be_skipped, 'Y', to_char(line))) as ut_varchar2_list
128+
) to_be_skipped_list
129+
from ut_coverage_sources_tmp o
130+
group by o.owner, o.name
131+
) loop
132+
133+
--get coverage data
134+
l_line_calls := ut_coverage_helper.get_raw_coverage_data( src_object.owner, src_object.name );
135+
136+
--if there is coverage, we need to filter out the garbage (badly indicated data from dbms_profiler)
137+
if l_line_calls.count > 0 then
138+
--remove lines that should not be indicted as meaningful
139+
for i in 1 .. src_object.to_be_skipped_list.count loop
140+
if src_object.to_be_skipped_list(i) is not null then
141+
l_line_calls.delete(src_object.to_be_skipped_list(i));
142+
end if;
143+
end loop;
144+
end if;
145+
146+
if not l_result.objects.exists(src_object.full_name) then
147+
l_result.objects(src_object.full_name) := l_new_unit;
148+
end if;
149+
l_result.total_lines := l_result.total_lines + src_object.lines_count;
150+
l_result.objects(src_object.full_name).total_lines := src_object.lines_count;
151+
--map to results
152+
line_no := l_line_calls.first;
153+
if line_no is null then
154+
l_result.uncovered_lines := l_result.uncovered_lines + src_object.lines_count;
155+
l_result.objects(src_object.full_name).uncovered_lines := src_object.lines_count;
156+
else
157+
loop
158+
exit when line_no is null;
159+
160+
if l_line_calls(line_no) > 0 then
161+
l_result.covered_lines := l_result.covered_lines + 1;
162+
l_result.executions := l_result.executions + l_line_calls(line_no);
163+
l_result.objects(src_object.full_name).covered_lines := l_result.objects(src_object.full_name).covered_lines + 1;
164+
l_result.objects(src_object.full_name).executions := l_result.objects(src_object.full_name).executions + l_line_calls(line_no);
165+
elsif l_line_calls(line_no) = 0 then
166+
l_result.uncovered_lines := l_result.uncovered_lines + 1;
167+
l_result.objects(src_object.full_name).uncovered_lines := l_result.objects(src_object.full_name).uncovered_lines + 1;
168+
end if;
169+
l_result.objects(src_object.full_name).lines(line_no) := l_line_calls(line_no);
170+
171+
line_no := l_line_calls.next(line_no);
172+
end loop;
173+
end if;
174+
175+
176+
end loop;
177+
178+
commit;
179+
return l_result;
180+
end get_coverage_data;
181+
182+
function get_schema_names_from_run(a_run ut_run) return ut_varchar2_list is
183+
type t_schema_names is table of boolean index by varchar2(500);
184+
l_schema_names t_schema_names;
185+
l_result ut_varchar2_list;
186+
187+
l_schema_name varchar2(500);
188+
189+
procedure get_suite_item_schema_names(a_suite_item ut_logical_suite, a_schema_names in out nocopy t_schema_names) is
190+
begin
191+
if a_suite_item is of (ut_suite) then
192+
a_schema_names(a_suite_item.object_owner) := true;
193+
elsif a_suite_item.items is not null then
194+
for i in 1 .. a_suite_item.items.count loop
195+
if a_suite_item is of (ut_logical_suite) then
196+
get_suite_item_schema_names(treat( a_suite_item.items(i) as ut_logical_suite), a_schema_names);
197+
end if;
198+
end loop;
199+
end if;
200+
end;
201+
202+
begin
203+
if a_run is not null and a_run.items is not null then
204+
for i in 1 .. a_run.items.count loop
205+
if a_run.items(i) is of (ut_logical_suite) then
206+
get_suite_item_schema_names(treat( a_run.items(i) as ut_logical_suite), l_schema_names);
207+
end if;
208+
end loop;
209+
end if;
210+
if l_schema_names.count > 0 then
211+
l_result := ut_varchar2_list();
212+
l_schema_name := l_schema_names.first;
213+
loop
214+
exit when l_schema_name is null;
215+
l_result.extend;
216+
l_result(l_result.last) := l_schema_name;
217+
l_schema_name := l_schema_names.next(l_schema_name);
218+
end loop;
219+
end if;
220+
return l_result;
221+
end;
222+
end;
223+
/

0 commit comments

Comments
 (0)