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 }