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 std.exception : collectException; 16 import logger = std.experimental.logger; 17 18 import dextool.type; 19 20 import dextool.plugin.mutate.backend.database : Database, IterateMutantRow; 21 import dextool.plugin.mutate.backend.interface_ : FilesysIO; 22 import dextool.plugin.mutate.type : MutationKind, ReportKind, ReportLevel, ReportSection; 23 import dextool.plugin.mutate.backend.type : Mutation, Offset; 24 25 import dextool.plugin.mutate.backend.diff_parser : Diff; 26 import dextool.plugin.mutate.backend.report.type : SimpleWriter, ReportEvent, 27 FileReport, FilesReporter; 28 import dextool.plugin.mutate.backend.report.utility : MakeMutationTextResult, 29 window, windowSize, makeMutationText; 30 import dextool.plugin.mutate.config : ConfigReport; 31 32 ExitStatusType runReport(ref Database db, const MutationKind[] kind, 33 const ConfigReport conf, FilesysIO fio) @safe nothrow { 34 Diff diff; 35 try { 36 if (conf.unifiedDiff) 37 diff = fromStdin; 38 } catch (Exception e) { 39 logger.warning(e.msg).collectException; 40 } 41 42 try { 43 auto genrep = ReportGenerator.make(kind, conf, fio); 44 runAllMutantReporter(db, kind, genrep); 45 46 auto fp = makeFilesReporter(db, conf, kind, fio, diff); 47 if (fp !is null) 48 runFilesReporter(db, fp, kind); 49 } catch (Exception e) { 50 logger.error(e.msg).collectException; 51 return ExitStatusType.Errors; 52 } 53 54 return ExitStatusType.Ok; 55 } 56 57 @safe: 58 private: 59 60 void runAllMutantReporter(ref Database db, const(MutationKind)[] kind, ref ReportGenerator genrep) { 61 import dextool.plugin.mutate.backend.utility; 62 63 const auto kinds = dextool.plugin.mutate.backend.utility.toInternal(kind); 64 65 // TODO remove this parameter. seems to be unnecessary. 66 genrep.mutationKindEvent(kind is null ? [MutationKind.any] : kind); 67 68 genrep.locationStartEvent; 69 db.iterateMutants(kinds, &genrep.locationEvent); 70 genrep.locationEndEvent; 71 72 genrep.locationStatEvent; 73 74 genrep.statEvent(db); 75 } 76 77 void runFilesReporter(ref Database db, FilesReporter fps, const(MutationKind)[] kind) { 78 assert(fps !is null, "report should never be null"); 79 80 import dextool.plugin.mutate.backend.utility; 81 import dextool.plugin.mutate.backend.database : FileMutantRow; 82 83 const auto kinds = dextool.plugin.mutate.backend.utility.toInternal(kind); 84 85 fps.mutationKindEvent(kind); 86 87 foreach (f; db.getDetailedFiles) { 88 auto fp = fps.getFileReportEvent(db, f); 89 db.iterateFileMutants(kinds, f.file, &fp.fileMutantEvent); 90 fp.endFileEvent(db); 91 } 92 93 fps.postProcessEvent(db); 94 fps.endEvent(db); 95 } 96 97 FilesReporter makeFilesReporter(ref Database db, const ConfigReport conf, 98 const(MutationKind)[] kind, FilesysIO fio, ref Diff diff) { 99 import dextool.plugin.mutate.backend.report.html; 100 import dextool.plugin.mutate.backend.utility; 101 102 const auto kinds = dextool.plugin.mutate.backend.utility.toInternal(kind); 103 104 final switch (conf.reportKind) { 105 case ReportKind.plain: 106 case ReportKind.markdown: 107 case ReportKind.compiler: 108 case ReportKind.json: 109 case ReportKind.csv: 110 return null; 111 case ReportKind.html: 112 return new ReportHtml(kinds, conf, fio, diff); 113 } 114 } 115 116 /** 117 * Expects the event to come in the following order: 118 * - mutationKindEvent 119 * - locationStartEvent 120 * - locationEvent 121 * - locationEndEvent 122 * - statStartEvent 123 * - statEvent 124 * - statEndEvent 125 */ 126 struct ReportGenerator { 127 import std.algorithm : each; 128 import dextool.plugin.mutate.backend.report.compiler; 129 import dextool.plugin.mutate.backend.report.csv; 130 import dextool.plugin.mutate.backend.report.html; 131 import dextool.plugin.mutate.backend.report.json; 132 import dextool.plugin.mutate.backend.report.markdown; 133 import dextool.plugin.mutate.backend.report.plain; 134 135 ReportEvent[] listeners; 136 FilesReporter fileReporter; 137 138 static auto make(const MutationKind[] kind, const ConfigReport conf, FilesysIO fio) { 139 import dextool.plugin.mutate.backend.utility; 140 141 auto kinds = dextool.plugin.mutate.backend.utility.toInternal(kind); 142 ReportEvent[] listeners; 143 144 final switch (conf.reportKind) { 145 case ReportKind.plain: 146 listeners = [new ReportPlain(kinds, conf, fio)]; 147 break; 148 case ReportKind.markdown: 149 listeners = [new ReportMarkdown(kinds, conf, fio)]; 150 break; 151 case ReportKind.compiler: 152 listeners = [new ReportCompiler(kinds, conf.reportLevel, fio)]; 153 break; 154 case ReportKind.json: 155 listeners = [new ReportJson(kinds, conf, fio)]; 156 break; 157 case ReportKind.csv: 158 listeners = [new ReportCSV(kinds, conf.reportLevel, fio)]; 159 break; 160 case ReportKind.html: 161 listeners = null; 162 break; 163 } 164 165 return ReportGenerator(listeners); 166 } 167 168 void mutationKindEvent(const MutationKind[] kind_) { 169 listeners.each!(a => a.mutationKindEvent(kind_)); 170 } 171 172 void locationStartEvent() { 173 listeners.each!(a => a.locationStartEvent); 174 } 175 176 // trusted: trusting that d2sqlite3 and sqlite3 is memory safe. 177 void locationEvent(const ref IterateMutantRow r) @trusted { 178 listeners.each!(a => a.locationEvent(r)); 179 } 180 181 void locationEndEvent() { 182 listeners.each!(a => a.locationEndEvent); 183 } 184 185 void locationStatEvent() { 186 listeners.each!(a => a.locationStatEvent); 187 } 188 189 void statEvent(ref Database db) { 190 listeners.each!(a => a.statEvent(db)); 191 } 192 } 193 194 Diff fromStdin() @trusted { 195 import std.stdio : stdin; 196 import dextool.plugin.mutate.backend.diff_parser : UnifiedDiffParser; 197 198 UnifiedDiffParser parser; 199 foreach (l; stdin.byLine) { 200 debug logger.trace(l); 201 parser.process(l); 202 } 203 return parser.result; 204 }