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