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