@@ -16,21 +16,10 @@ create or replace package body ut_coverage is
1616 limitations under the License.
1717 */
1818
19+
1920 type t_source_lines is table of binary_integer;
2021
21- -- The source query has two important transformations done in it.
22- -- the flag: to_be_skipped ='Y' is set for a line of code that is badly reported by DBMS_PROFILER as executed 0 times.
23- -- This includes lines that are:
24- -- - PACKAGE, PROCEDURE, FUNCTION definition line,
25- -- - BEGIN, END of a block
26- -- Another transformation is adjustment of line number for TRIGGER body.
27- -- DBMS_PROFILER is reporting line numbers for triggers not as defined in DBA_SOURCE, its usign line numbers as defined in DBA_TRIGGERS
28- -- the DBA_TRIGGERS does not contain the trigger specification lines, only lines that define the trigger body.
29- -- the query adjusts the line numbers for triggers by finding first occurrence of begin|declare|compound in the trigger body line.
30- -- The subquery is optimized by:
31- -- - COALESCE function -> it will execute only for TRIGGERS
32- -- - scalar subquery cache -> it will only execute once for one trigger source code.
33- function get_cov_sources_sql(a_coverage_options ut_coverage_options) return varchar2 is
22+ function get_cov_sources_sql(a_coverage_options ut_coverage_options, a_skipped_lines varchar2 default 'Y') return varchar2 is
3423 l_result varchar2(32767);
3524 l_full_name varchar2(100);
3625 l_view_name varchar2(200) := ut_metadata.get_dba_view('dba_source');
@@ -54,8 +43,10 @@ create or replace package body ut_coverage is
5443 where t.owner = s.owner and t.type = s.type and t.name = s.name
5544 and regexp_like( t.text, '[A-Za-z0-9$#_]*(begin|declare|compound).*','i'))
5645 ) as line,
57- s.text,
58- case
46+ s.text, ]';
47+ if a_skipped_lines = 'Y' then
48+ l_result := l_result ||
49+ q'[case
5950 when
6051 -- to avoid execution of regexp_like on every line
6152 -- first do a rough check for existence of search pattern keyword
@@ -70,8 +61,13 @@ create or replace package body ut_coverage is
7061 '^([\t ]*(((not)?\s*(overriding|final|instantiable)[\t ]*)*(static|constructor|member)?[\t ]*(procedure|function)|package([\t ]+body)|begin|end([\t ]+\S+)*[ \t]*;))', 'i'
7162 )
7263 then 'Y'
73- end as to_be_skipped
74- from ]'||l_view_name||q'[ s]';
64+ end as to_be_skipped ]';
65+ else
66+ l_result := l_result || q'['N' as to_be_skipped ]';
67+ end if;
68+
69+ l_result := l_result ||' from '||l_view_name||q'[ s]';
70+
7571 if a_coverage_options.file_mappings is not empty then
7672 l_result := l_result || '
7773 join table(:file_mappings) f
@@ -95,7 +91,7 @@ create or replace package body ut_coverage is
9591 return l_result;
9692 end;
9793
98- function get_cov_sources_cursor(a_coverage_options ut_coverage_options) return sys_refcursor is
94+ function get_cov_sources_cursor(a_coverage_options in ut_coverage_options,a_sql in varchar2 ) return sys_refcursor is
9995 l_cursor sys_refcursor;
10096 l_skip_objects ut_object_names;
10197 l_sql varchar2(32767);
@@ -104,7 +100,7 @@ create or replace package body ut_coverage is
104100 --skip all the utplsql framework objects and all the unit test packages that could potentially be reported by coverage.
105101 l_skip_objects := ut_utils.get_utplsql_objects_list() multiset union all coalesce(a_coverage_options.exclude_objects, ut_object_names());
106102 end if;
107- l_sql := get_cov_sources_sql(a_coverage_options) ;
103+ l_sql := a_sql ;
108104 if a_coverage_options.file_mappings is not empty then
109105 open l_cursor for l_sql using a_coverage_options.file_mappings, l_skip_objects;
110106 elsif a_coverage_options.include_objects is not empty then
@@ -115,7 +111,7 @@ create or replace package body ut_coverage is
115111 return l_cursor;
116112 end;
117113
118- procedure populate_tmp_table(a_coverage_options ut_coverage_options) is
114+ procedure populate_tmp_table(a_coverage_options in ut_coverage_options,a_sql in varchar2 ) is
119115 pragma autonomous_transaction;
120116 l_cov_sources_crsr sys_refcursor;
121117 l_cov_sources_data ut_coverage_helper.t_coverage_sources_tmp_rows;
@@ -124,7 +120,7 @@ create or replace package body ut_coverage is
124120 if not ut_coverage_helper.is_tmp_table_populated() or ut_coverage_helper.is_develop_mode() then
125121 ut_coverage_helper.cleanup_tmp_table();
126122
127- l_cov_sources_crsr := get_cov_sources_cursor(a_coverage_options);
123+ l_cov_sources_crsr := get_cov_sources_cursor(a_coverage_options,a_sql );
128124
129125 loop
130126 fetch l_cov_sources_crsr bulk collect into l_cov_sources_data limit 1000;
@@ -143,14 +139,14 @@ create or replace package body ut_coverage is
143139 /**
144140 * Public functions
145141 */
146- procedure coverage_start is
142+ procedure coverage_start(a_coverage_options ut_coverage_options default null) is
147143 begin
148144 ut_coverage_helper.coverage_start('utPLSQL Code coverage run '||ut_utils.to_string(systimestamp));
149145 end;
150146
151- procedure coverage_start_develop is
147+ procedure coverage_start_develop(a_coverage_options ut_coverage_options default null) is
152148 begin
153- ut_coverage_helper.coverage_start_develop() ;
149+ ut_coverage_helper.coverage_start_develop;
154150 end;
155151
156152 procedure coverage_pause is
@@ -174,81 +170,54 @@ create or replace package body ut_coverage is
174170 end;
175171
176172 function get_coverage_data(a_coverage_options ut_coverage_options) return t_coverage is
177- l_line_calls ut_coverage_helper.t_unit_line_calls;
178- l_result t_coverage;
179- l_new_unit t_unit_coverage;
180- l_line_no binary_integer;
181- l_source_objects_crsr ut_coverage_helper.t_tmp_table_objects_crsr;
182- l_source_object ut_coverage_helper.t_tmp_table_object;
173+ l_result_block ut_coverage.t_coverage;
174+ l_result_profiler_enrich ut_coverage.t_coverage;
175+ l_object ut_coverage.t_full_name;
176+ l_line_no binary_integer;
183177 begin
184-
185- --prepare global temp table with sources
186- populate_tmp_table(a_coverage_options);
187-
188- l_source_objects_crsr := ut_coverage_helper.get_tmp_table_objects_cursor();
189- loop
190- fetch l_source_objects_crsr into l_source_object;
191- exit when l_source_objects_crsr%notfound;
192-
193- --get coverage data
194- l_line_calls := ut_coverage_helper.get_raw_coverage_data( l_source_object.owner, l_source_object.name );
195-
196- --if there is coverage, we need to filter out the garbage (badly indicated data from dbms_profiler)
197- if l_line_calls.count > 0 then
198- --remove lines that should not be indicted as meaningful
199- for i in 1 .. l_source_object.to_be_skipped_list.count loop
200- if l_source_object.to_be_skipped_list(i) is not null then
201- l_line_calls.delete(l_source_object.to_be_skipped_list(i));
202- end if;
203- end loop;
204- end if;
205-
206- --if there are no file mappings or object was actually captured by profiler
207- if a_coverage_options.file_mappings is null or l_line_calls.count > 0 then
208-
209- --populate total stats
210- l_result.total_lines := l_result.total_lines + l_source_object.lines_count;
211-
212- --populate object level coverage stats
213- if not l_result.objects.exists(l_source_object.full_name) then
214- l_result.objects(l_source_object.full_name) := l_new_unit;
215- l_result.objects(l_source_object.full_name).owner := l_source_object.owner;
216- l_result.objects(l_source_object.full_name).name := l_source_object.name;
217- l_result.objects(l_source_object.full_name).total_lines := l_source_object.lines_count;
178+ -- Get raw data for both reporters, order is important as tmp table will skip headers and dont populate
179+ -- tmp table for block again.
180+ l_result_profiler_enrich:= ut_coverage_profiler.get_coverage_data(a_coverage_options => a_coverage_options);
181+
182+ -- If block coverage available we will use it.
183+ $if dbms_db_version.version = 12 and dbms_db_version.release >= 2 or dbms_db_version.version > 12 $then
184+ l_result_block := ut_coverage_block.get_coverage_data(a_coverage_options => a_coverage_options);
185+
186+ -- Enrich profiler results with some of the block results
187+ l_object := l_result_profiler_enrich.objects.first;
188+ while (l_object is not null)
189+ loop
190+
191+ l_line_no := l_result_profiler_enrich.objects(l_object).lines.first;
192+
193+ -- to avoid no data found check if we got object in profiler
194+ if l_result_block.objects.exists(l_object) then
195+ while (l_line_no is not null)
196+ loop
197+ -- To avoid no data check for object line
198+ if l_result_block.objects(l_object).lines.exists(l_line_no) then
199+ -- enrich line level stats
200+ l_result_profiler_enrich.objects(l_object).lines(l_line_no).partcove := l_result_block.objects(l_object).lines(l_line_no).partcove;
201+ l_result_profiler_enrich.objects(l_object).lines(l_line_no).covered_blocks := l_result_block.objects(l_object).lines(l_line_no).covered_blocks;
202+ l_result_profiler_enrich.objects(l_object).lines(l_line_no).no_blocks := l_result_block.objects(l_object).lines(l_line_no).no_blocks;
203+ -- enrich object level stats
204+ l_result_profiler_enrich.objects(l_object).partcovered_lines := nvl(l_result_profiler_enrich.objects(l_object).partcovered_lines,0) + l_result_block.objects(l_object).lines(l_line_no).partcove;
218205 end if;
219- --map to results
220- l_line_no := l_line_calls.first;
221- if l_line_no is null then
222- l_result.uncovered_lines := l_result.uncovered_lines + l_source_object.lines_count;
223- l_result.objects(l_source_object.full_name).uncovered_lines := l_source_object.lines_count;
224- else
225- loop
226- exit when l_line_no is null;
227-
228- if l_line_calls(l_line_no) > 0 then
229- --total stats
230- l_result.covered_lines := l_result.covered_lines + 1;
231- l_result.executions := l_result.executions + l_line_calls(l_line_no);
232- --object level stats
233- l_result.objects(l_source_object.full_name).covered_lines := l_result.objects(l_source_object.full_name).covered_lines + 1;
234- l_result.objects(l_source_object.full_name).executions := l_result.objects(l_source_object.full_name).executions + l_line_calls(l_line_no);
235- elsif l_line_calls(l_line_no) = 0 then
236- l_result.uncovered_lines := l_result.uncovered_lines + 1;
237- l_result.objects(l_source_object.full_name).uncovered_lines := l_result.objects(l_source_object.full_name).uncovered_lines + 1;
238- end if;
239- l_result.objects(l_source_object.full_name).lines(l_line_no) := l_line_calls(l_line_no);
240-
241- l_line_no := l_line_calls.next(l_line_no);
242- end loop;
243- end if;
244- end if;
245-
246- end loop;
247-
248- close l_source_objects_crsr;
249-
250- return l_result;
251- end get_coverage_data;
252-
206+ --At the end go to next line
207+ l_line_no := l_result_profiler_enrich.objects(l_object).lines.next(l_line_no);
208+ end loop;
209+ --total level stats enrich
210+ l_result_profiler_enrich.partcovered_lines := nvl(l_result_profiler_enrich.partcovered_lines,0) + l_result_profiler_enrich.objects(l_object).partcovered_lines;
211+ -- At the end go to next object
212+ end if;
213+
214+ l_object := l_result_profiler_enrich.objects.next(l_object);
215+
216+ end loop;
217+ $end
218+
219+ return l_result_profiler_enrich;
220+ end get_coverage_data;
221+
253222end;
254223/
0 commit comments