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 : 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 
67 /// Configuration data for the compile_commands.json
68 struct ConfigCompileDb {
69     import dextool.compilation_db : CompileCommandFilter;
70 
71     /// Raw user input via either config or cli
72     string[] rawDbs;
73 
74     /// path to compilation databases.
75     AbsolutePath[] dbs;
76 
77     /// Flags the user wants to be automatically removed from the compile_commands.json.
78     CompileCommandFilter flagFilter;
79 }
80 
81 /// Configuration of how the mutation analyzer should act.
82 struct ConfigAnalyze {
83     /// User input of excludes before they are adjusted to relative root
84     string[] rawExclude;
85     /// User input of includes before they are adjusted to relative root
86     string[] rawInclude;
87 
88     /// The constructed glob filter which based on rawExclude and rawinclude.
89     GlobFilter fileMatcher;
90 
91     /// The size of the thread pool which affects how many files are analyzed in parallel.
92     int poolSize;
93 
94     /// What files to analyze is derived from a diff.
95     bool unifiedDiffFromStdin;
96 
97     /// Remove files from the database that aren't found when analyzing.
98     bool prune;
99 
100     /// Turn off the sqlite synchronization safety
101     bool fastDbStore;
102 
103     /// If profiling data should be printed.
104     bool profile;
105 
106     /// Force the result from the files to always be saved
107     bool forceSaveAnalyze;
108 
109     /// Number of mutants to at most put in a schema (soft limit)
110     NamedType!(long, Tag!"MutantsPerSchema", long.init, TagStringable) mutantsPerSchema = 1000;
111 
112     /// Minimum number of mutants per schema for the schema to be saved in the database.
113     NamedType!(long, Tag!"MinMutantsPerSchema", long.init, TagStringable) minMutantsPerSchema = 3;
114 
115     /// User file/directories containing tests to checksum and timestamp
116     string[] rawTestPaths;
117     AbsolutePath[] testPaths;
118 
119     /// User input of excludes before they are adjusted to relative root
120     string[] rawTestExclude;
121     /// User input of includes before they are adjusted to relative root
122     string[] rawTestInclude;
123 
124     /// The constructed glob filter which based on rawExclude and rawinclude.
125     GlobFilter testFileMatcher;
126 
127     /// If coverage maps should be generated and saved.
128     NamedType!(bool, Tag!"SaveCoverage", bool.init, TagStringable) saveCoverage;
129 }
130 
131 /// Settings for the compiler
132 struct ConfigCompiler {
133     import dextool.compilation_db : SystemCompiler = Compiler;
134 
135     /// Additional flags the user wants to add besides those that are in the compile_commands.json.
136     string[] extraFlags;
137 
138     /// True requires system includes to be passed on to the compiler via -I
139     bool forceSystemIncludes;
140 
141     /// Deduce compiler flags from this compiler and not the one in the
142     /// supplied compilation database.  / This is needed when the one specified
143     /// in the DB has e.g. a c++ stdlib that is not compatible with clang.
144     SystemCompiler useCompilerSystemIncludes;
145 
146     NamedType!(bool, Tag!"AllowErrors", bool.init, TagStringable) allowErrors;
147 }
148 
149 /// Settings for mutation testing
150 struct ConfigMutationTest {
151     ShellCommand[] mutationTester;
152     /// Find executables in this directory and add them to mutationTester.
153     Path[] testCommandDir;
154     /// Flags to add to all executables found in `testCommandDir`
155     string[] testCommandDirFlag;
156 
157     ShellCommand mutationCompile;
158     ShellCommand[] mutationTestCaseAnalyze;
159     TestCaseAnalyzeBuiltin[] mutationTestCaseBuiltin;
160 
161     /// If the user hard code a timeout for the test suite.
162     Nullable!Duration mutationTesterRuntime;
163 
164     /// Timeout to use when compiling.
165     Duration buildCmdTimeout = 30.dur!"minutes";
166 
167     /// In what order to choose mutants to test.
168     MutationOrder mutationOrder;
169     bool dryRun;
170 
171     /// How to behave when new test cases are detected.
172     enum NewTestCases {
173         doNothing,
174         /// Automatically reset alive mutants
175         resetAlive,
176     }
177 
178     NewTestCases onNewTestCases;
179 
180     /// How to behave when test cases are detected of having been removed
181     enum RemovedTestCases {
182         doNothing,
183         /// Remove it and all results connectedto the test case
184         remove,
185     }
186 
187     RemovedTestCases onRemovedTestCases;
188 
189     /// How to behave when mutants have aged.
190     enum OldMutant {
191         nothing,
192         test,
193     }
194 
195     OldMutant onOldMutants;
196     long oldMutantsNr;
197     NamedType!(double, Tag!"OldMutantPercentage", double.init, TagStringable) oldMutantPercentage = 0.0;
198 
199     /// Max time to run mutation testing.
200     // note that Duration.max + Clock.currTime results in a negative time...
201     Duration maxRuntime = 52.dur!"weeks";
202 
203     // Constrain the mutation testing.
204     TestConstraint constraint;
205 
206     /// If constraints should be read from a unified diff via stdin.
207     bool unifiedDiffFromStdin;
208 
209     /// Stop after this many alive mutants are found. Only effective if constraint.empty is false.
210     Nullable!int maxAlive;
211 
212     /// The size of the thread pool which affects how many tests are executed in parallel.
213     int testPoolSize;
214 
215     /// Seed used when randomly choosing mutants to test in a pull request.
216     long pullRequestSeed = 42;
217 
218     /// If early stopping of test command execution should be used
219     bool useEarlyTestCmdStop;
220 
221     /// If schematas are used for mutation testing.
222     bool useSchemata;
223 
224     /// Sanity check a schemata before it is used.
225     bool sanityCheckSchemata;
226 
227     /// If the schematas should be written to a separate file for offline inspection.
228     bool logSchemata;
229 
230     /// Stop mutation testing after the last schemata has been executed
231     bool stopAfterLastSchema;
232 
233     /// Minimum number of mutants per schema for it to be used.
234     NamedType!(long, Tag!"MinMutantsPerSchema", long.init, TagStringable) minMutantsPerSchema = 3;
235 
236     enum LoadBehavior {
237         nothing,
238         /// Slow the testing until the load goes below the threshold
239         slowdown,
240         /// Stop mutation testing if the 15min load average reach this number.
241         halt,
242     }
243 
244     LoadBehavior loadBehavior;
245     NamedType!(double, Tag!"LoadThreshold", double.init, TagStringable) loadThreshold;
246 
247     /// If coverage data should be gathered and saved.
248     NamedType!(bool, Tag!"UseCoverage", bool.init, TagStringable) useCoverage;
249 
250     /// If the generated coverage files should be saved.
251     NamedType!(bool, Tag!"LogCoverage", bool.init, TagStringable) logCoverage;
252 
253     /// allows a user to control exactly which files the coverage and schemata runtime is injected in.
254     UserRuntime[] userRuntimeCtrl;
255 
256     /// Continuesly run the test suite to see that the test suite is OK when no mutants are injected.
257     NamedType!(bool, Tag!"ContinuesCheckTestSuite", bool.init, TagStringable) contCheckTestSuite;
258     NamedType!(int, Tag!"ContinuesCheckTestSuitePeriod", int.init, TagStringable) contCheckTestSuitePeriod = 100;
259 }
260 
261 /// Settings for the administration mode
262 struct ConfigAdmin {
263     AdminOperation adminOp;
264     Mutation.Status mutantStatus;
265     Mutation.Status mutantToStatus;
266     string testCaseRegex;
267     long mutationId;
268     string mutantRationale;
269 
270     /// used to specify a kind of mutation
271     Mutation.Kind[] subKind;
272 }
273 
274 struct ConfigWorkArea {
275     /// User input root.
276     string rawRoot;
277 
278     AbsolutePath root;
279 
280     /// User input of excludes before they are adjusted to relative root
281     string[] rawExclude;
282     /// User input of includes before they are adjusted to relative root
283     string[] rawInclude;
284 
285     /// The constructed glob filter which based on rawExclude and rawinclude.
286     /// Only mutants whose location match will be generated.
287     GlobFilter mutantMatcher;
288 }
289 
290 /// Configuration of the generate mode.
291 struct ConfigGenerate {
292     long mutationId;
293 }