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 }