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