1 /**
2 Copyright: Copyright (c) 2018, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännströmoakim 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 module dextool.plugin.mutate.backend.test_mutant.makefile_post_analyze;
11 
12 import std.exception : collectException;
13 import std.range : isInputRange, isOutputRange;
14 import logger = std.experimental.logger;
15 
16 import dextool.plugin.mutate.backend.test_mutant.interface_ : TestCaseReport, GatherTestCase;
17 import dextool.plugin.mutate.backend.type : TestCase;
18 import dextool.type : AbsolutePath;
19 
20 /** Parse input for the first makefile targets that fail.
21  *
22  * It is important that it is the first failing to avoid possible problems with
23  * recursive makefiles which would end with multiple makefile targets that
24  * fail. I assume that the first one is the most specific and relevant.
25  *
26  * TODO: if that isn't good enough maybe another approach would be to
27  * concatenate them to one big target? It would reflect in a "chain" of targets
28  * that lead up to the error.
29  *
30  * Params:
31  *  r = range that is chunked by line
32  *  report = where the results are put.
33   */
34 struct MakefileParser {
35     import std.regex : regex, ctRegex, matchFirst;
36 
37     private {
38         bool isDone;
39 
40         // example: binary exiting with something else than zero.
41         //make: *** [exit1] Error 1
42         //make: *** [exit2] Error 2
43         //make: *** [segfault] Segmentation fault (core dumped)
44         enum re_exit_with_error_code = ctRegex!(`.*make:\s*\*\*\*\s*\[(?P<tc>.*)\].*`);
45     }
46 
47     void process(T)(T line, TestCaseReport report) {
48         import std.range : put;
49         import std.string : strip;
50 
51         if (isDone)
52             return;
53 
54         auto exit_with_error_code_match = matchFirst(line, re_exit_with_error_code);
55 
56         if (!exit_with_error_code_match.empty) {
57             report.reportFailed(TestCase(exit_with_error_code_match["tc"].strip.idup));
58             isDone = true;
59         }
60     }
61 }
62 
63 version (unittest) {
64     import std.algorithm : each;
65     import std.array : array;
66     import unit_threaded : shouldEqual, shouldBeIn;
67 }
68 
69 @("shall report the failed test case")
70 unittest {
71     auto app = new GatherTestCase;
72 
73     MakefileParser parser;
74     // dfmt off
75     foreach (a; [
76     `./a.out segfault`,
77     `segfault`,
78     `makefile:4: recipe for target 'segfault' failed`,
79     `make: *** [segfault] Segmentation fault (core dumped)`,
80     `make: *** [exit1] Error 1`,
81     `make: *** [exit2] Error 2`,
82     ]) parser.process(a, app);
83     // dfmt on
84 
85     shouldEqual(app.failed.byKey.array, [TestCase("segfault")]);
86 }