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