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

Skip to content

Commit 80e6aab

Browse files
author
luw07
committed
Updated with block coverage for 12c
1 parent 5c6049c commit 80e6aab

4 files changed

Lines changed: 424 additions & 18 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
CREATE OR REPLACE PACKAGE ut_block_coverage AUTHID CURRENT_USER 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+
PROCEDURE coverage_start;
20+
21+
PROCEDURE coverage_stop;
22+
23+
--dummy out as would require changes to base reporter type
24+
PROCEDURE coverage_resume;
25+
26+
--dummy out as would require changes to base reporter type
27+
PROCEDURE coverage_pause;
28+
29+
FUNCTION get_coverage_data(a_coverage_options ut_coverage_options)
30+
RETURN ut_coverage.t_coverage;
31+
32+
END;
33+
/
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
CREATE OR REPLACE PACKAGE BODY ut_block_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_coverage_id INTEGER;
20+
21+
TYPE t_unit_line_call IS RECORD(
22+
blocks BINARY_INTEGER DEFAULT 0
23+
,covered_blocks BINARY_INTEGER DEFAULT 0
24+
,partcovered BINARY_INTEGER DEFAULT 0);
25+
26+
TYPE t_unit_line_calls IS TABLE OF t_unit_line_call INDEX BY BINARY_INTEGER;
27+
28+
TYPE t_source_lines IS TABLE OF BINARY_INTEGER;
29+
30+
-- The source query has two important transformations done in it.
31+
-- the flag: to_be_skipped ='Y' is set for a line of code that is badly reported by DBMS_PROFILER as executed 0 times.
32+
-- This includes lines that are:
33+
-- - PACKAGE, PROCEDURE, FUNCTION definition line,
34+
-- - BEGIN, END of a block
35+
-- Another transformation is adjustment of line number for TRIGGER body.
36+
-- DBMS_PROFILER is reporting line numbers for triggers not as defined in DBA_SOURCE, its usign line numbers as defined in DBA_TRIGGERS
37+
-- the DBA_TRIGGERS does not contain the trigger specification lines, only lines that define the trigger body.
38+
-- the query adjusts the line numbers for triggers by finding first occurrence of begin|declare|compound in the trigger body line.
39+
-- The subquery is optimized by:
40+
-- - COALESCE function -> it will execute only for TRIGGERS
41+
-- - scalar subquery cache -> it will only execute once for one trigger source code.
42+
FUNCTION get_cov_sources_sql(a_coverage_options ut_coverage_options) RETURN VARCHAR2 IS
43+
l_result VARCHAR2(32767);
44+
l_full_name VARCHAR2(100);
45+
l_view_name VARCHAR2(200) := ut_metadata.get_dba_view('dba_source');
46+
BEGIN
47+
IF a_coverage_options.file_mappings IS NOT NULL AND
48+
a_coverage_options.file_mappings.count > 0 THEN
49+
l_full_name := 'f.file_name';
50+
ELSE
51+
l_full_name := 'lower(s.owner||''.''||s.name)';
52+
END IF;
53+
l_result := '
54+
select full_name, owner, name, line, to_be_skipped, text
55+
from (
56+
select ' || l_full_name || q'[ as full_name,
57+
s.owner,
58+
s.name,
59+
s.line -
60+
coalesce(
61+
case when type!='TRIGGER' then 0 end,
62+
(select min(t.line) - 1
63+
from ]' || l_view_name || q'[ t
64+
where t.owner = s.owner and t.type = s.type and t.name = s.name
65+
and regexp_like( t.text, '[A-Za-z0-9$#_]*(begin|declare|compound).*','i'))
66+
) as line,
67+
s.text,
68+
'N' as to_be_skipped
69+
from ]' || l_view_name || q'[ s]';
70+
IF a_coverage_options.file_mappings IS NOT empty THEN
71+
l_result := l_result || '
72+
join table(:file_mappings) f
73+
on s.name = f.object_name
74+
and s.type = f.object_type
75+
and s.owner = f.object_owner
76+
where 1 = 1';
77+
ELSIF a_coverage_options.include_objects IS NOT empty THEN
78+
l_result := l_result || '
79+
where (s.owner, s.name) in (select il.owner, il.name from table(:include_objects) il)';
80+
ELSE
81+
l_result := l_result || '
82+
where s.owner in (select upper(t.column_value) from table(:l_schema_names) t)';
83+
END IF;
84+
l_result := l_result || q'[
85+
and s.type not in ('PACKAGE', 'TYPE', 'JAVA SOURCE')
86+
--Exclude calls to utPLSQL framework, Unit Test packages and objects from a_exclude_list parameter of coverage reporter
87+
and (s.owner, s.name) not in (select el.owner, el.name from table(:l_skipped_objects) el)
88+
)
89+
where line > 0]';
90+
RETURN l_result;
91+
END;
92+
93+
--' /*Not mess formatter*/
94+
95+
FUNCTION get_cov_sources_cursor(a_coverage_options ut_coverage_options)
96+
RETURN SYS_REFCURSOR IS
97+
l_cursor SYS_REFCURSOR;
98+
l_skip_objects ut_object_names;
99+
l_schema_names ut_varchar2_rows;
100+
l_sql VARCHAR2(32767);
101+
BEGIN
102+
l_schema_names := coalesce(a_coverage_options.schema_names,
103+
ut_varchar2_rows(sys_context('USERENV', 'CURRENT_SCHEMA')));
104+
IF NOT ut_coverage_helper.is_develop_mode() THEN
105+
--skip all the utplsql framework objects and all the unit test packages that could potentially be reported by coverage.
106+
l_skip_objects := ut_utils.get_utplsql_objects_list() MULTISET UNION ALL
107+
coalesce(a_coverage_options.exclude_objects, ut_object_names());
108+
END IF;
109+
l_sql := get_cov_sources_sql(a_coverage_options);
110+
IF a_coverage_options.file_mappings IS NOT empty THEN
111+
OPEN l_cursor FOR l_sql
112+
USING a_coverage_options.file_mappings, l_skip_objects;
113+
ELSIF a_coverage_options.include_objects IS NOT empty THEN
114+
OPEN l_cursor FOR l_sql
115+
USING a_coverage_options.include_objects, l_skip_objects;
116+
ELSE
117+
OPEN l_cursor FOR l_sql
118+
USING l_schema_names, l_skip_objects;
119+
END IF;
120+
RETURN l_cursor;
121+
END;
122+
123+
PROCEDURE populate_tmp_table(a_coverage_options ut_coverage_options) IS
124+
PRAGMA AUTONOMOUS_TRANSACTION;
125+
l_cov_sources_crsr SYS_REFCURSOR;
126+
l_cov_sources_data ut_coverage_helper.t_coverage_sources_tmp_rows;
127+
BEGIN
128+
129+
IF NOT ut_coverage_helper.is_tmp_table_populated() OR
130+
ut_coverage_helper.is_develop_mode() THEN
131+
ut_coverage_helper.cleanup_tmp_table();
132+
133+
l_cov_sources_crsr := get_cov_sources_cursor(a_coverage_options);
134+
135+
LOOP
136+
FETCH l_cov_sources_crsr BULK COLLECT
137+
INTO l_cov_sources_data LIMIT 1000;
138+
139+
ut_coverage_helper.insert_into_tmp_table(l_cov_sources_data);
140+
141+
EXIT WHEN l_cov_sources_crsr%NOTFOUND;
142+
END LOOP;
143+
144+
CLOSE l_cov_sources_crsr;
145+
END IF;
146+
COMMIT;
147+
END;
148+
149+
/**
150+
* Public functions
151+
*/
152+
PROCEDURE coverage_start IS
153+
BEGIN
154+
dbms_plsql_code_coverage.create_coverage_tables(force_it => TRUE);
155+
g_coverage_id := dbms_plsql_code_coverage.start_coverage(run_comment => 'utPLSQL Code coverage run ' ||
156+
ut_utils.to_string(systimestamp));
157+
END;
158+
159+
PROCEDURE coverage_stop IS
160+
BEGIN
161+
dbms_plsql_code_coverage.stop_coverage;
162+
END;
163+
164+
FUNCTION get_raw_coverage_data(a_object_owner VARCHAR2
165+
,a_object_name VARCHAR2) RETURN t_unit_line_calls IS
166+
TYPE coverage_row IS RECORD(
167+
line BINARY_INTEGER
168+
,blocks BINARY_INTEGER
169+
,covered_blocks BINARY_INTEGER);
170+
TYPE coverage_rows IS TABLE OF coverage_row;
171+
l_tmp_data coverage_rows;
172+
l_results t_unit_line_calls;
173+
174+
l_debug NUMBER;
175+
BEGIN
176+
SELECT ccb.line
177+
,COUNT(ccb.block) totalblocks
178+
,SUM(ccb.covered) AS coveredblocks BULK COLLECT
179+
INTO l_tmp_data
180+
FROM dbmspcc_units ccu
181+
LEFT OUTER JOIN dbmspcc_blocks ccb
182+
ON ccu.run_id = ccb.run_id
183+
AND ccu.object_id = ccb.object_id
184+
WHERE ccu.owner = a_object_owner
185+
AND ccu.name = a_object_name
186+
AND ccu.run_id = g_coverage_id
187+
GROUP BY ccb.line
188+
ORDER BY 1;
189+
190+
l_debug := l_tmp_data.count;
191+
192+
FOR i IN 1 .. l_tmp_data.count
193+
LOOP
194+
l_results(l_tmp_data(i).line).blocks := l_tmp_data(i).blocks;
195+
l_results(l_tmp_data(i).line).covered_blocks := l_tmp_data(i).covered_blocks;
196+
l_results(l_tmp_data(i).line).partcovered := CASE
197+
WHEN (l_tmp_data(i).blocks > 1) AND
198+
(l_tmp_data(i).blocks > l_tmp_data(i)
199+
.covered_blocks) THEN
200+
1
201+
ELSE
202+
0
203+
END;
204+
END LOOP;
205+
RETURN l_results;
206+
END;
207+
208+
PROCEDURE coverage_resume IS
209+
BEGIN
210+
NULL;
211+
END;
212+
213+
PROCEDURE coverage_pause IS
214+
BEGIN
215+
NULL;
216+
END;
217+
218+
FUNCTION get_coverage_data(a_coverage_options ut_coverage_options)
219+
RETURN ut_coverage.t_coverage IS
220+
l_line_calls t_unit_line_calls;
221+
l_result ut_coverage.t_coverage;
222+
l_new_unit ut_coverage.t_unit_coverage;
223+
line_no BINARY_INTEGER;
224+
l_source_objects_crsr ut_coverage_helper.t_tmp_table_objects_crsr;
225+
l_source_object ut_coverage_helper.t_tmp_table_object;
226+
BEGIN
227+
228+
--prepare global temp table with sources
229+
populate_tmp_table(a_coverage_options);
230+
231+
l_source_objects_crsr := ut_coverage_helper.get_tmp_table_objects_cursor();
232+
LOOP
233+
FETCH l_source_objects_crsr
234+
INTO l_source_object;
235+
EXIT WHEN l_source_objects_crsr%NOTFOUND;
236+
237+
--get coverage data
238+
l_line_calls := get_raw_coverage_data(l_source_object.owner, l_source_object.name);
239+
240+
--if there is coverage, we need to filter out the garbage (badly indicated data from dbms_profiler)
241+
IF l_line_calls.count > 0 THEN
242+
--remove lines that should not be indicted as meaningful
243+
FOR i IN 1 .. l_source_object.to_be_skipped_list.count
244+
LOOP
245+
IF l_source_object.to_be_skipped_list(i) IS NOT NULL THEN
246+
l_line_calls.delete(l_source_object.to_be_skipped_list(i));
247+
END IF;
248+
END LOOP;
249+
END IF;
250+
251+
--if there are no file mappings or object was actually captured by profiler
252+
IF a_coverage_options.file_mappings IS NULL OR l_line_calls.count > 0 THEN
253+
254+
--populate total stats
255+
l_result.total_lines := l_result.total_lines + l_source_object.lines_count;
256+
257+
--populate object level coverage stats
258+
IF NOT l_result.objects.exists(l_source_object.full_name) THEN
259+
l_result.objects(l_source_object.full_name) := l_new_unit;
260+
l_result.objects(l_source_object.full_name).owner := l_source_object.owner;
261+
l_result.objects(l_source_object.full_name).name := l_source_object.name;
262+
l_result.objects(l_source_object.full_name).total_lines := l_source_object.lines_count;
263+
END IF;
264+
--map to results
265+
line_no := l_line_calls.first;
266+
IF line_no IS NULL THEN
267+
l_result.uncovered_lines := l_result.uncovered_lines +
268+
l_source_object.lines_count;
269+
l_result.objects(l_source_object.full_name).uncovered_lines := l_source_object.lines_count;
270+
ELSE
271+
LOOP
272+
EXIT WHEN line_no IS NULL;
273+
274+
--total stats
275+
276+
--Get total blocks ,blocks covered, blocks not covered
277+
l_result.total_blocks := NVL(l_result.total_blocks, 0) + l_line_calls(line_no)
278+
.blocks;
279+
l_result.covered_blocks := NVL(l_result.covered_blocks, 0) + l_line_calls(line_no)
280+
.covered_blocks;
281+
l_result.uncovered_blocks := NVL(l_result.uncovered_blocks, 0) +
282+
(l_line_calls(line_no).blocks - l_line_calls(line_no)
283+
.covered_blocks);
284+
285+
--If line is not partially covered add as full line cover
286+
IF l_line_calls(line_no).partcovered = 1 THEN
287+
l_result.partcovered_lines := l_result.partcovered_lines + 1;
288+
ELSE
289+
l_result.covered_lines := l_result.covered_lines + 1;
290+
END IF;
291+
292+
-- Use nvl as be default is null
293+
--Increase total blocks
294+
l_result.objects(l_source_object.full_name).total_blocks := NVL(l_result.objects(l_source_object.full_name)
295+
.total_blocks,
296+
0) + l_line_calls(line_no)
297+
.blocks;
298+
299+
--Total uncovered blocks is a line blocks minus covered blocsk
300+
l_result.objects(l_source_object.full_name).uncovered_blocks := NVL(l_result.objects(l_source_object.full_name)
301+
.uncovered_blocks,
302+
0) +
303+
(l_line_calls(line_no)
304+
.blocks - l_line_calls(line_no)
305+
.covered_blocks);
306+
307+
--If we have any coverted blocks in line
308+
IF l_line_calls(line_no).covered_blocks > 0 THEN
309+
310+
l_result.executions := l_result.executions + 1;
311+
--object level stats
312+
313+
IF l_line_calls(line_no).partcovered = 1 THEN
314+
l_result.objects(l_source_object.full_name).partcovered_lines := l_result.objects(l_source_object.full_name)
315+
.partcovered_lines + 1;
316+
317+
ELSE
318+
l_result.objects(l_source_object.full_name).covered_lines := l_result.objects(l_source_object.full_name)
319+
.covered_lines + 1;
320+
321+
END IF;
322+
323+
l_result.objects(l_source_object.full_name).covered_blocks := NVL(l_result.objects(l_source_object.full_name)
324+
.covered_blocks,
325+
0) + l_line_calls(line_no)
326+
.covered_blocks;
327+
328+
l_result.objects(l_source_object.full_name).executions := NVL(l_result.objects(l_source_object.full_name)
329+
.executions,
330+
0) + 1;
331+
l_result.objects(l_source_object.full_name).lines(line_no).execution := 1;
332+
333+
ELSIF l_line_calls(line_no).covered_blocks = 0 THEN
334+
l_result.uncovered_lines := l_result.uncovered_lines + 1;
335+
l_result.objects(l_source_object.full_name).uncovered_lines := l_result.objects(l_source_object.full_name)
336+
.uncovered_lines + 1;
337+
l_result.objects(l_source_object.full_name).lines(line_no).execution := 0;
338+
339+
END IF;
340+
341+
l_result.objects(l_source_object.full_name).lines(line_no).partcove := l_line_calls(line_no)
342+
.partcovered;
343+
344+
line_no := l_line_calls.next(line_no);
345+
END LOOP;
346+
END IF;
347+
END IF;
348+
349+
END LOOP;
350+
351+
CLOSE l_source_objects_crsr;
352+
353+
RETURN l_result;
354+
END get_coverage_data;
355+
356+
END;
357+
/

0 commit comments

Comments
 (0)