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 This module contains functionality to administrate the database
11 */
12 module dextool.plugin.mutate.backend.admin;
13 
14 import std.exception : collectException;
15 import logger = std.experimental.logger;
16 
17 import dextool.type;
18 
19 import dextool.plugin.mutate.type : MutationKind, AdminOperation;
20 import dextool.plugin.mutate.backend.database : Database, MutationId;
21 import dextool.plugin.mutate.backend.type : Mutation, Offset;
22 import dextool.plugin.mutate.backend.interface_ : FilesysIO;
23 import dextool.plugin.mutate.backend.generate_mutant : makeMutationText;
24 
25 auto makeAdmin() {
26     return BuildAdmin();
27 }
28 
29 private:
30 
31 import std.regex : regex, Regex;
32 
33 struct BuildAdmin {
34 @safe:
35 nothrow:
36     private struct InternalData {
37         bool errorInData;
38 
39         AdminOperation admin_op;
40         Mutation.Kind[] kinds;
41         Mutation.Status status;
42         Mutation.Status to_status;
43         Regex!char test_case_regex;
44         MutationId mutant_id;
45         string mutant_rationale;
46         FilesysIO fio;
47     }
48 
49     private InternalData data;
50 
51     auto operation(AdminOperation v) {
52         data.admin_op = v;
53         return this;
54     }
55 
56     auto mutations(MutationKind[] v) {
57         import dextool.plugin.mutate.backend.utility;
58 
59         data.kinds = toInternal(v);
60         return this;
61     }
62 
63     auto fromStatus(Mutation.Status v) {
64         data.status = v;
65         return this;
66     }
67 
68     auto toStatus(Mutation.Status v) {
69         data.to_status = v;
70         return this;
71     }
72 
73     auto testCaseRegex(string v) {
74         try {
75             data.test_case_regex = regex(v);
76         } catch (Exception e) {
77             logger.error(e.msg).collectException;
78             data.errorInData = true;
79         }
80         return this;
81     }
82 
83     auto markMutantData(long v, string s, FilesysIO f) {
84         data.mutant_id = v;
85         data.mutant_rationale = s;
86         data.fio = f;
87         return this;
88     }
89 
90     ExitStatusType run(ref Database db) {
91         if (data.errorInData) {
92             logger.error("Invalid parameters").collectException;
93             return ExitStatusType.Errors;
94         }
95 
96         final switch (data.admin_op) {
97         case AdminOperation.none:
98             logger.error("No admin operation specified").collectException;
99             return ExitStatusType.Errors;
100         case AdminOperation.resetMutant:
101             return resetMutant(db, data.kinds,
102                     data.status, data.to_status);
103         case AdminOperation.removeMutant:
104             return removeMutant(db, data.kinds);
105         case AdminOperation.removeTestCase:
106             return removeTestCase(db,
107                     data.kinds, data.test_case_regex);
108         case AdminOperation.markMutant:
109             return markMutant(db, data.mutant_id, data.kinds,
110                     data.to_status, data.mutant_rationale, data.fio);
111         case AdminOperation.removeMarkedMutant:
112             return removeMarkedMutant(db, data.mutant_id);
113         }
114     }
115 }
116 
117 ExitStatusType resetMutant(ref Database db, const Mutation.Kind[] kinds,
118         Mutation.Status status, Mutation.Status to_status) @safe nothrow {
119     try {
120         db.resetMutant(kinds, status, to_status);
121     } catch (Exception e) {
122         logger.error(e.msg).collectException;
123         return ExitStatusType.Errors;
124     }
125 
126     return ExitStatusType.Ok;
127 }
128 
129 ExitStatusType removeMutant(ref Database db, const Mutation.Kind[] kinds) @safe nothrow {
130     try {
131         db.removeMutant(kinds);
132     } catch (Exception e) {
133         logger.error(e.msg).collectException;
134         return ExitStatusType.Errors;
135     }
136 
137     return ExitStatusType.Ok;
138 }
139 
140 ExitStatusType removeTestCase(ref Database db, const Mutation.Kind[] kinds, const Regex!char regex) @safe nothrow {
141     try {
142         db.removeTestCase(regex, kinds);
143     } catch (Exception e) {
144         logger.error(e.msg).collectException;
145         return ExitStatusType.Errors;
146     }
147     return ExitStatusType.Ok;
148 }
149 
150 ExitStatusType markMutant(ref Database db, MutationId id, const Mutation.Kind[] kinds,
151         Mutation.Status status, string rationale, FilesysIO fio) @trusted nothrow {
152     try {
153         import std.conv : to;
154         auto trans = db.transaction;
155 
156         const st_id = db.getMutationStatusId(id);
157         if (st_id.isNull) {
158             logger.errorf("Failure when marking mutant: %s", id);
159         } else {
160             auto mut = db.getMutation(id);
161             if (mut.isNull)
162                 logger.errorf("Failure when marking mutant: %s", id);
163             else {
164                 // assume that mutant has kind
165                 auto txt = makeMutationText(fio.makeInput(AbsolutePath(mut.file, DirName(fio.getOutputDir))),
166                         Offset(mut.sloc.line, mut.sloc.column), db.getKind(id), mut.lang).mutation;
167                 db.markMutant(mut, st_id.get, status, rationale, to!string(txt));
168                 db.updateMutationStatus(st_id.get, status);
169                 logger.infof(`Mutant %s marked with status %s and rationale '%s'.`, id, status, rationale);
170             }
171         }
172 
173         trans.commit;
174     } catch (Exception e) {
175         logger.error(e.msg).collectException;
176         return ExitStatusType.Errors;
177     }
178     return ExitStatusType.Ok;
179 }
180 
181 ExitStatusType removeMarkedMutant(ref Database db, MutationId id) @trusted nothrow {
182     try {
183         auto trans = db.transaction;
184 
185         // MutationStatusId used as check, removal of marking and updating status to unknown
186         const st_id = db.getMutationStatusId(id);
187         if (st_id.isNull) {
188             logger.errorf("Failure when removing marked mutant: %s", id);
189         } else {
190             if (db.isMarked(id)) {
191                 db.removeMarkedMutant(st_id.get);
192                 db.updateMutationStatus(st_id.get, Mutation.Status.unknown);
193                 logger.infof("Removed marking for mutant %s.", id);
194             } else {
195                 logger.errorf("Failure when removing marked mutant (mutant %s is not marked)", id);
196             }
197         }
198 
199         trans.commit;
200     } catch (Exception e) {
201         logger.error(e.msg).collectException;
202         return ExitStatusType.Errors;
203     }
204     return ExitStatusType.Ok;
205 }