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, 110 data.kinds, 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 const Mutation.Status status, string rationale, FilesysIO fio) @trusted nothrow { 152 import dextool.plugin.mutate.backend.database : Rationale; 153 154 try { 155 auto trans = db.transaction; 156 157 auto mut = db.getMutation(id); 158 if (mut.isNull) { 159 logger.errorf("Failure when marking mutant: %s", id); 160 return ExitStatusType.Errors; 161 } 162 163 // because getMutation worked we know the ID is valid thus no need to 164 // check the return values when it or derived values are used. 165 166 const st_id = db.getMutationStatusId(id).get; 167 const checksum = db.getChecksum(st_id).get; 168 169 // assume that mutant has kind 170 const txt = makeMutationText(fio.makeInput(AbsolutePath(mut.get.file, 171 DirName(fio.getOutputDir))), Offset(mut.get.sloc.line, 172 mut.get.sloc.column), db.getKind(id), mut.get.lang).mutation; 173 174 db.markMutant(id, mut.get.file, mut.get.sloc, st_id, checksum, status, 175 Rationale(rationale), txt.idup); 176 177 db.updateMutationStatus(st_id, status); 178 179 logger.infof(`Mutant %s marked with status %s and rationale %s`, id, status, rationale); 180 181 trans.commit; 182 return ExitStatusType.Ok; 183 } catch (Exception e) { 184 logger.trace(e).collectException; 185 logger.error(e.msg).collectException; 186 } 187 return ExitStatusType.Errors; 188 } 189 190 ExitStatusType removeMarkedMutant(ref Database db, MutationId id) @trusted nothrow { 191 try { 192 auto trans = db.transaction; 193 194 // MutationStatusId used as check, removal of marking and updating status to unknown 195 const st_id = db.getMutationStatusId(id); 196 if (st_id.isNull) { 197 logger.errorf("Failure when removing marked mutant: %s", id); 198 return ExitStatusType.Errors; 199 } 200 201 if (db.isMarked(id)) { 202 db.removeMarkedMutant(st_id.get); 203 db.updateMutationStatus(st_id.get, Mutation.Status.unknown); 204 logger.infof("Removed marking for mutant %s.", id); 205 } else { 206 logger.errorf("Failure when removing marked mutant (mutant %s is not marked)", id); 207 } 208 209 trans.commit; 210 return ExitStatusType.Ok; 211 } catch (Exception e) { 212 logger.error(e.msg).collectException; 213 } 214 return ExitStatusType.Errors; 215 }