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) @safe 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(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(db); 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.report.json; 101 import dextool.plugin.mutate.backend.utility; 102 103 const auto kinds = dextool.plugin.mutate.backend.utility.toInternal(kind); 104 105 final switch (conf.reportKind) { 106 case ReportKind.plain: 107 case ReportKind.markdown: 108 case ReportKind.compiler: 109 case ReportKind.csv: 110 return null; 111 case ReportKind.json: 112 return new ReportJson(kinds, conf, fio, diff); 113 case ReportKind.html: 114 return new ReportHtml(kinds, conf, fio, diff); 115 } 116 } 117 118 /** 119 * Expects the event to come in the following order: 120 * - mutationKindEvent 121 * - locationStartEvent 122 * - locationEvent 123 * - locationEndEvent 124 * - statStartEvent 125 * - statEvent 126 * - statEndEvent 127 */ 128 struct ReportGenerator { 129 import std.algorithm : each; 130 import dextool.plugin.mutate.backend.report.compiler; 131 import dextool.plugin.mutate.backend.report.csv; 132 import dextool.plugin.mutate.backend.report.html; 133 import dextool.plugin.mutate.backend.report.markdown; 134 import dextool.plugin.mutate.backend.report.plain; 135 136 ReportEvent[] listeners; 137 FilesReporter fileReporter; 138 139 static auto make(const MutationKind[] kind, const ConfigReport conf, FilesysIO fio) { 140 import dextool.plugin.mutate.backend.utility; 141 142 auto kinds = dextool.plugin.mutate.backend.utility.toInternal(kind); 143 ReportEvent[] listeners; 144 145 final switch (conf.reportKind) { 146 case ReportKind.plain: 147 listeners = [new ReportPlain(kinds, conf, fio)]; 148 break; 149 case ReportKind.markdown: 150 listeners = [new ReportMarkdown(kinds, conf, fio)]; 151 break; 152 case ReportKind.compiler: 153 listeners = [new ReportCompiler(kinds, conf.reportLevel, fio)]; 154 break; 155 case ReportKind.json: 156 listeners = null; 157 break; 158 case ReportKind.csv: 159 listeners = [new ReportCSV(kinds, conf.reportLevel, fio)]; 160 break; 161 case ReportKind.html: 162 listeners = null; 163 break; 164 } 165 166 return ReportGenerator(listeners); 167 } 168 169 void mutationKindEvent(const MutationKind[] kind_) { 170 listeners.each!(a => a.mutationKindEvent(kind_)); 171 } 172 173 void locationStartEvent(ref Database db) { 174 listeners.each!(a => a.locationStartEvent(db)); 175 } 176 177 // trusted: trusting that d2sqlite3 and sqlite3 is memory safe. 178 void locationEvent(const ref IterateMutantRow r) @trusted { 179 listeners.each!(a => a.locationEvent(r)); 180 } 181 182 void locationEndEvent() { 183 listeners.each!(a => a.locationEndEvent); 184 } 185 186 void locationStatEvent() { 187 listeners.each!(a => a.locationStatEvent); 188 } 189 190 void statEvent(ref Database db) { 191 listeners.each!(a => a.statEvent(db)); 192 } 193 }