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 #SPC-plugin_mutate_track_ctest 11 12 # Design 13 The parser is a strict Moore FSM. 14 The FSM is small enough that a switch implementation is good enough, clear and explicit. 15 16 The parser has a strict separation of the *next state* and *action*. 17 This is to make it easier to unittest the FSM is so is needed. 18 It also makes it easier to understand what the state transitions are dependent on and when an action is performed. 19 20 The calculation of the next state is a strongly pure function to enforce that it is only dependent on the input. See: `nextState`. 21 */ 22 module dextool.plugin.mutate.backend.test_mutant.ctest_post_analyze; 23 24 import std.exception : collectException; 25 import std.range : isInputRange, isOutputRange; 26 import logger = std.experimental.logger; 27 28 import dextool.type : AbsolutePath; 29 import dextool.plugin.mutate.backend.type : TestCase; 30 31 /** Parse input for ctest test cases. 32 Params: 33 r = range that is chunked by line 34 sink = an output that accepts values of type TestCase via `put`. 35 */ 36 struct CtestParser { 37 import std.regex : regex, ctRegex, matchFirst; 38 39 private { 40 // example: The following tests FAILED: 41 enum re_start_failing_tc_list = ctRegex!(`^\s*The following tests FAILED`); 42 // example: 40 - gtest-typed-test_test (OTHER_FAULT) 43 enum re_fail_msg = ctRegex!(`^\s*\d*\s*-\s*(?P<tc>.*?)\s*\(.*\)`); 44 // example: Errors while running CTest 45 enum re_end_failing_tc_list = ctRegex!(`^\s*Errors while running CTest`); 46 47 FsmData data; 48 } 49 50 void process(T, T1)(T line, ref T1 sink) { 51 import std.range : put; 52 import std..string : strip; 53 54 auto fail_msg_match = matchFirst(line, re_fail_msg); 55 data.hasStartOfList = !matchFirst(line, re_start_failing_tc_list).empty; 56 data.hasFailedMessage = !fail_msg_match.empty; 57 data.hasEndOfList = !matchFirst(line, re_end_failing_tc_list).empty; 58 59 { 60 auto rval = nextState(data); 61 data.st = rval[0]; 62 data.act = rval[1]; 63 } 64 65 final switch (data.act) with (Action) { 66 case none: 67 break; 68 case putTestCase: 69 put(sink, TestCase(fail_msg_match["tc"].strip.idup)); 70 break; 71 } 72 } 73 } 74 75 private: 76 77 enum State { 78 findStartOfList, 79 extractTestCase, 80 } 81 82 enum Action { 83 none, 84 putTestCase, 85 } 86 87 struct FsmData { 88 State st; 89 Action act; 90 91 bool hasStartOfList; 92 bool hasFailedMessage; 93 bool hasEndOfList; 94 } 95 96 auto nextState(immutable FsmData d) @safe pure nothrow @nogc { 97 import std.typecons : tuple; 98 99 State next = d.st; 100 Action act = d.act; 101 102 final switch (d.st) with (State) { 103 case findStartOfList: 104 act = Action.none; 105 if (d.hasStartOfList) 106 next = extractTestCase; 107 break; 108 case extractTestCase: 109 act = Action.none; 110 if (d.hasFailedMessage) 111 act = Action.putTestCase; 112 else if (d.hasEndOfList) 113 next = findStartOfList; 114 break; 115 } 116 117 return tuple(next, act); 118 }