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 }