File Coverage

Catch.xs
Criterion Covered Total %
statement 139 230 60.4
branch 129 352 36.6
condition n/a
subroutine n/a
pod n/a
total 268 582 46.0


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