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