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