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 }