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])). |