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