1 /** 2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved. 3 License: MPL-2 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This Source Code Form is subject to the terms of the Mozilla Public License, 7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 8 one at http://mozilla.org/MPL/2.0/. 9 10 This module contains functionality for reporting the mutations. Both the 11 summary and details for each mutation. 12 */ 13 module dextool.plugin.mutate.backend.report; 14 15 import logger = std.experimental.logger; 16 import std.ascii : newline; 17 import std.exception : collectException; 18 19 import dextool.type; 20 21 import dextool.plugin.mutate.backend.database : Database, IterateMutantRow; 22 import dextool.plugin.mutate.backend.diff_parser : Diff, diffFromStdin; 23 import dextool.plugin.mutate.backend.generate_mutant : MakeMutationTextResult, makeMutationText; 24 import dextool.plugin.mutate.backend.interface_ : FilesysIO; 25 import dextool.plugin.mutate.backend.report.type : SimpleWriter, ReportEvent, 26 FileReport, FilesReporter; 27 import dextool.plugin.mutate.backend.report.utility : window, windowSize; 28 import dextool.plugin.mutate.backend.type : Mutation, Offset; 29 import dextool.plugin.mutate.backend.utility : getProfileResult, Profile; 30 import dextool.plugin.mutate.config : ConfigReport; 31 import dextool.plugin.mutate.type : MutationKind, ReportKind, ReportLevel, ReportSection; 32 33 ExitStatusType runReport(ref Database db, const MutationKind[] kind, 34 const ConfigReport conf, FilesysIO fio) @trusted nothrow { 35 Diff diff; 36 try { 37 if (conf.unifiedDiff) 38 diff = diffFromStdin; 39 } catch (Exception e) { 40 logger.warning(e.msg).collectException; 41 } 42 43 try { 44 auto genrep = ReportGenerator.make(db, kind, conf, fio); 45 runAllMutantReporter(db, kind, genrep); 46 47 auto fp = makeFilesReporter(db, conf, kind, fio, diff); 48 if (fp !is null) 49 runFilesReporter(db, fp, kind); 50 } catch (Exception e) { 51 debug logger.trace(e).collectException; 52 logger.error(e.msg).collectException; 53 return ExitStatusType.Errors; 54 } 55 56 if (conf.profile) 57 try { 58 import std.stdio : writeln; 59 60 writeln(getProfileResult.toString); 61 } catch (Exception e) { 62 logger.warning("Unable to print the profile data: ", e.msg).collectException; 63 } 64 65 return ExitStatusType.Ok; 66 } 67 68 @safe: 69 private: 70 71 void runAllMutantReporter(ref Database db, const(MutationKind)[] kind, ref ReportGenerator genrep) { 72 import dextool.plugin.mutate.backend.utility; 73 74 const auto kinds = dextool.plugin.mutate.backend.utility.toInternal(kind); 75 76 // TODO remove this parameter. seems to be unnecessary. 77 genrep.mutationKindEvent(kind); 78 79 genrep.locationStartEvent(db); 80 { 81 auto profile = Profile("iterate mutants for report"); 82 db.iterateMutants(kinds, &genrep.locationEvent); 83 } 84 85 auto profile = Profile("post process report"); 86 genrep.locationEndEvent; 87 genrep.locationStatEvent; 88 genrep.statEvent(db); 89 } 90 91 void runFilesReporter(ref Database db, FilesReporter fps, const(MutationKind)[] kind) { 92 assert(fps !is null, "report should never be null"); 93 94 import dextool.plugin.mutate.backend.utility; 95 import dextool.plugin.mutate.backend.database : FileMutantRow; 96 97 const auto kinds = dextool.plugin.mutate.backend.utility.toInternal(kind); 98 99 fps.mutationKindEvent(kind); 100 101 foreach (f; db.getDetailedFiles) { 102 auto profile = Profile("generate report for " ~ f.file); 103 auto fp = fps.getFileReportEvent(db, f); 104 db.iterateFileMutants(kinds, f.file, &fp.fileMutantEvent); 105 fp.endFileEvent(db); 106 } 107 108 auto profile = Profile("post process report"); 109 fps.postProcessEvent(db); 110 fps.endEvent(db); 111 } 112 113 FilesReporter makeFilesReporter(ref Database db, const ConfigReport conf, 114 const(MutationKind)[] kind, FilesysIO fio, ref Diff diff) { 115 import dextool.plugin.mutate.backend.report.html; 116 import dextool.plugin.mutate.backend.report.json; 117 import dextool.plugin.mutate.backend.utility; 118 119 const auto kinds = dextool.plugin.mutate.backend.utility.toInternal(kind); 120 121 final switch (conf.reportKind) { 122 case ReportKind.plain: 123 case ReportKind.markdown: 124 case ReportKind.compiler: 125 case ReportKind.csv: 126 return null; 127 case ReportKind.json: 128 return new ReportJson(kinds, conf, fio, diff); 129 case ReportKind.html: 130 return new ReportHtml(kinds, conf, fio, diff); 131 } 132 } 133 134 /** 135 * Expects the event to come in the following order: 136 * - mutationKindEvent 137 * - locationStartEvent 138 * - locationEvent 139 * - locationEndEvent 140 * - statStartEvent 141 * - statEvent 142 * - statEndEvent 143 */ 144 struct ReportGenerator { 145 import std.algorithm : each; 146 import dextool.plugin.mutate.backend.report.compiler; 147 import dextool.plugin.mutate.backend.report.csv; 148 import dextool.plugin.mutate.backend.report.html; 149 import dextool.plugin.mutate.backend.report.markdown; 150 import dextool.plugin.mutate.backend.report.plain; 151 152 Database db; 153 ReportEvent[] listeners; 154 FilesReporter fileReporter; 155 156 static auto make(ref Database db, const MutationKind[] kind, 157 const ConfigReport conf, FilesysIO fio) @system { 158 import dextool.plugin.mutate.backend.utility; 159 160 auto kinds = dextool.plugin.mutate.backend.utility.toInternal(kind); 161 ReportEvent[] listeners; 162 163 final switch (conf.reportKind) { 164 case ReportKind.plain: 165 listeners = [new ReportPlain(kinds, conf, fio)]; 166 break; 167 case ReportKind.markdown: 168 listeners = [new ReportMarkdown(kinds, conf, fio)]; 169 break; 170 case ReportKind.compiler: 171 listeners = [new ReportCompiler(kinds, conf.reportLevel, fio)]; 172 break; 173 case ReportKind.json: 174 listeners = null; 175 break; 176 case ReportKind.csv: 177 listeners = [new ReportCSV(kinds, conf.reportLevel, fio)]; 178 break; 179 case ReportKind.html: 180 listeners = null; 181 break; 182 } 183 184 return ReportGenerator(db, listeners); 185 } 186 187 void mutationKindEvent(const MutationKind[] kind_) { 188 listeners.each!(a => a.mutationKindEvent(kind_)); 189 } 190 191 void locationStartEvent(ref Database db) { 192 listeners.each!(a => a.locationStartEvent(db)); 193 } 194 195 // trusted: trusting that d2sqlite3 and sqlite3 is memory safe. 196 void locationEvent(const ref IterateMutantRow r) @trusted { 197 listeners.each!(a => a.locationEvent(db, r)); 198 } 199 200 void locationEndEvent() { 201 listeners.each!(a => a.locationEndEvent); 202 } 203 204 void locationStatEvent() { 205 listeners.each!(a => a.locationStatEvent); 206 } 207 208 void statEvent(ref Database db) { 209 listeners.each!(a => a.statEvent(db)); 210 } 211 }