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