1 /**
2 Copyright: Copyright (c) 2018, 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 module dextool.plugin.mutate.config;
11 
12 import core.time : Duration, dur;
13 import std.typecons : Nullable;
14 
15 import my.filter : GlobFilter;
16 import my.named_type;
17 import my.optional;
18 
19 import dextool.plugin.mutate.type;
20 import dextool.type : AbsolutePath, Path;
21 public import dextool.plugin.mutate.backend.type : Mutation, TestGroup;
22 
23 /// The mode the tool is operating in
24 enum ToolMode {
25     /// No mode set
26     none,
27     /// analyze for mutation points
28     analyzer,
29     /// center that can operate and control subcomponents
30     generate_mutant,
31     /// test mutation points with a test suite
32     test_mutants,
33     /// generate a report of the mutation points
34     report,
35     /// administrator interface for the mutation database
36     admin,
37     /// Dump the TOML configuration to the console
38     dumpConfig,
39     /// Write a TOML config to the filesystem
40     initConfig,
41 }
42 
43 /// Config of the report.
44 struct ConfigReport {
45     ReportKind reportKind;
46     ReportSection[] reportSection = [ReportSection.summary];
47 
48     /// Directory to write logs to when writing to the filesystem.
49     AbsolutePath logDir;
50 
51     /// Controls how to sort test cases by their kill statistics.
52     ReportKillSortOrder tcKillSortOrder;
53     int tcKillSortNum = 20;
54 
55     /// User regex for reporting groups of tests
56     TestGroup[] testGroups;
57 
58     /// If a unified diff should be used in the report
59     bool unifiedDiff;
60 
61     /// If profiling data should be printed.
62     bool profile;
63 
64     NamedType!(uint, Tag!"HighInterestMutantsNr", uint.init, TagStringable) highInterestMutantsNr = 5;
65 
66     alias TestMetaData = NamedType!(AbsolutePath, Tag!"TestMetaData",
67             AbsolutePath.init, TagStringable);
68     Optional!TestMetaData testMetadata;
69 }
70 
71 /// Configuration data for the compile_commands.json
72 struct ConfigCompileDb {
73     import dextool.compilation_db : CompileCommandFilter;
74 
75     /// Raw user input via either config or cli
76     string[] rawDbs;
77 
78     /// path to compilation databases.
79     AbsolutePath[] dbs;
80 
81     /// Flags the user wants to be automatically removed from the compile_commands.json.
82     CompileCommandFilter flagFilter;
83 }
84 
85 /// Configuration of how the mutation analyzer should act.
86 struct ConfigAnalyze {
87     /// User input of excludes before they are adjusted to relative root
88     string[] rawExclude;
89     /// User input of includes before they are adjusted to relative root
90     string[] rawInclude;
91 
92     /// The constructed glob filter which based on rawExclude and rawinclude.
93     GlobFilter fileMatcher;
94 
95     /// The size of the thread pool which affects how many files are analyzed in parallel.
96     int poolSize;
97 
98     /// What files to analyze is derived from a diff.
99     bool unifiedDiffFromStdin;
100 
101     /// Remove files from the database that aren't found when analyzing.
102     bool prune;
103 
104     /// Turn off the sqlite synchronization safety
105     bool fastDbStore;
106 
107     /// If profiling data should be printed.
108     bool profile;
109 
110     /// Force the result from the files to always be saved
111     bool forceSaveAnalyze;
112 
113     /// User file/directories containing tests to checksum and timestamp
114     string[] rawTestPaths;
115     AbsolutePath[] testPaths;
116 
117     /// User input of excludes before they are adjusted to relative root
118     string[] rawTestExclude;
119     /// User input of includes before they are adjusted to relative root
120     string[] rawTestInclude;
121 
122     /// The constructed glob filter which based on rawExclude and rawinclude.
123     GlobFilter testFileMatcher;
124 }
125 
126 /// Settings for the compiler
127 struct ConfigCompiler {
128     import dextool.compilation_db : SystemCompiler = Compiler;
129 
130     /// Additional flags the user wants to add besides those that are in the compile_commands.json.
131     string[] extraFlags;
132 
133     /// True requires system includes to be passed on to the compiler via -I
134     bool forceSystemIncludes;
135 
136     /// Deduce compiler flags from this compiler and not the one in the
137     /// supplied compilation database.  / This is needed when the one specified
138     /// in the DB has e.g. a c++ stdlib that is not compatible with clang.
139     SystemCompiler useCompilerSystemIncludes;
140 
141     NamedType!(bool, Tag!"AllowErrors", bool.init, TagStringable) allowErrors;
142 }
143 
144 /// Settings for mutation testing
145 struct ConfigMutationTest {
146     ShellCommand[] mutationTester;
147     /// Find executables in this directory and add them to mutationTester.
148     Path[] testCommandDir;
149     /// Flags to add to all executables found in `testCommandDir`
150     string[] testCommandDirFlag;
151 
152     ShellCommand mutationCompile;
153     ShellCommand[] mutationTestCaseAnalyze;
154     TestCaseAnalyzeBuiltin[] mutationTestCaseBuiltin;
155 
156     /// If the user hard code a timeout for the test suite.
157     Nullable!Duration mutationTesterRuntime;
158 
159     /// Timeout to use when compiling.
160     Duration buildCmdTimeout = 30.dur!"minutes";
161 
162     /// In what order to choose mutants to test.
163     MutationOrder mutationOrder = MutationOrder.bySize;
164     bool dryRun;
165 
166     /// How to behave when new test cases are detected.
167     enum NewTestCases {
168         doNothing,
169         /// Automatically reset alive mutants
170         resetAlive,
171     }
172 
173     NewTestCases onNewTestCases;
174 
175     /// How to behave when test cases are detected of having been removed
176     enum RemovedTestCases {
177         doNothing,
178         /// Remove it and all results connectedto the test case
179         remove,
180     }
181 
182     RemovedTestCases onRemovedTestCases;
183 
184     /// How to behave when mutants have aged.
185     enum OldMutant {
186         nothing,
187         test,
188     }
189 
190     OldMutant onOldMutants;
191     long oldMutantsNr;
192     NamedType!(double, Tag!"OldMutantPercentage", double.init, TagStringable) oldMutantPercentage = 0.0;
193 
194     /// Max time to run mutation testing.
195     // note that Duration.max + Clock.currTime results in a negative time...
196     Duration maxRuntime = 52.dur!"weeks";
197 
198     // Constrain the mutation testing.
199     TestConstraint constraint;
200 
201     /// If constraints should be read from a unified diff via stdin.
202     bool unifiedDiffFromStdin;
203 
204     /// Stop after this many alive mutants are found. Only effective if constraint.empty is false.
205     Nullable!int maxAlive;
206 
207     /// The size of the thread pool which affects how many tests are executed in parallel.
208     int testPoolSize;
209 
210     /// If early stopping of test command execution should be used
211     bool useEarlyTestCmdStop;
212 
213     enum LoadBehavior {
214         nothing,
215         /// Slow the testing until the load goes below the threshold
216         slowdown,
217         /// Stop mutation testing if the 15min load average reach this number.
218         halt,
219     }
220 
221     LoadBehavior loadBehavior;
222     NamedType!(double, Tag!"LoadThreshold", double.init, TagStringable) loadThreshold;
223 
224     /// Continuesly run the test suite to see that the test suite is OK when no mutants are injected.
225     NamedType!(bool, Tag!"ContinuesCheckTestSuite", bool.init, TagStringable) contCheckTestSuite;
226     NamedType!(int, Tag!"ContinuesCheckTestSuitePeriod", int.init, TagStringable) contCheckTestSuitePeriod = 100;
227 
228     NamedType!(bool, Tag!"TestCmdChecksum", bool.init, TagStringable) testCmdChecksum;
229 
230     NamedType!(long, Tag!"MaxTestCaseOutputCaptureMbyte", int.init, TagStringable) maxTestCaseOutput = 10;
231 
232     NamedType!(bool, Tag!"UseSkipMutant", bool.init, TagStringable) useSkipMutant;
233 
234     NamedType!(double, Tag!"MaxMemoryUsage", double.init, TagStringable) maxMemUsage = 90.0;
235 }
236 
237 /// Settings for the administration mode
238 struct ConfigAdmin {
239     AdminOperation adminOp;
240     Mutation.Status mutantStatus;
241     Mutation.Status mutantToStatus;
242     string testCaseRegex;
243     long mutationId;
244     string mutantRationale;
245 
246     /// used to specify a kind of mutation
247     Mutation.Kind[] subKind;
248 }
249 
250 struct ConfigWorkArea {
251     /// User input root.
252     string rawRoot;
253 
254     AbsolutePath root;
255 
256     /// User input of excludes before they are adjusted to relative root
257     string[] rawExclude;
258     /// User input of includes before they are adjusted to relative root
259     string[] rawInclude;
260 
261     /// The constructed glob filter which based on rawExclude and rawinclude.
262     /// Only mutants whose location match will be generated.
263     GlobFilter mutantMatcher;
264 }
265 
266 /// Configuration of the generate mode.
267 struct ConfigGenerate {
268     long mutationId;
269 }
270 
271 struct ConfigSchema {
272     bool use;
273 
274     SchemaRuntime runtime;
275 
276     /// Number of mutants to at most put in a schema (soft limit)
277     NamedType!(long, Tag!"MutantsPerSchema", long.init, TagStringable) mutantsPerSchema = 1000;
278 
279     /// Minimum number of mutants per schema for the schema to be saved in the database.
280     NamedType!(long, Tag!"MinMutantsPerSchema", long.init, TagStringable) minMutantsPerSchema = 3;
281 
282     /// Sanity check a schemata before it is used.
283     bool sanityCheckSchemata;
284 
285     /// If the schematas should be written to a separate file for offline inspection.
286     /// Write the instrumented source code to .cov.<ext> for separate inspection.
287     bool log;
288 
289     /// Stop mutation testing after the last schemata has been executed
290     bool stopAfterLastSchema;
291 
292     /// allows a user to control exactly which files the coverage and schemata
293     /// runtime is injected in.
294     UserRuntime[] userRuntimeCtrl;
295 
296     /// Only compile and execute the test suite. Used to train the schema generator.
297     NamedType!(bool, Tag!"SchemaTrainGenerator", bool.init, TagStringable) onlyCompile;
298 }
299 
300 struct ConfigCoverage {
301     bool use;
302 
303     CoverageRuntime runtime;
304 
305     /// If the generated coverage files should be saved.
306     bool log;
307 
308     /// allows a user to control exactly which files the coverage and schemata
309     /// runtime is injected in.
310     UserRuntime[] userRuntimeCtrl;
311 }