File Coverage

Catch.xs
Criterion Covered Total %
statement 141 209 67.4
branch 124 318 38.9
condition n/a
subroutine n/a
pod n/a
total 265 527 50.2


line stmt bran cond sub pod time code
1             #include // this fixes win32 because that is included by breaks unless previously included
2             #define CATCH_CONFIG_RUNNER
3             #define CATCH_CONFIG_DEFAULT_REPORTER "perl"
4             //#define CATCH_CONFIG_ENABLE_BENCHMARKING
5             #include
6             #include
7             #include
8              
9             using namespace Catch;
10             using namespace std;
11              
12 1           static Catch::Session session;
13              
14 100           struct Printer {
15 50           Printer (ostream& _stream, const AssertionStats& _stats)
16 50           : stream(_stream), result(_stats.assertionResult), messages(_stats.infoMessages), itMessage(_stats.infoMessages.begin()) {}
17              
18 50           void print () {
19 50           itMessage = messages.begin();
20              
21 50           switch (result.getResultType()) {
22             case ResultWas::Ok:
23 50           printOriginalExpression();
24 50           printReconstructedExpression();
25 50           printRemainingMessages();
26 50           break;
27             case ResultWas::ExpressionFailed:
28 0           printOriginalExpression();
29 0           printReconstructedExpression();
30 0 0         if (result.isOk()) printIssue(" # TODO");
    0          
    0          
31 0           printRemainingMessages();
32 0           break;
33             case ResultWas::ThrewException:
34 0 0         printIssue("unexpected exception ");
    0          
35 0           printExpressionWas();
36 0           printRemainingMessages();
37 0           break;
38             case ResultWas::FatalErrorCondition:
39 0 0         printIssue("fatal error condition with message:");
    0          
40 0           printMessage();
41 0           printExpressionWas();
42 0           printRemainingMessages();
43 0           break;
44             case ResultWas::DidntThrowException:
45 0 0         printIssue("expected exception, got none");
    0          
46 0           printExpressionWas();
47 0           printRemainingMessages();
48 0           break;
49             case ResultWas::Info:
50 0           stream << "#info";
51 0           printMessage();
52 0           printRemainingMessages();
53 0           break;
54             case ResultWas::Warning:
55 0           stream << "#warning";
56 0           printMessage();
57 0           printRemainingMessages();
58 0           break;
59             case ResultWas::ExplicitFailure:
60 0 0         printIssue("explicitly");
    0          
61 0           printRemainingMessages();
62 0           break;
63             // These cases are here to prevent compiler warnings
64             case ResultWas::Unknown:
65             case ResultWas::FailureBit:
66             case ResultWas::Exception:
67 0           stream << "** unsupported ResultWas (should not happenned) **";
68 0           break;
69             }
70 50           }
71              
72             private:
73 132           static inline void expr_replace (string& expr, const string& c, const string& subs) {
74 132           size_t pos = expr.find(c);
75 135 100         while (pos < expr.length()) {
76 3           expr.replace(pos, c.length(), subs);
77 3           pos = expr.find(c, pos + subs.length());
78             }
79 132           }
80            
81 0           void printIssue (const string& issue) const {
82 0           stream << " " << issue;
83 0           }
84              
85 0           void printExpressionWas () {
86 0 0         if (!result.hasExpression()) return;
87 0           stream << "; expression was:";
88 0           printOriginalExpression();
89             }
90              
91 50           void printOriginalExpression () const {
92 50 50         if (result.hasExpression()) stream << " " << result.getExpression();
    50          
    50          
93 50           }
94            
95 50           void printReconstructedExpression () const {
96 50 50         if (!result.hasExpandedExpression()) return;
    100          
97 33 50         stream << " for: ";
98 83 50         string expr = result.getExpandedExpression();
99             // prevent "str" == "str" splitting into several lines
100 33 50         expr_replace(expr, "\"\r\n==\r\n\"", "\" == \"");
    50          
    50          
101 33 50         expr_replace(expr, "\"\n==\n\"", "\" == \"");
    50          
    50          
102             // replace remaining newlines in text/expressions
103 33 50         expr_replace(expr, "\r", "\\r");
    50          
    50          
104 33 50         expr_replace(expr, "\n", "\\n");
    50          
    50          
105 33 50         stream << expr;
106             }
107              
108 0           void printMessage () {
109 0 0         if (itMessage != messages.end()) {
110 0           stream << " '" << itMessage->message << "'";
111 0           ++itMessage;
112             }
113 0           }
114              
115 50           void printRemainingMessages () {
116 50 50         if (itMessage == messages.cend()) return;
117              
118             // using messages.end() directly (or auto) yields compilation error:
119 0           auto itEnd = messages.cend();
120 0 0         const size_t N = static_cast(std::distance(itMessage, itEnd));
121              
122 0 0         stream << " with " << pluralise( N, "message" ) << ":";
    0          
    0          
    0          
    0          
123              
124 50 0         for (; itMessage != itEnd; ++itMessage) {
125             // If this assertion is a warning ignore any INFO messages
126 0 0         if (itMessage->type != ResultWas::Info) {
127 0 0         stream << "\n# " << itMessage->message;
    0          
128             }
129             }
130             }
131              
132             ostream& stream;
133             AssertionResult const& result;
134             vector messages;
135             vector::const_iterator itMessage;
136             };
137              
138              
139 20 50         struct PerlReporter : IStreamingReporter {
140 247 50         struct Scope {
141             uint32_t count;
142             uint32_t failed;
143             uint32_t depth;
144             string name;
145             string fullname;
146             };
147             static Scope context;
148              
149 0 0         static string getDescription () { return "Reports test results in perl test-harness compatible format"; }
150            
151 5           PerlReporter (const ReporterConfig& _config)
152 5 50         : scope(), sliding_scope(), fatal(), config(_config.fullConfig()), stream(_config.stream())
    50          
153             {
154 5           reporterPrefs.shouldRedirectStdOut = false;
155 5           reporterPrefs.shouldReportAllAssertions = true;
156 5           }
157            
158 54           ReporterPreferences getPreferences () const override { return reporterPrefs; }
159              
160 0           void noMatchingTestCases (const string& spec) override {
161 0           startErrorLine() << "# No test cases matched '" << spec << "'" << endl;
162 0           }
163            
164 0           void skipTest (const TestCaseInfo&) override {}
165            
166 5           void testRunStarting (const TestRunInfo&) override {
167 5           scopes.push_back(context);
168 5           scope = &scopes.back();
169 5           }
170            
171 5           void testRunEnded (const TestRunStats&) override {
172 5           context.count = scope->count;
173 5           context.failed = scope->failed;
174 5           scopes.clear();
175 5           scope = nullptr;
176 5           }
177            
178 5           void testGroupStarting (const GroupInfo&) override {}
179 5           void testGroupEnded (const TestGroupStats&) override {}
180            
181 13           void testCaseStarting (const TestCaseInfo&) override {}
182            
183 13           void testCaseEnded (const TestCaseStats&) override {
184 13 50         if (fatal) {
185 0           commitAssertions();
186 0           sliding_scope = &scopes[1];
187             }
188 13           commitSlidingScope();
189 13           }
190            
191 43           void sectionStarting (const SectionInfo& info) override {
192 43 100         if (sliding_scope && sliding_scope->name == info.name) {
    100          
    100          
193 12           ++sliding_scope;
194 12           return;
195             }
196 31           commitSlidingScope();
197 31           startScope(info);
198             }
199            
200 31           void startScope (const SectionInfo& info) {
201 31 50         startLine();
202 62 100         auto fullname = scope->fullname.length() ? (scope->fullname + " / " + info.name) : info.name;
    50          
    50          
    50          
    100          
    0          
203 31 50         stream << "# Subtest: " << fullname << endl;
    50          
    50          
204 31 50         scopes.push_back({0, 0, scope->depth + 1, info.name, fullname});
    50          
    50          
205 31           scope = &scopes.back();
206 31           }
207            
208 43           void sectionEnded (const SectionStats&) override {
209 43 50         if (fatal) return;
210 43 100         if (!sliding_scope) sliding_scope = scope + 1;
211 43           --sliding_scope;
212 43 100         if (sliding_scope == &scopes[1]) commitAssertions();
213             }
214            
215 44           void commitSlidingScope () {
216 44 100         if (!sliding_scope) return;
217 22           size_t cnt = &scopes.back() - sliding_scope + 1;
218 53 100         while (cnt--) closeCurrentScope();
219 22           sliding_scope = nullptr;
220             }
221            
222 31           void closeCurrentScope () {
223 62 50         auto name = scope->fullname;
224 31           bool failed = scope->failed;
225 31 50         startLine() << "1.." << scope->count << endl;
    50          
    50          
    50          
226 31 50         if (scope->failed) {
227 0 0         startErrorLine() << "# Looks like you failed " << scope->failed << " test of " << scope->count << " at [" << name << "]." << endl;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
228             }
229            
230 31           scopes.pop_back();
231 31 50         if (scopes.empty()) throw "WTF?";
232 31           scope = &scopes.back();
233            
234 31           ++scope->count;
235 31 50         startLine();
236 31 50         if (failed) {
237 0           ++scope->failed;
238 0 0         stream << "not ok";
239             }
240 31 50         else stream << "ok";
241 31 50         stream << " " << scope->count << " - [" << name << "]" << endl;
    50          
    50          
    50          
    50          
    50          
242 31           }
243            
244 143           ostream& startLine () {
245 496 100         for (size_t i = 0; i < scope->depth; ++i) stream << " ";
246 143           return stream;
247             }
248              
249 0           ostream& startErrorLine () {
250 0 0         for (size_t i = 0; i < scope->depth; ++i) std::cerr << " ";
251 0           return std::cerr;
252             }
253              
254 50           void assertionStarting (const AssertionInfo&) override {}
255              
256 50           bool assertionEnded (const AssertionStats& stats) override {
257 100 50         ostringstream s;
258 50 50         Printer(s, stats).print();
    50          
259 50 50         assertions.push_back({stats, s.str()});
    50          
    50          
260 50           return true;
261             }
262            
263 22           void commitAssertions () {
264 72 100         for (auto& row : assertions) {
265 50           auto& stats = row.stats;
266 100 50         auto result = stats.assertionResult;
267             // prevent diagnostic messages from counting
268 50 50         bool is_test = result.getResultType() != ResultWas::Info && result.getResultType() != ResultWas::Warning;
    50          
    50          
    50          
269            
270 50           Colour::Code color = Colour::None;
271 50 50         ostream& ss = result.succeeded() ? startLine() : startErrorLine();
    50          
    50          
    0          
272            
273 50 50         if (is_test) {
274 50           ++scope->count;
275 50 50         if (result.succeeded()) {
    50          
276 50 50         ss << "ok";
277             } else {
278 0           ++scope->failed;
279 0 0         ss << "not ok";
280 0           color = Colour::ResultError;
281             }
282 50 50         ss << " " << scope->count << " -";
    50          
    50          
283             }
284            
285             {
286 100 50         Colour cg(color); (void)cg;
287 50 50         ss << row.expr;
288 50 50         ss << " # at " << result.getSourceInfo();
    50          
    50          
289             }
290            
291 50 50         ss << endl;
292            
293 50 50         if (is_test && !result.succeeded()) {
    50          
    50          
    50          
294 0 0         startErrorLine() << "#\e[1;31m Failed test in section [" << scope->fullname << "] at " << result.getSourceInfo() << "\e[0m" << endl;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
295             }
296             }
297 22           assertions.clear();
298 22           }
299            
300 0           void fatalErrorEncountered (StringRef) override {
301 0           fatal = true;
302 0           }
303            
304             // void benchmarkPreparing (const std::string& ) override {}
305             // void benchmarkStarting (const BenchmarkInfo& ) override {}
306             // void benchmarkEnded (const BenchmarkStats<>& ) override {}
307             // void benchmarkFailed (const std::string& ) override {}
308              
309             private:
310 336 50         struct AData {
311             AssertionStats stats;
312             string expr;
313             };
314            
315             vector scopes;
316             Scope* scope;
317             Scope* sliding_scope;
318             vector assertions;
319             bool fatal;
320             ReporterPreferences reporterPrefs;
321             IConfigPtr config;
322             ostream& stream;
323             };
324              
325 1           PerlReporter::Scope PerlReporter::context;
326            
327 1 50         CATCH_REGISTER_REPORTER("perl", PerlReporter);
    50          
328              
329             MODULE = Test::Catch PACKAGE = Test::Catch
330             PROTOTYPES: DISABLE
331              
332             bool _run (SV* count, SV* failed, int depth, ...) {
333             int err;
334             {
335 10 50         std::vector argv = {"test"};
336            
337 9 100         for (int i = 3; i < items; ++i) {
338 4           SV* arg = ST(i);
339 4 50         if (!SvOK(arg)) continue;
    0          
    0          
340 4 50         argv.push_back(SvPV_nolen(arg));
    0          
    50          
341             }
342            
343 5 50         argv.push_back("-i");
344            
345 5 50         session.useConfigData({});
346 5 50         err = session.applyCommandLine(argv.size(), argv.data());
347             }
348 5 50         if (err) croak("session.applyCommandLine: error %d", err);
349            
350 5 50         PerlReporter::context.count = SvUV(count);
351 5 50         PerlReporter::context.failed = SvUV(failed);
352 5           PerlReporter::context.depth = depth;
353            
354 5           RETVAL = session.run() == 0;
355            
356 5           sv_setuv(count, PerlReporter::context.count);
357 5           sv_setuv(failed, PerlReporter::context.failed);
358             }