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 module dextool.plugin.mutate.backend.type;
11 
12 import dextool.hash : Checksum128;
13 public import dextool.plugin.mutate.backend.database.type : MutantAttr, MutantMetaData;
14 
15 @safe:
16 
17 alias Checksum = Checksum128;
18 
19 /// Used to replace invalid UTF-8 characters.
20 immutable invalidUtf8 = "[invalid utf8]";
21 
22 /** A mutation point for a specific file.
23  *
24  * TODO: shouldn't this have the file ID?
25  *
26  * See: definitions.md for more information
27  */
28 struct MutationPoint {
29     Offset offset;
30     Mutation[] mutations;
31 
32     bool opEquals()(auto ref const S s) @safe pure nothrow const @nogc {
33         return offset == s.offset && mutations == s.mutations;
34     }
35 }
36 
37 /// Offset range. It is a closed->open set.
38 struct Offset {
39     uint begin;
40     uint end;
41 
42     size_t toHash() @safe pure nothrow const @nogc scope {
43         auto a = begin.hashOf();
44         return end.hashOf(a); // mixing two hash values
45     }
46 
47     bool opEquals()(auto ref const typeof(this) s) const {
48         return s.begin == begin && s.end == end;
49     }
50 
51     int opCmp(ref const typeof(this) rhs) @safe pure nothrow const @nogc {
52         // return -1 if "this" is less than rhs, 1 if bigger and zero equal
53         if (begin < rhs.begin)
54             return -1;
55         if (begin > rhs.begin)
56             return 1;
57         if (end < rhs.end)
58             return -1;
59         if (end > rhs.end)
60             return 1;
61         return 0;
62     }
63 }
64 
65 /// Location in the source code.
66 struct SourceLoc {
67     uint line;
68     uint column;
69 
70     int opCmp(ref const typeof(this) rhs) @safe pure nothrow const @nogc {
71         // return -1 if "this" is less than rhs, 1 if bigger and zero equal
72         if (line < rhs.line)
73             return -1;
74         if (line > rhs.line)
75             return 1;
76         if (column < rhs.column)
77             return -1;
78         if (column > rhs.column)
79             return 1;
80         return 0;
81     }
82 }
83 
84 struct SourceLocRange {
85     SourceLoc begin;
86     SourceLoc end;
87 
88     int opCmp(ref const typeof(this) rhs) const {
89         // return -1 if "this" is less than rhs, 1 if bigger and zero equal
90         auto cb = begin.opCmp(rhs.begin);
91         if (cb != 0)
92             return cb;
93         auto ce = end.opCmp(rhs.end);
94         if (ce != 0)
95             return ce;
96         return 0;
97     }
98 }
99 
100 /// A possible mutation.
101 struct Mutation {
102     /// States what kind of mutations that can be performed on this mutation point.
103     // ONLY ADD NEW ITEMS TO THE END
104     enum Kind : uint {
105         /// the kind is not initialized thus can only ignore the point
106         none,
107         /// Relational operator replacement
108         rorLT,
109         rorLE,
110         rorGT,
111         rorGE,
112         rorEQ,
113         rorNE,
114         /// Logical connector replacement
115         lcrAnd,
116         lcrOr,
117         /// Arithmetic operator replacement
118         aorMul,
119         aorDiv,
120         aorRem,
121         aorAdd,
122         aorSub,
123         aorMulAssign,
124         aorDivAssign,
125         aorRemAssign,
126         aorAddAssign,
127         aorSubAssign,
128         /// Unary operator insert on an lvalue
129         uoiPostInc,
130         uoiPostDec,
131         // these work for rvalue
132         uoiPreInc,
133         uoiPreDec,
134         uoiAddress,
135         uoiIndirection,
136         uoiPositive,
137         uoiNegative,
138         uoiComplement,
139         uoiNegation,
140         uoiSizeof_,
141         /// Absolute value replacement
142         absPos,
143         absNeg,
144         absZero,
145         /// statement deletion
146         stmtDel,
147         /// Conditional Operator Replacement (reduced set)
148         corAnd,
149         corOr,
150         corFalse,
151         corLhs,
152         corRhs,
153         corEQ,
154         corNE,
155         corTrue,
156         /// Relational operator replacement
157         rorTrue,
158         rorFalse,
159         /// Decision/Condition Coverage
160         dccTrue,
161         dccFalse,
162         dccBomb,
163         /// Decision/Condition Requirement
164         dcrCaseDel,
165         /// Relational operator replacement for pointers
166         rorpLT,
167         rorpLE,
168         rorpGT,
169         rorpGE,
170         rorpEQ,
171         rorpNE,
172         /// Logical Operator Replacement Bit-wise (lcrb)
173         lcrbAnd,
174         lcrbOr,
175         lcrbAndAssign,
176         lcrbOrAssign,
177         lcrbLhs,
178         lcrbRhs,
179         /// Logical connector replacement
180         lcrLhs,
181         lcrRhs,
182         lcrTrue,
183         lcrFalse,
184         /// Arithmetic operator replacement
185         aorLhs,
186         aorRhs,
187         // uoi
188         uoiDel,
189     }
190 
191     /// The status of a mutant.
192     enum Status : ubyte {
193         /// the mutation isn't tested
194         unknown,
195         /// killed by the test suite
196         killed,
197         /// not killed by the test suite
198         alive,
199         /// the mutation resulted in invalid code that didn't compile
200         killedByCompiler,
201         /// the mutant resulted in the test suite/sut reaching the timeout threshold
202         timeout,
203     }
204 
205     Kind kind;
206     Status status;
207 }
208 
209 /// The unique checksum for a schemata.
210 struct SchemataChecksum {
211     Checksum value;
212 }
213 
214 /** The checksum that uniquely identify the mutation done in the source code.
215  *
216  * Multiple mutants can end up resulting in the same change in the source code.
217  */
218 struct CodeChecksum {
219     Checksum value;
220     alias value this;
221 }
222 
223 /// The mutant coupled to the source code mutant that is injected.
224 struct CodeMutant {
225     CodeChecksum id;
226     Mutation mut;
227 
228     bool opEquals(const typeof(this) s) const {
229         return id == s.id;
230     }
231 
232     size_t toHash() @safe pure nothrow const @nogc scope {
233         return id.toHash;
234     }
235 }
236 
237 /// A test case from the test suite that is executed on mutants.
238 struct TestCase {
239     /// The name of the test case as extracted from the test suite.
240     string name;
241 
242     /// A location identifier intended to be presented to the user.
243     string location;
244 
245     this(string name) @safe pure nothrow @nogc scope {
246         this(name, null);
247     }
248 
249     this(string name, string loc) @safe pure nothrow @nogc scope {
250         this.name = name;
251         this.location = loc;
252     }
253 
254     int opCmp(ref const typeof(this) s) @safe pure nothrow const @nogc scope {
255         if (name < s.name)
256             return -1;
257         else if (name > s.name)
258             return 1;
259         else if (location < s.location)
260             return -1;
261         else if (location > s.location)
262             return 1;
263 
264         return 0;
265     }
266 
267     bool opEquals(ref const typeof(this) s) @safe pure nothrow const @nogc scope {
268         return name == s.name && location == s.location;
269     }
270 
271     size_t toHash() @safe nothrow const {
272         return typeid(string).getHash(&name) + typeid(string).getHash(&location);
273     }
274 
275     string toString() @safe pure const nothrow {
276         import std.array : appender;
277 
278         auto buf = appender!string;
279         toString(buf);
280         return buf.data;
281     }
282 
283     import std.range : isOutputRange;
284 
285     void toString(Writer)(ref Writer w) const if (isOutputRange!(Writer, char)) {
286         import std.range : put;
287 
288         if (location.length != 0) {
289             put(w, location);
290             put(w, ":");
291         }
292         put(w, name);
293     }
294 }
295 
296 /// The language a file or mutant is.
297 enum Language {
298     /// the default is assumed to be c++
299     assumeCpp,
300     ///
301     cpp,
302     ///
303     c
304 }
305 
306 /// Test Group criterias.
307 struct TestGroup {
308     import std.regex : Regex, regex;
309 
310     string description;
311     string name;
312 
313     /// What the user configured as regex. Useful when e.g. generating reports
314     /// for a user.
315     string userInput;
316     /// The compiled regex.
317     Regex!char re;
318 
319     this(string name, string desc, string r) {
320         this.name = name;
321         description = desc;
322         userInput = r;
323         re = regex(r);
324     }
325 
326     string toString() @safe pure const {
327         import std.format : format;
328 
329         return format("TestGroup(%s, %s, %s)", name, description, userInput);
330     }
331 
332     import std.range : isOutputRange;
333 
334     void toString(Writer)(ref Writer w) if (isOutputRange!(Writer, char)) {
335         import std.format : formattedWrite;
336 
337         formattedWrite(w, "TestGroup(%s, %s, %s)", name, description, userInput);
338     }
339 }
340 
341 /// Number of times a mutant has been tested.
342 struct MutantTestCount {
343     long value;
344     alias value this;
345 }
346 
347 /** A source code token.
348  *
349  * The source can contain invalid UTF-8 chars therefor every token has to be
350  * validated. Otherwise it isn't possible to generate a report.
351  */
352 struct Token {
353     import std.format : format;
354     import clang.c.Index : CXTokenKind;
355 
356     // TODO: this should be a language agnostic type when more languages are
357     // added in the future.
358     CXTokenKind kind;
359     Offset offset;
360     SourceLoc loc;
361     SourceLoc locEnd;
362     string spelling;
363 
364     this(CXTokenKind kind, Offset offset, SourceLoc loc, SourceLoc locEnd, string spelling) {
365         this.kind = kind;
366         this.offset = offset;
367         this.loc = loc;
368         this.locEnd = locEnd;
369 
370         try {
371             import std.utf : validate;
372 
373             validate(spelling);
374             this.spelling = spelling;
375         } catch (Exception e) {
376             this.spelling = invalidUtf8;
377         }
378     }
379 
380     string toId() @safe const {
381         return format("%s-%s", offset.begin, offset.end);
382     }
383 
384     string toName() @safe const {
385         import std.conv : to;
386 
387         return kind.to!string;
388     }
389 
390     int opCmp(ref const typeof(this) s) const @safe {
391         if (offset.begin > s.offset.begin)
392             return 1;
393         if (offset.begin < s.offset.begin)
394             return -1;
395         if (offset.end > s.offset.end)
396             return 1;
397         if (offset.end < s.offset.end)
398             return -1;
399         return 0;
400     }
401 }
402 
403 @("shall be possible to construct in @safe")
404 @safe unittest {
405     import clang.c.Index : CXTokenKind;
406 
407     auto tok = Token(CXTokenKind.comment, Offset(1, 2), SourceLoc(1, 2), SourceLoc(1, 2), "smurf");
408 }