1 % (c) 2009-2024 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(coverage_tools,[
6 print_coverage_info/0,
7 print_coverage_info/1,
8 get_file_coverage_info/1, get_file_coverage_info/2,
9 gen_pltables_coverage_rows/4,
10 gen_pltables_descriptions_table/3,
11 export_tables/3,
12 export_tables_html/1,
13 export_tables_latex/1,
14 export_tables_html_stream/1,
15 export_tables_latex_stream/1,
16 colorize_source_files/2,
17 export_emma/1,
18 '$NOT_COVERED'/1,
19 uncovered/1
20 ]).
21
22 :- use_module(library(lists)).
23 :- use_module(library(between)).
24 :- use_module(library(sets),[intersection/3]).
25
26 :- use_module(probsrc(tools)).
27 :- use_module(pltablesrc(pltables), [
28 new_table/2, add_column/3,
29 pltable/2, add_row/3,
30 tables_to_html/2, tables_to_html_stream/2,
31 tables_to_latex/2, tables_to_latex_stream/2,
32 tables_to_xml/2
33 ]).
34 :- use_module(probsrc(self_check)).
35 :- use_module(probsrc(junit_tests)).
36
37
38 :- use_module(probsrc(module_information)).
39 :- module_info(group,coverage_analysis).
40 :- module_info(description,'This module automatically generates the SICStus Prolog coverage information reports.').
41
42 :- set_prolog_flag(double_quotes, codes).
43
44 :- volatile summary_row/9, module_row/10.
45 :- dynamic summary_row/9, module_row/10.
46
47 :- use_module(covsrc(coverage_tools_annotations)).
48 % defines: '$NOT_COVERED'(_X) :- true.
49
50 print_coverage_info :-
51 coverage_data(D),
52 merge_coverage_pred_data(D,MD),
53 print_uncovered(MD), nl,
54 merge_coverage_file_data(MD,FD),
55 print_file_stats(FD).
56
57 print_coverage_info(Module) :-
58 coverage_data(D),
59 merge_coverage_pred_data(D,MD),
60 print_uncovered(Module,MD), nl,
61 merge_coverage_file_data(MD,FD),
62 print_file_stats(Module,FD).
63
64 get_file_coverage_info(FDSorted) :-
65 coverage_data(Data),
66 get_file_coverage_info(Data,FDSorted).
67
68 get_file_coverage_info(Data,FDSorted) :-
69 merge_coverage_pred_data(Data,MD),
70 merge_coverage_file_data(MD,FD),
71 sort(FD, FDSorted).
72
73 merge_coverage_pred_data([],[]).
74 merge_coverage_pred_data([CD | Tail],Res) :-
75 create_fresh_pred_data(CD,NewData),
76 merge_coverage_pred_data(Tail,NewData,Res).
77
78 merge_coverage_pred_data([],LastData,[LastData]).
79 merge_coverage_pred_data([CD | Tail],LastData,Res) :-
80 (merge_pred_data(CD,LastData,NewData) -> Res=NR
81 ; Res=[LastData|NR], create_fresh_pred_data(CD,NewData)
82 ),merge_coverage_pred_data(Tail,NewData,NR).
83
84 % translate a list of sorted counter data into summary of data for every predicate
85 % pred_data(FileName,PredicateSpecification,NrOfClauses,NrOfCoveredClauses,WasTheLastClauseAlreadyCovered,ListOfCoveredLines,ListOfUncoveredLines)
86 merge_pred_data(counter(File,PredSpec,ClauseNo,LineNr)-Tagged,
87 pred_data(File,PredSpec,MaxClause,Covered,LastCov,OldLineNrCovered,OldLineNrUncovered,OldLineNrNonDet),
88 pred_data(File,PredSpec,NewMax,NewCovered,NewLastCov,NewLineNrCovered,NewLineNrUncovered,NewLineNrNonDet)) :-
89 (Tagged = nondet(_) -> NewLineNrNonDet = [LineNr|OldLineNrNonDet] ; NewLineNrNonDet = OldLineNrNonDet),
90 (uncovered(Tagged)
91 -> NewLineNrCovered = OldLineNrCovered, NewLineNrUncovered = [LineNr|OldLineNrUncovered]
92 ; NewLineNrCovered = [LineNr|OldLineNrCovered], NewLineNrUncovered = OldLineNrUncovered),
93 (ClauseNo>MaxClause
94 -> NewMax = ClauseNo,
95 (uncovered(Tagged)
96 -> NewCovered=Covered, NewLastCov=0
97 ; NewCovered is Covered+1, NewLastCov=1)
98 ; NewMax=MaxClause,
99 (ClauseNo<MaxClause -> print('*** WARNING: ClauseNr decreasing: '),nl,
100 print(pred_data(File,PredSpec,MaxClause,Covered)),nl,
101 print(new_clause_no(ClauseNo)), nl, %trace,
102 NewCovered=Covered, NewLastCov=LastCov
103 ; (uncovered(Tagged);LastCov=1) -> NewCovered=Covered, NewLastCov=LastCov
104 ; NewLastCov=1, NewCovered is Covered+1 % we cover a clause that was thus far uncovered
105 )
106 ).
107
108 create_fresh_pred_data(counter(File,PredSpec,ClauseNo,LineNr)-Tagged,
109 pred_data(File,PredSpec,ClauseNo,Covered,LastCov,CovLines,UnCovLines,NonDetLines)) :-
110 (Tagged = nondet(_) -> NonDetLines = [LineNr] ; NonDetLines = []),
111 (uncovered(Tagged) -> Covered=0,CovLines=[],UnCovLines=[LineNr] ; Covered=1,CovLines=[LineNr],UnCovLines=[]),
112 LastCov=Covered,
113 (ClauseNo<1 -> print('### Warning: clause number <1 : '), print(ClauseNo),nl,
114 print('### '),print_pred(File,PredSpec),nl,trace
115 ; true).
116
117 uncovered(det(0)).
118 uncovered(nondet(0)).
119
120 print_uncovered(T) :- print('UNCOVERED PREDICATES:'),nl,print_uncovered2(T).
121 print_uncovered2([]).
122 print_uncovered2([pred_data(File,PredSpec,_M,0,_,_,_,_)|T]) :- !,
123 print(' '),print_pred(File,PredSpec), nl, print_uncovered2(T).
124 print_uncovered2([_|T]) :- print_uncovered2(T).
125
126 print_uncovered(Module,T) :- print('UNCOVERED PREDICATES:'),nl,print_uncovered2(Module,T).
127 print_uncovered2(_Module,[]).
128 print_uncovered2(Module,[pred_data(File,PredSpec,_M,0,_,_,_,_)|T]) :- !,
129 (
130 PredSpec = Module:_/_
131 -> print(' '),print_pred(File,PredSpec), nl
132 ; true
133 ),
134 print_uncovered2(Module,T).
135 print_uncovered2(Module,[_|T]) :- print_uncovered2(Module,T).
136
137
138 print_pred(File,Pred) :- print(Pred), print(' in '), print(File).
139
140 print_file_stats(L) :- print('COVERAGE PER FILE:'),nl,
141 init_program_data(PD),print_file_stats2(L,PD).
142 print_file_stats2([],prog_data(NrFiles,Preds,CovPreds,Clauses,CovClauses)) :- % now print total summary:
143 PPerc is CovPreds*100/Preds, CPerc is CovClauses*100/Clauses,
144 format("TOTAL #FILES: ~w~n ~w/~w (~2f %) Predicates~n ~w/~w (~2f %) Clauses~n",
145 [NrFiles,CovPreds,Preds,PPerc,CovClauses,Clauses,CPerc]).
146 print_file_stats2([FileData|T],PD) :-
147 FileData = file_data(File,Preds,CovPreds,Clauses,CovClauses,_,_,_),
148 PPerc is CovPreds*100/Preds, CPerc is CovClauses*100/Clauses,
149 format("~w~n ~w/~w (~2f %) Predicates~n ~w/~w (~2f %) Clauses~n",
150 [File,CovPreds,Preds,PPerc,CovClauses,Clauses,CPerc]),
151 merge_program_data(FileData,PD,NPD),
152 print_file_stats2(T,NPD).
153
154 print_file_stats(_Module,[]).
155 print_file_stats(Module,[FileData|T]) :-
156 FileData = file_data(File,Preds,CovPreds,Clauses,CovClauses,_,_,_),
157 get_modulename_filename(File,Module)
158 -> PPerc is CovPreds*100/Preds, CPerc is CovClauses*100/Clauses,
159 format("~w~n ~w/~w (~2f %) Predicates~n ~w/~w (~2f %) Clauses~n",[File,CovPreds,Preds,PPerc,CovClauses,Clauses,CPerc])
160 ; print_file_stats(Module,T).
161
162
163 % translate a list of sorted predicate data into summary of data for every file
164 % file_data(FileName,NrOfPredicates,NrOfPredicatesCovered,NrOfClauses,NrOfClausesCovered,CoveredLines,UncoveredLines,NonDetLines)
165 merge_coverage_file_data([],[]).
166 merge_coverage_file_data([PD | Tail],Res) :-
167 create_fresh_file_data(PD,NewData),
168 merge_coverage_file_data(Tail,NewData,Res).
169
170 merge_coverage_file_data([],LastData,[LastData]).
171 merge_coverage_file_data([PD | Tail],LastData,Res) :-
172 (merge_file_data(PD,LastData,NewData) -> Res=NR
173 ; Res=[LastData|NR], create_fresh_file_data(PD,NewData)
174 ),merge_coverage_file_data(Tail,NewData,NR).
175
176 :-assert_must_succeed(coverage_tools:merge_file_data(pred_data(file,spec,1,2,_,[5],[5],[5]),file_data(file,5,2,6,3,[6],[6],[6]),file_data(file,6,3,7,5,[5,6],[5,6],[5,6]))).
177 :-assert_must_fail(coverage_tools:merge_file_data(pred_data(file,spec,1,2,_,[4],[4],[5]),file_data(file,5,2,6,3,[6],[7],[5]),file_data(file,6,3,7,5,[5,6],[5,6],[7]))).
178 :-assert_must_fail(coverage_tools:merge_file_data(pred_data(file,spec,1,2,_,_,_,_),file_data(another_file,5,2,6,3,_,_,_),file_data(file,6,3,7,5,_,_,_))).
179 merge_file_data(pred_data(File,_PredSpec,MaxClause,Covered,_,CoveredLines,UncoveredLines,NonDetLines),
180 file_data(File,Preds,PCov,Clauses,CCov,AlreadyCoveredLines,AlreadyUncoveredLines,AlreadyNonDetLines),
181 file_data(File,P1,PC1,C1,CC1,NowCoveredLines,NowUncoveredLines,NowNonDetLines)) :-
182 append(NonDetLines,AlreadyNonDetLines,NowNonDetLines),
183 append(CoveredLines,AlreadyCoveredLines,NowCoveredLines),
184 append(UncoveredLines,AlreadyUncoveredLines,NowUncoveredLines),
185 P1 is Preds+1,
186 (Covered>0 -> PC1 is PCov+1 ; PC1=PCov),
187 CC1 is CCov+Covered,
188 C1 is Clauses+MaxClause.
189
190 :- assert_must_succeed(coverage_tools:create_fresh_file_data(pred_data(file,spec,15,2,_,[5],[7],[7]),file_data(file,1,1,15,2,[5],[7],[7]))).
191 :- assert_must_fail(coverage_tools:create_fresh_file_data(pred_data(file,spec,15,2,_,[5],[6],[6]),file_data(file,1,1,15,2,[7],[8],[6]))).
192 :- assert_must_fail(coverage_tools:create_fresh_file_data(pred_data(file,spec,15,0,_,[5],[6],[6]),file_data(file,1,1,15,2,[5],[6],[6]))).
193 create_fresh_file_data(pred_data(File,_PredSpec,MaxClause,Covered,_,CoveredLines,UncoveredLines,NonDetLines),
194 file_data(File,1,PCov,MaxClause,Covered,CoveredLines,UncoveredLines,NonDetLines)) :-
195 (Covered>0 -> PCov=1 ; PCov=0).
196
197 init_program_data(prog_data(0,0,0,0,0)).
198 merge_program_data(file_data(_,P,CP,C,CC,_,_,_),prog_data(Files1,P1,CP1,C1,CC1),
199 prog_data(Files2,P2,CP2,C2,CC2)) :-
200 Files2 is Files1+1,
201 P2 is P1+P, CP2 is CP1+CP,
202 C2 is C1+C, CC2 is CC1+CC.
203
204 :- assert_must_succeed(coverage_tools:coverage_attributes(50, 50,[total=[53,48],covered=[53,48],covered_percentage=[49,48,48,46,48]])).
205 :- assert_must_fail(coverage_tools:coverage_attributes(50,2,[total=[53,48],covered=[52],covered_percentage=[52,46,48]])).
206 :- assert_must_succeed(coverage_tools:coverage_attributes(100,100,[total=[49,48,48],covered=[49,48,48],covered_percentage=[49,48,48,46,48]])).
207 coverage_attributes(Total,Covered,Res) :-
208 Perc is Covered*100/Total,
209 number_codes(Total,TC), number_codes(Covered,CC), number_codes(Perc,PC),
210 Res = ['='(total,TC), '='(covered,CC), '='(covered_percentage,PC)].
211
212 gen_pltables_descriptions_table(TableName, TableAttributes, ImportantGroups) :-
213 new_table(TableName, TableAttributes),
214 add_column(TableName, 'name', []),
215 add_column(TableName, 'description', []), !,
216 findall(Module, (get_module_info(Module,group,MGroup),member(MGroup,ImportantGroups)), ListImportant),
217 gen_pltables_descriptions_rows(TableName, ListImportant),
218 findall(Module, (get_module_info(Module,group,MGroup),\+(member(MGroup,ImportantGroups))), ListNotImportant),
219 gen_pltables_descriptions_rows(TableName, ListNotImportant).
220
221 gen_pltables_descriptions_rows(_, []).
222 gen_pltables_descriptions_rows(TableName, [Module | T]) :-
223 (get_module_info(Module, description, Desc)
224 -> add_row(TableName, [Module, Desc], [])
225 ; true),
226 gen_pltables_descriptions_rows(TableName,T).
227
228 gen_pltables_coverage_table(TableName, TableAttributes, FileData) :-
229 new_table(TableName, TableAttributes),
230 add_column(TableName, 'module', [align(left)]),
231 add_column(TableName, 'clauses', [align(right)]),
232 add_column(TableName, 'covered', [align(right)]),
233 add_column(TableName, 'uncoverable', [align(right)]),
234 add_column(TableName, '%', [align(right)]),
235 add_column(TableName, 'preds', [align(right)]),
236 add_column(TableName, 'covered', [align(right)]),
237 add_column(TableName, 'uncoverable', [align(right)]),
238 add_column(TableName, '%', [align(right)]),
239 summary_row(TableName, FileData, [0,0,0,0,0,0]).
240
241 summary_row(ModuleGroup, [], [Preds,CovPreds,IntPreds,Clauses,CovClauses,IntClauses]) :-
242 PercentageClauses1 is CovClauses*100/(Clauses-IntClauses),
243 RoundedClauses is round(PercentageClauses1*10),
244 PercentageClauses is RoundedClauses/10,
245 (PercentageClauses > 100 -> junit_coverage_report_error(coverage_tables,'Covered more than 100% of all Clauses') ; true),
246 PercentagePreds1 is CovPreds*100/(Preds-IntPreds),
247 RoundedPreds is round(PercentagePreds1*10),
248 PercentagePreds is RoundedPreds/10,
249 (PercentagePreds > 100 -> junit_coverage_report_error(coverage_tables,'Covered more than 100% of all Clauses') ; true),
250 add_row(ModuleGroup, [summary,Clauses,CovClauses,IntClauses,PercentageClauses,Preds,CovPreds,IntPreds,PercentagePreds], [bold]),
251 assertz(summary_row(ModuleGroup,Clauses,CovClauses,IntClauses,PercentageClauses,Preds,CovPreds,IntPreds,PercentagePreds)).
252 summary_row(ModuleGroup, [file_data(File,Preds,CovPreds,Clauses,CovClauses,CoveredLines,_,_)|T],[CPreds,CCovPreds,CFailPreds,CClauses,CCovClauses,CFailClauses]) :-
253 allowed_file(File), !,
254 get_modulename_filename(File, Module),
255 (get_module_info(Module, group, ModuleGroupFile) ; ModuleGroupFile = other),
256 ( ModuleGroup = ModuleGroupFile ->
257 ( NCPreds is CPreds + Preds,
258 NCCovPreds is CCovPreds + CovPreds,
259 NCClauses is CClauses + Clauses,
260 NCCovClauses is CCovClauses + CovClauses,
261 uncoverable_preds(File,IntPreds),
262 uncoverable_clauses(File,IntClauses,CoveredLines),
263 NCFailPreds is IntPreds + CFailPreds,
264 NCFailClauses is IntClauses + CFailClauses,
265 summary_row(ModuleGroup,T,[NCPreds,NCCovPreds,NCFailPreds,NCClauses,NCCovClauses,NCFailClauses])
266 )
267 ; summary_row(ModuleGroup,T,[CPreds,CCovPreds,CFailPreds,CClauses,CCovClauses,CFailClauses])
268 ).
269 % file was not allowed / wrong ending
270 summary_row(ModuleGroup, [_H|T],[CPreds,CCovPreds,CFailPreds,CClauses,CCovClauses,CFailClauses]) :-
271 summary_row(ModuleGroup,T,[CPreds,CCovPreds,CFailPreds,CClauses,CCovClauses,CFailClauses]).
272
273 gen_pltables_coverage_rows([], _, _, _).
274 gen_pltables_coverage_rows([file_data(File,Preds,CovPreds,Clauses,CovClauses,CoveredLines,_,_)|T],
275 TableAttributes, ImportantGroups, ColorizedURL) :-
276 allowed_file(File), !,
277 get_tail_filename(File,TF),
278 get_modulename_filename(File, Module),
279 uncoverable_preds(File,IntPreds),
280 uncoverable_clauses(File,IntClauses,CoveredLines),
281 (Clauses-IntClauses =:= 0
282 -> PercentageClauses1 = 0
283 ; PercentageClauses1 is CovClauses*100/(Clauses-IntClauses)),
284 RoundedClauses is round(PercentageClauses1*10),
285 PercentageClauses is RoundedClauses/10,
286 (PercentageClauses > 100 -> junit_coverage_report_error(coverage_tables,'Covered more than 100% of all Clauses') ; true),
287 (Preds - IntPreds =:= 0
288 -> PercentagePreds1 = 0
289 ; PercentagePreds1 is CovPreds*100/(Preds-IntPreds)),
290 RoundedPreds is round(PercentagePreds1*10),
291 PercentagePreds is RoundedPreds/10,
292 (PercentagePreds > 100 -> junit_coverage_report_error(coverage_tables,'Covered more than 100% of all Clauses') ; true),
293 (get_module_info(Module, group, ModuleGroup) ; ModuleGroup = other),
294 (
295 PercentageClauses < 10 ->
296 RowAttrs1 = [color('FF0000')] ;
297 RowAttrs1 = []
298 ),
299 (
300 PercentagePreds < 10 ->
301 RowAttrs2 = [color('FF0000')|RowAttrs1] ;
302 RowAttrs2 = RowAttrs1
303 ),
304 (pltable(ModuleGroup,_) -> true
305 ;
306 ((
307 member(ModuleGroup, ImportantGroups) ->
308 ToAppend = [description(ModuleGroup, [bold])] ;
309 ToAppend = [description(ModuleGroup, [])]
310 ),
311 append(ToAppend,TableAttributes, TableAttributes2),
312 gen_pltables_coverage_table(ModuleGroup, TableAttributes2, [file_data(File,Preds,CovPreds,Clauses,CovClauses,_,_,_)|T])
313 )),
314 ajoin([ColorizedURL,Module,'.html'],URL),
315 append([url(URL)],RowAttrs2,RowAttrs3),
316 add_row(ModuleGroup, [TF,Clauses,CovClauses,IntClauses,PercentageClauses,Preds,CovPreds,IntPreds,PercentagePreds],RowAttrs3),
317 assertz(module_row(ModuleGroup,TF,Clauses,CovClauses,IntClauses,PercentageClauses,Preds,CovPreds,IntPreds,PercentagePreds)),
318 gen_pltables_coverage_rows(T, TableAttributes, ImportantGroups,ColorizedURL).
319 % file was not allowed / wrong ending
320 gen_pltables_coverage_rows([_H|T], TableAttributes, ImportantGroups, ColorizedURL) :-
321 gen_pltables_coverage_rows(T, TableAttributes, ImportantGroups,ColorizedURL).
322
323 :- assert_must_succeed(coverage_tools:allowed_file('/a/b/c/d.pl')).
324 :- assert_must_succeed(coverage_tools:allowed_file('d.pl')).
325 :- assert_must_fail(coverage_tools:allowed_file('/a/b/c/d.eventb')).
326 :- assert_must_fail(coverage_tools:allowed_file('/a/b/c/d.P')).
327 :- assert_must_fail(coverage_tools:allowed_file('/a/b/c/d.P')).
328 :- assert_must_fail(coverage_tools:allowed_file('d.eventb')).
329 :- assert_must_fail(coverage_tools:allowed_file('d.P')).
330 :- assert_must_fail(coverage_tools:allowed_file('/a/b/c/d.pml.pl')).
331 :- assert_must_fail(coverage_tools:allowed_file('TestingUnicodeFeatures_unicode_saved.csp.pl')).
332 allowed_file(File) :-
333 get_tail_filename(File,TF),
334 atom_codes(TF,TFCodes),
335 ? suffix(TFCodes,".pl"),
336 ? \+ suffix(TFCodes, ".pml.pl"),
337 ? \+ suffix(TFCodes, ".csp.pl").
338
339 export_tables(FilenameHTML,FilenameTex,FilenameXML) :-
340 findall(TableName, pltable(TableName, _), UnsortedListOfTables),
341 sort(UnsortedListOfTables,ListOfTables),
342 tables_to_html(ListOfTables, FilenameHTML),
343 tables_to_latex(ListOfTables, FilenameTex),
344 tables_to_xml(ListOfTables, FilenameXML).
345
346 export_tables_html(FilenameHTML) :-
347 findall(TableName, pltables:pltable(TableName, _), UnsortedListOfTables),
348 sort(UnsortedListOfTables,ListOfTables),
349 tables_to_html(ListOfTables, FilenameHTML).
350
351 export_tables_latex(FilenameTex) :-
352 findall(TableName, pltables:pltable(TableName, _), UnsortedListOfTables),
353 sort(UnsortedListOfTables,ListOfTables),
354 tables_to_latex(ListOfTables, FilenameTex).
355
356 export_tables_html_stream(Stream) :-
357 findall(TableName, pltables:pltable(TableName, _), UnsortedListOfTables),
358 sort(UnsortedListOfTables,ListOfTables),
359 tables_to_html_stream(ListOfTables, Stream).
360
361 export_tables_latex_stream(Stream) :-
362 findall(TableName, pltables:pltable(TableName, _), UnsortedListOfTables),
363 sort(UnsortedListOfTables,ListOfTables),
364 tables_to_latex_stream(ListOfTables, Stream).
365
366 colorize_source_files([],_ColorizedPath).
367 colorize_source_files([file_data(File,Preds,_CovPreds,Clauses,_CovClauses,CoveredLines,UncoveredLines,NonDetLines)|T],ColorizedPath) :-
368 length(CoveredLines,CovL), length(UncoveredLines,UncovL), Tot is CovL+UncovL,
369 format('Colorizing file ~w (with ~w predicates and ~w clauses) and ~w/~w lines covered~n',[File,Preds,Clauses,CovL,Tot]),
370 colorize_source_file(File,CoveredLines,UncoveredLines,NonDetLines,ColorizedPath),
371 colorize_source_files(T,ColorizedPath).
372
373 colorize_source_file(FileIn,CoveredLines,UncoveredLines,NonDetLines,ColorizedPath) :-
374 colored_html_path(ColorizedPath,FileIn,FileOut),
375 open(FileIn,read,StreamIn),
376 open(FileOut,write,StreamOut),
377 write(StreamOut, '<html>\n<head>\n'),
378 write(StreamOut, ' <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"/>\n'),
379 write(StreamOut, ' <style type="text/css">\n'),
380 write(StreamOut, ' .uncovered {color:#D02020}\n'),
381 write(StreamOut, ' .covered {color:#109010}\n'),
382 write(StreamOut, ' .mixed {color:#FFA500}\n'),
383 write(StreamOut, ' .unexecutable {color:#000000}\n'),
384 write(StreamOut, ' .uncoverable {color:#0000FF}\n'),
385 write(StreamOut, ' .red {color:#FF0000}\n'),
386 write(StreamOut, ' </style>\n</head>\n<body>\n'),
387 write(StreamOut, '<table style="white-space:pre;font-family:monospace;" cellspacing="0" cellpadding="0">\n'),
388 colorize_source_lines(StreamIn,StreamOut,FileIn,CoveredLines,UncoveredLines,NonDetLines,1),
389 write(StreamOut, '</table>\n</body>\n</html>\n'),
390 close(StreamIn), close(StreamOut).
391
392 colorize_source_lines(StreamIn,StreamOut,FileIn,CoveredLines,UncoveredLines,NonDetLines,CurrentLine) :-
393 read_line(StreamIn, LineIn),
394 (
395 LineIn = end_of_file -> true
396 ; output_source_line(StreamOut,FileIn,LineIn,CoveredLines,UncoveredLines,NonDetLines,CurrentLine),
397 CL2 is CurrentLine + 1,
398 colorize_source_lines(StreamIn,StreamOut,FileIn,CoveredLines,UncoveredLines,NonDetLines,CL2)
399 ).
400
401 output_source_line(StreamOut,FileIn,Line,CoveredLines,UncoveredLines,NonDetLines,CurrentLine) :-
402 source_line_class(FileIn,CurrentLine,CoveredLines,UncoveredLines,Class),
403 line_det(CurrentLine,NonDetLines,LineDet,LineDetClass),
404 escape_line(Line,EscapedLine),
405 format(StreamOut,'<tr><td>~w </td><td class="~w">~w</td><td class="~w">~s</td></tr>\n',[CurrentLine,LineDetClass,LineDet,Class,EscapedLine]).
406
407 line_det(CurrentLine,NonDetLines,LineDet,red) :-
408 member(CurrentLine,NonDetLines) -> LineDet = '?' ; LineDet = ''.
409
410 :- assert_must_succeed(coverage_tools:source_line_class('Filename',15,[1,2,3,15],[4,5,6],covered)).
411 :- assert_must_succeed(coverage_tools:source_line_class('Filename',15,[1,2,3],[4,5,6,15],uncovered)).
412 :- assert_must_succeed(coverage_tools:source_line_class('Filename',15,[1,2,3,15],[4,5,6,15],mixed)).
413 :- assert_must_fail((coverage_tools:source_line_class('Filename',15,[1,2,3,15],[4,5,6,15],X),X=covered)).
414 :- assert_must_succeed(coverage_tools:source_line_class('Filename',15,[1,2,3,15],[4,5,6,15],uncovered)).
415 source_line_class(_FileIn,CurrentLine,CoveredLines,UncoveredLines,mixed) :-
416 memberchk(CurrentLine,CoveredLines),
417 memberchk(CurrentLine,UncoveredLines), !.
418 source_line_class(_FileIn,CurrentLine,CoveredLines,_UncoveredLines,covered) :-
419 memberchk(CurrentLine,CoveredLines),!.
420 source_line_class(FileIn,CurrentLine,_Cov,_Uncov,uncoverable) :-
421 (user:clause_counter_collected(FileIn,_,_,CurrentLine,_,_);user:call_counter_collected(FileIn,_,_,CurrentLine)),!.
422 source_line_class(_FileIn,CurrentLine,_CoveredLines,UncoveredLines,uncovered) :-
423 memberchk(CurrentLine,UncoveredLines),!.
424 source_line_class(_FileIn,_CurrentLine,_CoveredLines,_UncoveredLines,unexecutable).
425
426 :- assert_must_succeed(coverage_tools:escape_line("Test mit < Zeichen", "Test mit < Zeichen")).
427 :- assert_must_succeed(coverage_tools:escape_line("Test mit > Zeichen", "Test mit > Zeichen")).
428 escape_line([],[]).
429 % less than symbol
430 escape_line([60|T],[38,108,116,59|T2]) :-
431 escape_line(T,T2).
432 % greater than symbol
433 escape_line([62|T],[38,103,116,59|T2]) :-
434 escape_line(T,T2).
435 escape_line([X|T],[X|T2]) :-
436 ? escape_line(T,T2).
437
438 :- assert_must_succeed(coverage_tools:colored_html_path('/path/','test.pl','/path/test.html')).
439 colored_html_path(ColorizedPath,FileIn,FileOut) :-
440 get_modulename_filename(FileIn,ModuleName),
441 ajoin([ColorizedPath, ModuleName, '.html'], FileOut).
442
443 % a predicate is only counted as failing if all definitions end in a failing predicate
444 uncoverable_preds(File,Count) :-
445 findall(clause(Module:Name/Arity,Counter), user:clause_counter_collected(File,Module:Name/Arity,Counter,_LayoutHead,_FirstLine,_LastLine),ListTemp),
446 remove_dups(ListTemp,List),
447 count_uncoverable_preds(List,Count).
448
449 count_uncoverable_preds([],0).
450 count_uncoverable_preds([clause(Module:Name/Arity,_Counter)|T],Count) :-
451 delete_all(T,Module:Name/Arity,Res),
452 count_uncoverable_preds(Res,Count2),
453 length(T,LengthBeforeDelete), length(Res,LengthAfterDelete),
454 Occurences is LengthBeforeDelete - LengthAfterDelete + 1,
455 user:definitionnr(Module,Name/Arity,NoOfDefinitions),
456 (Occurences = NoOfDefinitions -> Count is Count2 + 1 ; Count = Count2).
457
458 delete_all([],_PredSpec,[]).
459 delete_all([clause(PredSpec,_)|T], PredSpec, NT) :-
460 !, delete_all(T,PredSpec,NT).
461 delete_all([clause(PredSpec2,Counter)|T], PredSpec, [clause(PredSpec2,Counter)|NT]) :-
462 PredSpec2 \= PredSpec,
463 delete_all(T,PredSpec,NT).
464
465 % a clause is counted as uncoverable, if at least one of the definitions ends in a failing predicate
466 % this might need to be changed for clauses that might or might not fail
467 uncoverable_clauses(File,Count,CoveredLines) :-
468 findall(clause(Module:Name/Arity,Counter),
469 (user:clause_counter_collected(File,Module:Name/Arity,Counter,_LayoutHead,FirstLine,LastLine),
470 numlist(FirstLine,LastLine,AllLines),
471 intersection(AllLines,CoveredLines,[])),
472 ListTemp),
473 remove_dups(ListTemp,List),
474 length(List,Count).
475
476 sum_all(List,[A,B,C,D,E,F]) :- sum_all(List,[0,0,0,0,0,0],[A,B,C,D,E,F]).
477 sum_all([],[A,B,C,D,E,F],[A,B,C,D,E,F]).
478 sum_all([summary_row(_ModuleGroup,Clauses,CovClauses,IntClauses,_PercentageClauses,Preds,CovPreds,IntPreds,_PercentagePreds)|T],
479 [ClausesIn,CovClausesIn,IntClausesIn,PredsIn,CovPredsIn,IntPredsIn],[A,B,C,D,E,F]) :-
480 NClauses is Clauses + ClausesIn,
481 NCovClauses is CovClauses + CovClausesIn,
482 NIntClauses is IntClauses + IntClausesIn,
483 NPreds is Preds + PredsIn,
484 NCovPreds is CovPreds + CovPredsIn,
485 NIntPreds is IntPreds + IntPredsIn,
486 sum_all(T,[NClauses,NCovClauses,NIntClauses,NPreds,NCovPreds,NIntPreds],[A,B,C,D,E,F]).
487
488 emma_header(Stream) :-
489 findall(summary_row(A,B,C,D,E,F,G,H,I), summary_row(A,B,C,D,E,F,G,H,I), List),
490 sum_all(List,[Clauses,CovClauses,IntClauses,Preds,CovPreds,IntPreds]),
491 module_information:number_of_groups(Groups),
492 module_information:number_of_modules(Modules),
493 TCovClauses is CovClauses + IntClauses,
494 PClauses is TCovClauses/Clauses*100,
495 (PClauses > 100 -> junit_coverage_report_error(emma,'Covered more than 100% of all Clauses') ; true),
496 TCovPreds is CovPreds + IntPreds,
497 PPreds is TCovPreds/Preds*100,
498 (PPreds > 100 -> junit_coverage_report_error(emma,'Covered more than 100% of all Predicates') ; true),
499 format(Stream, ' <stats>~n', []),
500 format(Stream, ' <packages value="~w"/>~n', [Groups]),
501 format(Stream, ' <clases value="~w"/>~n', [Modules]),
502 format(Stream, ' <methods value="~w"/>~n', [Clauses]),
503 format(Stream, ' <srcfiles value="~w"/>~n', [Modules]),
504 format(Stream, ' <srclines value="0"/>~n', []), % TODO: Lines?
505 format(Stream, ' </stats>~n', []),
506 format(Stream, ' <data>~n', []),
507 format(Stream, ' <all name="all classes">~n', []),
508 format(Stream, ' <coverage type="class, %" value="100% (~w/~w)"/>~n', [Modules,Modules]),
509 format(Stream, ' <coverage type="method, %" value="~w% (~w/~w)"/>~n', [PClauses,TCovClauses,Clauses]),
510 format(Stream, ' <coverage type="block, %" value="~w% (~w/~w)"/>~n', [PPreds,TCovPreds,Preds]),
511 format(Stream, ' <coverage type="line, %" value="0% (0/0)"/>~n~n', []). % TODO?
512
513 emma_data(Stream) :-
514 summary_row(ModuleGroup,Clauses,CovClauses,IntClauses,_PercentageClauses,Preds,CovPreds,IntPreds,_PercentagePreds),
515 TCovClauses is CovClauses + IntClauses,
516 PClauses is TCovClauses/Clauses*100,
517 (PClauses > 100 -> junit_coverage_report_error(emma,'Covered more than 100% of all Clauses') ; true),
518 TCovPreds is CovPreds + IntPreds,
519 PPreds is TCovPreds/Preds*100,
520 (PPreds > 100 -> junit_coverage_report_error(emma,'Covered more than 100% of all Predicates') ; true),
521 module_information:number_of_modules(ModuleGroup,Modules),
522 format(Stream, ' <package name="~w">~n', [ModuleGroup]),
523 format(Stream, ' <coverage type="class, %" value="100% (~w/~w)"/>~n', [Modules,Modules]),
524 format(Stream, ' <coverage type="method, %" value="~w% (~w/~w)"/>~n', [PClauses,TCovClauses,Clauses]),
525 format(Stream, ' <coverage type="block, %" value="~w% (~w/~w)"/>~n', [PPreds,TCovPreds,Preds]),
526 format(Stream, ' <coverage type="line, %" value="0% (0/0)"/>~n~n', []), % TODO?
527 emma_data(Stream,ModuleGroup),
528 format(Stream, ' </package>~n~n', []),
529 fail.
530 emma_data(_S).
531
532 emma_data(Stream,ModuleGroup) :-
533 module_row(ModuleGroup,TF,Clauses,CovClauses,IntClauses,_PercentageClauses,Preds,CovPreds,IntPreds,_PercentagePreds),
534 TCovClauses is CovClauses + IntClauses,
535 PClauses is TCovClauses/Clauses*100,
536 (PClauses > 100 -> junit_coverage_report_error(emma,'Covered more than 100% of all Clauses') ; true),
537 TCovPreds is CovPreds + IntPreds,
538 PPreds is TCovPreds/Preds*100,
539 (PPreds > 100 -> junit_coverage_report_error(emma,'Covered more than 100% of all Predicates') ; true),
540 format(Stream, ' <srcfile name="~w">~n', [TF]),
541 format(Stream, ' <coverage type="class, %" value="100% (1/1)"/>~n', []),
542 format(Stream, ' <coverage type="method, %" value="~w% (~w/~w)"/>~n', [PClauses,TCovClauses,Clauses]),
543 format(Stream, ' <coverage type="block, %" value="~w% (~w/~w)"/>~n', [PPreds,TCovPreds,Preds]),
544 format(Stream, ' <coverage type="line, %" value="0% (0/0)"/>~n~n', []), % TODO?
545 format(Stream, ' <class name="~w">~n', [TF]),
546 format(Stream, ' <coverage type="class, %" value="100% (1/1)"/>~n', []),
547 format(Stream, ' <coverage type="method, %" value="~w% (~w/~w)"/>~n', [PClauses,TCovClauses,Clauses]),
548 format(Stream, ' <coverage type="block, %" value="~w% (~w/~w)"/>~n', [PPreds,TCovPreds,Preds]),
549 format(Stream, ' <coverage type="line, %" value="0% (0/0)"/>~n~n', []), % TODO?
550 format(Stream, ' </class>~n', []),
551 format(Stream, ' </srcfile>~n', []),
552 fail.
553 emma_data(_S,_M).
554
555 export_emma(Filename) :-
556 open(Filename,write,Stream),
557 write(Stream,'<?xml version="1.0" encoding="UTF-8"?>\n'),
558 write(Stream,'<report>\n'),
559 emma_header(Stream),
560 emma_data(Stream),
561 write(Stream, ' </all>\n'),
562 write(Stream, ' </data>\n'),
563 write(Stream, '</report>\n'),
564 close(Stream).
565
566 junit_coverage_report_error(Id,ErrorText) :-
567 create_and_print_junit_result(['Coverage Reports'],Id,0,error([ErrorText])).