1 #include "catch_run_context.h"
2 #include "catch_compiler_capabilities.h"
3 #include "catch_context.h"
4 #include "catch_enforce.h"
5 #include "catch_random_number_generator.h"
6 #include "catch_stream.h"
7 #include "catch_output_redirect.h"
8 
9 #include <cassert>
10 #include <algorithm>
11 #include <sstream>
12 
13 namespace Catch {
14 
15     namespace Generators {
16         struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
17             GeneratorBasePtr m_generator;
18 
GeneratorTrackerCatch::Generators::GeneratorTracker19             GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
20             :   TrackerBase( nameAndLocation, ctx, parent )
21             {}
22             ~GeneratorTracker();
23 
acquireCatch::Generators::GeneratorTracker24             static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) {
25                 std::shared_ptr<GeneratorTracker> tracker;
26 
27                 ITracker& currentTracker = ctx.currentTracker();
28                 // Under specific circumstances, the generator we want
29                 // to acquire is also the current tracker. If this is
30                 // the case, we have to avoid looking through current
31                 // tracker's children, and instead return the current
32                 // tracker.
33                 // A case where this check is important is e.g.
34                 //     for (int i = 0; i < 5; ++i) {
35                 //         int n = GENERATE(1, 2);
36                 //     }
37                 //
38                 // without it, the code above creates 5 nested generators.
39                 if (currentTracker.nameAndLocation() == nameAndLocation) {
40                     auto thisTracker = currentTracker.parent().findChild(nameAndLocation);
41                     assert(thisTracker);
42                     assert(thisTracker->isGeneratorTracker());
43                     tracker = std::static_pointer_cast<GeneratorTracker>(thisTracker);
44                 } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
45                     assert( childTracker );
46                     assert( childTracker->isGeneratorTracker() );
47                     tracker = std::static_pointer_cast<GeneratorTracker>( childTracker );
48                 } else {
49                     tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, &currentTracker );
50                     currentTracker.addChild( tracker );
51                 }
52 
53                 if( !tracker->isComplete() ) {
54                     tracker->open();
55                 }
56 
57                 return *tracker;
58             }
59 
60             // TrackerBase interface
isGeneratorTrackerCatch::Generators::GeneratorTracker61             bool isGeneratorTracker() const override { return true; }
hasGeneratorCatch::Generators::GeneratorTracker62             auto hasGenerator() const -> bool override {
63                 return !!m_generator;
64             }
closeCatch::Generators::GeneratorTracker65             void close() override {
66                 TrackerBase::close();
67                 // If a generator has a child (it is followed by a section)
68                 // and none of its children have started, then we must wait
69                 // until later to start consuming its values.
70                 // This catches cases where `GENERATE` is placed between two
71                 // `SECTION`s.
72                 // **The check for m_children.empty cannot be removed**.
73                 // doing so would break `GENERATE` _not_ followed by `SECTION`s.
74                 const bool should_wait_for_child = [&]() {
75                     // No children -> nobody to wait for
76                     if ( m_children.empty() ) {
77                         return false;
78                     }
79                     // If at least one child started executing, don't wait
80                     if ( std::find_if(
81                              m_children.begin(),
82                              m_children.end(),
83                              []( TestCaseTracking::ITrackerPtr tracker ) {
84                                  return tracker->hasStarted();
85                              } ) != m_children.end() ) {
86                         return false;
87                     }
88 
89                     // No children have started. We need to check if they _can_
90                     // start, and thus we should wait for them, or they cannot
91                     // start (due to filters), and we shouldn't wait for them
92                     auto* parent = m_parent;
93                     // This is safe: there is always at least one section
94                     // tracker in a test case tracking tree
95                     while ( !parent->isSectionTracker() ) {
96                         parent = &( parent->parent() );
97                     }
98                     assert( parent &&
99                             "Missing root (test case) level section" );
100 
101                     auto const& parentSection =
102                         static_cast<SectionTracker&>( *parent );
103                     auto const& filters = parentSection.getFilters();
104                     // No filters -> no restrictions on running sections
105                     if ( filters.empty() ) {
106                         return true;
107                     }
108 
109                     for ( auto const& child : m_children ) {
110                         if ( child->isSectionTracker() &&
111                              std::find( filters.begin(),
112                                         filters.end(),
113                                         static_cast<SectionTracker&>( *child )
114                                             .trimmedName() ) !=
115                                  filters.end() ) {
116                             return true;
117                         }
118                     }
119                     return false;
120                 }();
121 
122                 // This check is a bit tricky, because m_generator->next()
123                 // has a side-effect, where it consumes generator's current
124                 // value, but we do not want to invoke the side-effect if
125                 // this generator is still waiting for any child to start.
126                 if ( should_wait_for_child ||
127                      ( m_runState == CompletedSuccessfully &&
128                        m_generator->next() ) ) {
129                     m_children.clear();
130                     m_runState = Executing;
131                 }
132             }
133 
134             // IGeneratorTracker interface
getGeneratorCatch::Generators::GeneratorTracker135             auto getGenerator() const -> GeneratorBasePtr const& override {
136                 return m_generator;
137             }
setGeneratorCatch::Generators::GeneratorTracker138             void setGenerator( GeneratorBasePtr&& generator ) override {
139                 m_generator = std::move( generator );
140             }
141         };
~GeneratorTracker()142         GeneratorTracker::~GeneratorTracker() {}
143     }
144 
RunContext(IConfigPtr const & _config,IStreamingReporterPtr && reporter)145     RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter)
146     :   m_runInfo(_config->name()),
147         m_context(getCurrentMutableContext()),
148         m_config(_config),
149         m_reporter(std::move(reporter)),
150         m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
151         m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
152     {
153         m_context.setRunner(this);
154         m_context.setConfig(m_config);
155         m_context.setResultCapture(this);
156         m_reporter->testRunStarting(m_runInfo);
157     }
158 
~RunContext()159     RunContext::~RunContext() {
160         m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting()));
161     }
162 
testGroupStarting(std::string const & testSpec,std::size_t groupIndex,std::size_t groupsCount)163     void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) {
164         m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount));
165     }
166 
testGroupEnded(std::string const & testSpec,Totals const & totals,std::size_t groupIndex,std::size_t groupsCount)167     void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) {
168         m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting()));
169     }
170 
runTest(TestCase const & testCase)171     Totals RunContext::runTest(TestCase const& testCase) {
172         Totals prevTotals = m_totals;
173 
174         std::string redirectedCout;
175         std::string redirectedCerr;
176 
177         auto const& testInfo = testCase.getTestCaseInfo();
178 
179         m_reporter->testCaseStarting(testInfo);
180 
181         m_activeTestCase = &testCase;
182 
183 
184         ITracker& rootTracker = m_trackerContext.startRun();
185         assert(rootTracker.isSectionTracker());
186         static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun());
187         do {
188             m_trackerContext.startCycle();
189             m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo));
190             runCurrentTest(redirectedCout, redirectedCerr);
191         } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
192 
193         Totals deltaTotals = m_totals.delta(prevTotals);
194         if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) {
195             deltaTotals.assertions.failed++;
196             deltaTotals.testCases.passed--;
197             deltaTotals.testCases.failed++;
198         }
199         m_totals.testCases += deltaTotals.testCases;
200         m_reporter->testCaseEnded(TestCaseStats(testInfo,
201                                   deltaTotals,
202                                   redirectedCout,
203                                   redirectedCerr,
204                                   aborting()));
205 
206         m_activeTestCase = nullptr;
207         m_testCaseTracker = nullptr;
208 
209         return deltaTotals;
210     }
211 
config() const212     IConfigPtr RunContext::config() const {
213         return m_config;
214     }
215 
reporter() const216     IStreamingReporter& RunContext::reporter() const {
217         return *m_reporter;
218     }
219 
assertionEnded(AssertionResult const & result)220     void RunContext::assertionEnded(AssertionResult const & result) {
221         if (result.getResultType() == ResultWas::Ok) {
222             m_totals.assertions.passed++;
223             m_lastAssertionPassed = true;
224         } else if (!result.isOk()) {
225             m_lastAssertionPassed = false;
226             if( m_activeTestCase->getTestCaseInfo().okToFail() )
227                 m_totals.assertions.failedButOk++;
228             else
229                 m_totals.assertions.failed++;
230         }
231         else {
232             m_lastAssertionPassed = true;
233         }
234 
235         // We have no use for the return value (whether messages should be cleared), because messages were made scoped
236         // and should be let to clear themselves out.
237         static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
238 
239         if (result.getResultType() != ResultWas::Warning)
240             m_messageScopes.clear();
241 
242         // Reset working state
243         resetAssertionInfo();
244         m_lastResult = result;
245     }
resetAssertionInfo()246     void RunContext::resetAssertionInfo() {
247         m_lastAssertionInfo.macroName = StringRef();
248         m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
249     }
250 
sectionStarted(SectionInfo const & sectionInfo,Counts & assertions)251     bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) {
252         ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo));
253         if (!sectionTracker.isOpen())
254             return false;
255         m_activeSections.push_back(&sectionTracker);
256 
257         m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
258 
259         m_reporter->sectionStarting(sectionInfo);
260 
261         assertions = m_totals.assertions;
262 
263         return true;
264     }
acquireGeneratorTracker(StringRef generatorName,SourceLineInfo const & lineInfo)265     auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
266         using namespace Generators;
267         GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext,
268                                                               TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) );
269         m_lastAssertionInfo.lineInfo = lineInfo;
270         return tracker;
271     }
272 
testForMissingAssertions(Counts & assertions)273     bool RunContext::testForMissingAssertions(Counts& assertions) {
274         if (assertions.total() != 0)
275             return false;
276         if (!m_config->warnAboutMissingAssertions())
277             return false;
278         if (m_trackerContext.currentTracker().hasChildren())
279             return false;
280         m_totals.assertions.failed++;
281         assertions.failed++;
282         return true;
283     }
284 
sectionEnded(SectionEndInfo const & endInfo)285     void RunContext::sectionEnded(SectionEndInfo const & endInfo) {
286         Counts assertions = m_totals.assertions - endInfo.prevAssertions;
287         bool missingAssertions = testForMissingAssertions(assertions);
288 
289         if (!m_activeSections.empty()) {
290             m_activeSections.back()->close();
291             m_activeSections.pop_back();
292         }
293 
294         m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
295         m_messages.clear();
296         m_messageScopes.clear();
297     }
298 
sectionEndedEarly(SectionEndInfo const & endInfo)299     void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
300         if (m_unfinishedSections.empty())
301             m_activeSections.back()->fail();
302         else
303             m_activeSections.back()->close();
304         m_activeSections.pop_back();
305 
306         m_unfinishedSections.push_back(endInfo);
307     }
308 
309 #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
benchmarkPreparing(std::string const & name)310     void RunContext::benchmarkPreparing(std::string const& name) {
311         m_reporter->benchmarkPreparing(name);
312     }
benchmarkStarting(BenchmarkInfo const & info)313     void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
314         m_reporter->benchmarkStarting( info );
315     }
benchmarkEnded(BenchmarkStats<> const & stats)316     void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
317         m_reporter->benchmarkEnded( stats );
318     }
benchmarkFailed(std::string const & error)319     void RunContext::benchmarkFailed(std::string const & error) {
320         m_reporter->benchmarkFailed(error);
321     }
322 #endif // CATCH_CONFIG_ENABLE_BENCHMARKING
323 
pushScopedMessage(MessageInfo const & message)324     void RunContext::pushScopedMessage(MessageInfo const & message) {
325         m_messages.push_back(message);
326     }
327 
popScopedMessage(MessageInfo const & message)328     void RunContext::popScopedMessage(MessageInfo const & message) {
329         m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
330     }
331 
emplaceUnscopedMessage(MessageBuilder const & builder)332     void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) {
333         m_messageScopes.emplace_back( builder );
334     }
335 
getCurrentTestName() const336     std::string RunContext::getCurrentTestName() const {
337         return m_activeTestCase
338             ? m_activeTestCase->getTestCaseInfo().name
339             : std::string();
340     }
341 
getLastResult() const342     const AssertionResult * RunContext::getLastResult() const {
343         return &(*m_lastResult);
344     }
345 
exceptionEarlyReported()346     void RunContext::exceptionEarlyReported() {
347         m_shouldReportUnexpected = false;
348     }
349 
handleFatalErrorCondition(StringRef message)350     void RunContext::handleFatalErrorCondition( StringRef message ) {
351         // First notify reporter that bad things happened
352         m_reporter->fatalErrorEncountered(message);
353 
354         // Don't rebuild the result -- the stringification itself can cause more fatal errors
355         // Instead, fake a result data.
356         AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
357         tempResult.message = static_cast<std::string>(message);
358         AssertionResult result(m_lastAssertionInfo, tempResult);
359 
360         assertionEnded(result);
361 
362         handleUnfinishedSections();
363 
364         // Recreate section for test case (as we will lose the one that was in scope)
365         auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
366         SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
367 
368         Counts assertions;
369         assertions.failed = 1;
370         SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false);
371         m_reporter->sectionEnded(testCaseSectionStats);
372 
373         auto const& testInfo = m_activeTestCase->getTestCaseInfo();
374 
375         Totals deltaTotals;
376         deltaTotals.testCases.failed = 1;
377         deltaTotals.assertions.failed = 1;
378         m_reporter->testCaseEnded(TestCaseStats(testInfo,
379                                   deltaTotals,
380                                   std::string(),
381                                   std::string(),
382                                   false));
383         m_totals.testCases.failed++;
384         testGroupEnded(std::string(), m_totals, 1, 1);
385         m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
386     }
387 
lastAssertionPassed()388     bool RunContext::lastAssertionPassed() {
389          return m_lastAssertionPassed;
390     }
391 
assertionPassed()392     void RunContext::assertionPassed() {
393         m_lastAssertionPassed = true;
394         ++m_totals.assertions.passed;
395         resetAssertionInfo();
396         m_messageScopes.clear();
397     }
398 
aborting() const399     bool RunContext::aborting() const {
400         return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter());
401     }
402 
runCurrentTest(std::string & redirectedCout,std::string & redirectedCerr)403     void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {
404         auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
405         SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
406         m_reporter->sectionStarting(testCaseSection);
407         Counts prevAssertions = m_totals.assertions;
408         double duration = 0;
409         m_shouldReportUnexpected = true;
410         m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal };
411 
412         seedRng(*m_config);
413 
414         Timer timer;
415         CATCH_TRY {
416             if (m_reporter->getPreferences().shouldRedirectStdOut) {
417 #if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
418                 RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
419 
420                 timer.start();
421                 invokeActiveTestCase();
422 #else
423                 OutputRedirect r(redirectedCout, redirectedCerr);
424                 timer.start();
425                 invokeActiveTestCase();
426 #endif
427             } else {
428                 timer.start();
429                 invokeActiveTestCase();
430             }
431             duration = timer.getElapsedSeconds();
432         } CATCH_CATCH_ANON (TestFailureException&) {
433             // This just means the test was aborted due to failure
434         } CATCH_CATCH_ALL {
435             // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
436             // are reported without translation at the point of origin.
437             if( m_shouldReportUnexpected ) {
438                 AssertionReaction dummyReaction;
439                 handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction );
440             }
441         }
442         Counts assertions = m_totals.assertions - prevAssertions;
443         bool missingAssertions = testForMissingAssertions(assertions);
444 
445         m_testCaseTracker->close();
446         handleUnfinishedSections();
447         m_messages.clear();
448         m_messageScopes.clear();
449 
450         SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
451         m_reporter->sectionEnded(testCaseSectionStats);
452     }
453 
invokeActiveTestCase()454     void RunContext::invokeActiveTestCase() {
455         FatalConditionHandlerGuard _(&m_fatalConditionhandler);
456         m_activeTestCase->invoke();
457     }
458 
handleUnfinishedSections()459     void RunContext::handleUnfinishedSections() {
460         // If sections ended prematurely due to an exception we stored their
461         // infos here so we can tear them down outside the unwind process.
462         for (auto it = m_unfinishedSections.rbegin(),
463              itEnd = m_unfinishedSections.rend();
464              it != itEnd;
465              ++it)
466             sectionEnded(*it);
467         m_unfinishedSections.clear();
468     }
469 
handleExpr(AssertionInfo const & info,ITransientExpression const & expr,AssertionReaction & reaction)470     void RunContext::handleExpr(
471         AssertionInfo const& info,
472         ITransientExpression const& expr,
473         AssertionReaction& reaction
474     ) {
475         m_reporter->assertionStarting( info );
476 
477         bool negated = isFalseTest( info.resultDisposition );
478         bool result = expr.getResult() != negated;
479 
480         if( result ) {
481             if (!m_includeSuccessfulResults) {
482                 assertionPassed();
483             }
484             else {
485                 reportExpr(info, ResultWas::Ok, &expr, negated);
486             }
487         }
488         else {
489             reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
490             populateReaction( reaction );
491         }
492     }
reportExpr(AssertionInfo const & info,ResultWas::OfType resultType,ITransientExpression const * expr,bool negated)493     void RunContext::reportExpr(
494             AssertionInfo const &info,
495             ResultWas::OfType resultType,
496             ITransientExpression const *expr,
497             bool negated ) {
498 
499         m_lastAssertionInfo = info;
500         AssertionResultData data( resultType, LazyExpression( negated ) );
501 
502         AssertionResult assertionResult{ info, data };
503         assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
504 
505         assertionEnded( assertionResult );
506     }
507 
handleMessage(AssertionInfo const & info,ResultWas::OfType resultType,StringRef const & message,AssertionReaction & reaction)508     void RunContext::handleMessage(
509             AssertionInfo const& info,
510             ResultWas::OfType resultType,
511             StringRef const& message,
512             AssertionReaction& reaction
513     ) {
514         m_reporter->assertionStarting( info );
515 
516         m_lastAssertionInfo = info;
517 
518         AssertionResultData data( resultType, LazyExpression( false ) );
519         data.message = static_cast<std::string>(message);
520         AssertionResult assertionResult{ m_lastAssertionInfo, data };
521         assertionEnded( assertionResult );
522         if( !assertionResult.isOk() )
523             populateReaction( reaction );
524     }
handleUnexpectedExceptionNotThrown(AssertionInfo const & info,AssertionReaction & reaction)525     void RunContext::handleUnexpectedExceptionNotThrown(
526             AssertionInfo const& info,
527             AssertionReaction& reaction
528     ) {
529         handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction);
530     }
531 
handleUnexpectedInflightException(AssertionInfo const & info,std::string const & message,AssertionReaction & reaction)532     void RunContext::handleUnexpectedInflightException(
533             AssertionInfo const& info,
534             std::string const& message,
535             AssertionReaction& reaction
536     ) {
537         m_lastAssertionInfo = info;
538 
539         AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
540         data.message = message;
541         AssertionResult assertionResult{ info, data };
542         assertionEnded( assertionResult );
543         populateReaction( reaction );
544     }
545 
populateReaction(AssertionReaction & reaction)546     void RunContext::populateReaction( AssertionReaction& reaction ) {
547         reaction.shouldDebugBreak = m_config->shouldDebugBreak();
548         reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal);
549     }
550 
handleIncomplete(AssertionInfo const & info)551     void RunContext::handleIncomplete(
552             AssertionInfo const& info
553     ) {
554         m_lastAssertionInfo = info;
555 
556         AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
557         data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
558         AssertionResult assertionResult{ info, data };
559         assertionEnded( assertionResult );
560     }
handleNonExpr(AssertionInfo const & info,ResultWas::OfType resultType,AssertionReaction & reaction)561     void RunContext::handleNonExpr(
562             AssertionInfo const &info,
563             ResultWas::OfType resultType,
564             AssertionReaction &reaction
565     ) {
566         m_lastAssertionInfo = info;
567 
568         AssertionResultData data( resultType, LazyExpression( false ) );
569         AssertionResult assertionResult{ info, data };
570         assertionEnded( assertionResult );
571 
572         if( !assertionResult.isOk() )
573             populateReaction( reaction );
574     }
575 
576 
getResultCapture()577     IResultCapture& getResultCapture() {
578         if (auto* capture = getCurrentContext().getResultCapture())
579             return *capture;
580         else
581             CATCH_INTERNAL_ERROR("No result capture instance");
582     }
583 
seedRng(IConfig const & config)584     void seedRng(IConfig const& config) {
585         if (config.rngSeed() != 0) {
586             std::srand(config.rngSeed());
587             rng().seed(config.rngSeed());
588         }
589     }
590 
rngSeed()591     unsigned int rngSeed() {
592         return getCurrentContext().getConfig()->rngSeed();
593     }
594 
595 }
596