#define DPL_TEST_RUNNER_H
#include <chrono>
+ #include <cstdlib>
#include <cstring>
#include <exception>
#include <iostream>
#include <dpl/colors.h>
#include <dpl/gdbbacktrace.h>
#include <dpl/singleton.h>
+#include <dpl/test/performance_result.h>
+ #include <dpl/test/test_exception.h>
+ #include <dpl/test/test_failed.h>
+ #include <dpl/test/test_ignored.h>
+#include <dpl/test/test_result.h>
#include <dpl/test/test_results_collector.h>
namespace DPL {
m_currentTestCase(nullptr)
, m_terminate(false)
, m_allowChildLogs(false)
+ , m_deferDeepness(0U)
+ , m_firstDeferredExceptionType(DeferredExceptionType::DEFERRED_FAILED)
{}
- void beginPerformanceTestTime(std::chrono::system_clock::duration maxTimeInMicroseconds);
- void endPerformanceTestTime();
- void getCurrentTestCasePerformanceResult(bool& isPerformanceTest,
- std::chrono::system_clock::duration& result,
- std::chrono::system_clock::duration& resultMax);
- void setCurrentTestCasePerformanceResult(bool isPerformanceTest,
- std::chrono::system_clock::duration result,
- std::chrono::system_clock::duration resultMax);
+ void beginPerformance(std::chrono::system_clock::duration maxDurationInMicroseconds);
+ void endPerformance();
+ void setCurrentTestCasePerformanceResult(const PerformanceResultPtr &performance);
+ ConstPerformanceResultPtr getCurrentTestCasePerformanceResult();
void addFailReason(const std::string &reason);
{
std::string name;
TestCase proc;
-
- bool m_isPerformanceTest;
- std::chrono::system_clock::time_point m_performanceTestStartTime;
- std::chrono::system_clock::duration m_performanceTestDurationTime;
- std::chrono::system_clock::duration m_performanceMaxTime;
+ PerformanceResultPtr performance;
bool operator <(const TestCaseStruct &other) const
{
TestCaseStruct(const std::string &n, TestCase p) :
name(n),
- proc(p),
- m_isPerformanceTest(false)
+ proc(p)
{}
};
bool filterByXML(std::map<std::string, bool> & casesMap);
void normalizeXMLTag(std::string& str, const std::string& testcase);
- enum Status { FAILED, IGNORED, PASS };
-
- Status RunTestCase(const TestCaseStruct& testCase);
+ void RunTestCase(const TestCaseStruct& testCase);
void setCurrentTestCase(TestCaseStruct* testCase);
TestCaseStruct *getCurrentTestCase();
std::string getConcatedFailReason(const std::string &reason);
- void CollectResult(const std::string& id,
- const TestResultsCollectorBase::FailStatus status
- = TestResultsCollectorBase::FailStatus::NONE,
- const std::string& reason = std::string(),
- const bool& isPerformanceTest = false,
- const std::chrono::system_clock::duration& performanceTime = std::chrono::microseconds::zero(),
- const std::chrono::system_clock::duration& performanceMaxTime = std::chrono::microseconds::zero());
+ void CollectResult(const std::string& id, const TestResult &result);
public:
- class TestFailed
- {
- private:
- std::string m_message;
-
- public:
- TestFailed()
- {}
-
- //! \brief Failed test message creator
- //!
- //! \param[in] aTest string for tested expression
- //! \param[in] aFile source file name
- //! \param[in] aLine source file line
- //! \param[in] aMessage error message
- TestFailed(const char* aTest,
- const char* aFile,
- int aLine,
- const std::string &aMessage);
-
- TestFailed(const std::string &message);
-
- std::string GetMessage() const
- {
- return m_message;
- }
- };
-
- class Ignored
- {
- private:
- std::string m_message;
-
- public:
- Ignored()
- {}
-
- Ignored(const std::string &message) :
- m_message(message)
- {}
-
- std::string GetMessage() const
- {
- return m_message;
- }
- };
-
void MarkAssertion();
void RegisterTest(const char *testName, TestCase proc);
// The runner will terminate as soon as possible (after current test).
void Terminate();
bool GetAllowChildLogs();
+
+ void deferFailedException(const DPL::Test::TestFailed &ex);
+ void deferIgnoredException(const DPL::Test::TestIgnored &ex);
+ void deferBegin();
+ void deferEnd();
+
+ private:
+ std::vector<std::string> m_deferredExceptionsMessages;
+ std::size_t m_deferDeepness;
+ enum DeferredExceptionType {
+ DEFERRED_FAILED,
+ DEFERRED_IGNORED,
+ } m_firstDeferredExceptionType;
+ DPL::Test::TestFailed m_firstDeferredFail;
+ DPL::Test::TestIgnored m_firstDeferredIgnore;
};
typedef DPL::Singleton<TestRunner> TestRunnerSingleton;
{ \
std::ostringstream assertMsg; \
assertMsg << message << DPL::gdbbacktrace(); \
- DPL::Test::TestRunner::TestFailed e(#test, \
- __FILE__, \
- __LINE__, \
- assertMsg.str()); \
+ DPL::Test::TestFailed e(#test, \
+ __FILE__, \
+ __LINE__, \
+ assertMsg.str()); \
if (!std::uncaught_exception()) \
throw e; \
DPL::Test::TestRunnerSingleton::Instance().addFailReason(e.GetMessage()); \
if (!assertMsg.str().empty()) \
assertMsg << ". "; \
assertMsg << err << DPL::gdbbacktrace(); \
- DPL::Test::TestRunner::TestFailed e(#test, \
- __FILE__, \
- __LINE__, \
- assertMsg.str()); \
+ DPL::Test::TestFailed e(#test, \
+ __FILE__, \
+ __LINE__, \
+ assertMsg.str()); \
if (!std::uncaught_exception()) \
throw e; \
DPL::Test::TestRunnerSingleton::Instance().addFailReason(e.GetMessage()); \
* body.
*/
- #define RUNNER_IGNORED_MSG(message) \
- do \
- { \
- std::ostringstream assertMsg; \
- assertMsg << message; \
- throw DPL::Test::TestRunner::Ignored(assertMsg.str()); \
+ #define RUNNER_IGNORED_MSG(message) \
+ do \
+ { \
+ std::ostringstream assertMsg; \
+ assertMsg << message; \
+ throw DPL::Test::TestIgnored(assertMsg.str()); \
} while (0)
/**
#define RUNNER_PERF_TEST_BEGIN(maxTime) \
do { \
- DPL::Test::TestRunnerSingleton::Instance().beginPerformanceTestTime( \
+ DPL::Test::TestRunnerSingleton::Instance().beginPerformance( \
std::chrono::microseconds{static_cast<long long int>(maxTime*1000000.0)}); \
} while (0)
-#define RUNNER_PERF_TEST_END() \
- do { \
- DPL::Test::TestRunnerSingleton::Instance().endPerformanceTestTime(); \
+#define RUNNER_PERF_TEST_END() \
+ do { \
+ DPL::Test::TestRunnerSingleton::Instance().endPerformance(); \
} while (0)
/**
<< DPL::Colors::Text::RED_END << std::endl; \
} while (0)
+ /**
+ * DEFER MACROS
+ *
+ * Use them to defer fails and ignores in test cases.
+ * Some code constructions disallow to throw. Such places can be surrounded
+ * with RUNNER_DEFER_SCOPE macro. RUNNER_DEFER_TRYCATCH macro can be used to catch possibly thrown
+ * exceptions within such scope. Possibly catched exceptions will be rethrown
+ * when leaving RUNNER_DEFER_SCOPE scope.
+ * Macros can be safely nested.
+ */
+
+
+ #define RUNNER_DEFER_TRYCATCH(scope) \
+ do { \
+ try \
+ { \
+ scope \
+ } \
+ catch (const DPL::Test::TestFailed &ex) \
+ { \
+ DPL::Test::TestRunnerSingleton::Instance().deferFailedException(ex); \
+ } \
+ catch (const DPL::Test::TestIgnored &ex) \
+ { \
+ DPL::Test::TestRunnerSingleton::Instance().deferIgnoredException(ex); \
+ } \
+ } while (0) \
+
+ #define RUNNER_DEFER_SCOPE(scope) \
+ do { \
+ DPL::Test::TestRunnerSingleton::Instance().deferBegin(); \
+ scope \
+ DPL::Test::TestRunnerSingleton::Instance().deferEnd(); \
+ } while (0) \
+
#endif // DPL_TEST_RUNNER_H
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- /*
+ /**
* @file test_runner.cpp
* @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
* @author Lukasz Wrzosek (l.wrzosek@samsung.com)
* @brief This file is the implementation file of test runner
*/
#include <stddef.h>
+ #include <dpl/test/test_failed.h>
+ #include <dpl/test/test_ignored.h>
#include <dpl/test/test_runner.h>
#include <dpl/test/test_results_collector.h>
#include <dpl/exception.h>
#include <algorithm>
#include <cstdio>
#include <memory.h>
- #include <libgen.h>
- #include <cstring>
- #include <cstdlib>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
namespace DPL {
namespace Test {
- namespace // anonymous
- {
- std::string BaseName(std::string aPath)
- {
- ScopedFree<char> path(strdup(aPath.c_str()));
- if (nullptr == path.Get()) {
- throw std::bad_alloc();
- }
- char* baseName = basename(path.Get());
- std::string retValue = baseName;
- return retValue;
- }
- } // namespace anonymous
-
- //! \brief Failed test message creator
- //!
- //! \param[in] aTest string for tested expression
- //! \param[in] aFile source file name
- //! \param[in] aLine source file line
- //! \param[in] aMessage error message
- TestRunner::TestFailed::TestFailed(const char* aTest,
- const char* aFile,
- int aLine,
- const std::string &aMessage)
- {
- std::ostringstream assertMsg;
- assertMsg << "[" << BaseName(aFile) << ":" << aLine
- << "] Assertion failed ("
- << aTest << ") " << aMessage;
- m_message = assertMsg.str();
- }
-
- TestRunner::TestFailed::TestFailed(const std::string &message)
- {
- m_message = message;
- }
void TestRunner::RegisterTest(const char *testName, TestCase proc)
{
return true;
}
-TestRunner::Status TestRunner::RunTestCase(const TestCaseStruct& testCase)
+void TestRunner::RunTestCase(const TestCaseStruct& testCase)
{
setCurrentTestCase(&(const_cast<TestCaseStruct &>(testCase)));
+ m_deferDeepness = 0U;
try {
testCase.proc();
} catch (const TestFailed &e) {
// Simple test failure
CollectResult(testCase.name,
- TestResultsCollectorBase::FailStatus::FAILED,
- getConcatedFailReason(e.GetMessage()));
+ TestResult(TestResult::FailStatus::FAILED,
+ getConcatedFailReason(e.GetMessage())));
setCurrentTestCase(nullptr);
- return FAILED;
+ return;
- } catch (const Ignored &e) {
+ } catch (const TestIgnored &e) {
if (m_runIgnored) {
// Simple test have to be implemented
CollectResult(testCase.name,
- TestResultsCollectorBase::FailStatus::IGNORED,
- e.GetMessage());
+ TestResult(TestResult::FailStatus::IGNORED, e.GetMessage()));
}
setCurrentTestCase(nullptr);
- return IGNORED;
+ return;
} catch (const std::exception &) {
// std exception failure
CollectResult(testCase.name,
- TestResultsCollectorBase::FailStatus::FAILED,
- "std exception");
+ TestResult(TestResult::FailStatus::FAILED, "std exception"));
setCurrentTestCase(nullptr);
- return FAILED;
+ return;
} catch (...) {
// Unknown exception failure
CollectResult(testCase.name,
- TestResultsCollectorBase::FailStatus::FAILED,
- "unknown exception");
-
+ TestResult(TestResult::FailStatus::FAILED, "unknown exception"));
setCurrentTestCase(nullptr);
- return FAILED;
+ return;
}
+ // Everything OK
CollectResult(testCase.name,
- TestResultsCollectorBase::FailStatus::NONE,
- "",
- testCase.m_isPerformanceTest,
- testCase.m_performanceTestDurationTime,
- testCase.m_performanceMaxTime);
+ TestResult(TestResult::FailStatus::NONE,
+ std::string(),
+ testCase.performance));
setCurrentTestCase(nullptr);
-
- // Everything OK
- return PASS;
}
void TestRunner::RunTests()
m_currentTestCase = testCase;
}
-void TestRunner::beginPerformanceTestTime(std::chrono::system_clock::duration maxTimeInMicroseconds)
+void TestRunner::beginPerformance(std::chrono::system_clock::duration maxDurationInMicroseconds)
{
TestCaseStruct* testCase = getCurrentTestCase();
if (!testCase)
return;
- testCase->m_isPerformanceTest = true;
- testCase->m_performanceMaxTime = maxTimeInMicroseconds;
- testCase->m_performanceTestStartTime = std::chrono::system_clock::now();
-
- // Set result to 0 microseconds. Display 0ms result when end macro is missing.
- testCase->m_performanceTestDurationTime = std::chrono::microseconds::zero();
+ if (!testCase->performance)
+ testCase->performance.reset(new PerformanceResult(maxDurationInMicroseconds));
}
-void TestRunner::endPerformanceTestTime()
+void TestRunner::endPerformance()
{
TestCaseStruct* testCase = getCurrentTestCase();
if (!testCase)
return;
- testCase->m_performanceTestDurationTime = std::chrono::system_clock::now() -
- testCase->m_performanceTestStartTime;
+ testCase->performance->Finish();
}
-void TestRunner::getCurrentTestCasePerformanceResult(bool& isPerformanceTest,
- std::chrono::system_clock::duration& result,
- std::chrono::system_clock::duration& resultMax)
+ConstPerformanceResultPtr TestRunner::getCurrentTestCasePerformanceResult()
{
TestCaseStruct* testCase = getCurrentTestCase();
- if (!testCase || !(testCase->m_isPerformanceTest)){
- isPerformanceTest = false;
- return;
- }
+ if (!testCase)
+ return nullptr;
- isPerformanceTest = testCase->m_isPerformanceTest;
- result = testCase->m_performanceTestDurationTime;
- resultMax = testCase->m_performanceMaxTime;
+ return testCase->performance;
}
-void TestRunner::setCurrentTestCasePerformanceResult(bool isPerformanceTest,
- std::chrono::system_clock::duration result,
- std::chrono::system_clock::duration resultMax)
+void TestRunner::setCurrentTestCasePerformanceResult(const PerformanceResultPtr &performance)
{
TestCaseStruct* testCase = getCurrentTestCase();
if (!testCase)
return;
- testCase->m_isPerformanceTest = isPerformanceTest;
- testCase->m_performanceTestDurationTime = result;
- testCase->m_performanceMaxTime = resultMax;
+ testCase->performance = performance;
}
void TestRunner::addFailReason(const std::string &reason)
return reason + ret;
}
-void TestRunner::CollectResult(
- const std::string& id,
- const TestResultsCollectorBase::FailStatus status,
- const std::string& reason,
- const bool& isPerformanceTest,
- const std::chrono::system_clock::duration& performanceTestDurationTime,
- const std::chrono::system_clock::duration& performanceMaxTime)
+void TestRunner::CollectResult(const std::string& id, const TestResult& result)
{
std::for_each(m_collectors.begin(),
m_collectors.end(),
[&](const TestResultsCollectors::value_type & collector)
{
- collector.second->CollectResult(id,
- status,
- reason,
- isPerformanceTest,
- performanceTestDurationTime,
- performanceMaxTime);
+ collector.second->CollectResult(id, result);
});
}
return m_allowChildLogs;
}
+ void TestRunner::deferFailedException(const DPL::Test::TestFailed &ex)
+ {
+ if (m_deferDeepness <= 0)
+ throw ex;
+
+ if (m_deferredExceptionsMessages.empty()) {
+ m_firstDeferredFail = ex;
+ m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_FAILED;
+ }
+ m_deferredExceptionsMessages.push_back(ex.GetMessage());
+ }
+
+ void TestRunner::deferIgnoredException(const DPL::Test::TestIgnored &ex)
+ {
+ if (m_deferDeepness <= 0)
+ throw ex;
+
+ if (m_deferredExceptionsMessages.empty()) {
+ m_firstDeferredIgnore = ex;
+ m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_IGNORED;
+ }
+ m_deferredExceptionsMessages.push_back(ex.GetMessage());
+ }
+
+ void TestRunner::deferBegin()
+ {
+ m_deferDeepness++;
+ }
+
+ void TestRunner::deferEnd()
+ {
+ if (m_deferDeepness > 0)
+ m_deferDeepness--;
+
+ if (m_deferDeepness > 0)
+ return;
+
+ bool oops = std::uncaught_exception();
+ size_t additionalExceptions = oops ? 0 : 1;
+ for (size_t i = additionalExceptions; i < m_deferredExceptionsMessages.size(); ++i)
+ addFailReason(m_deferredExceptionsMessages[i]);
+
+ if (!oops && !m_deferredExceptionsMessages.empty())
+ {
+ m_deferredExceptionsMessages.clear();
+ switch (m_firstDeferredExceptionType) {
+ case DeferredExceptionType::DEFERRED_FAILED:
+ throw m_firstDeferredFail;
+ case DeferredExceptionType::DEFERRED_IGNORED:
+ throw m_firstDeferredIgnore;
+ }
+ }
+ m_deferredExceptionsMessages.clear();
}
+
+ } // namespace Test
} // namespace DPL
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- /*
+ /**
* @file test_runner_child.cpp
* @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
* @version 1.0
* @brief This file is the implementation file of test runner
*/
#include <stddef.h>
+ #include <dpl/test/test_failed.h>
+ #include <dpl/test/test_ignored.h>
#include <dpl/test/test_runner.h>
#include <dpl/test/test_runner_child.h>
#include <dpl/test/test_results_collector.h>
const int CHILD_TEST_PASS = 1;
const int CHILD_TEST_IGNORED = 2;
-const int MSG_TYPE_MESSAGE = 0; // sizeof(Message) + Message
-const int MSG_TYPE_PERF_TIME = 1; // perfTime + maxTime
-
int closeOutput() {
int devnull;
int retcode = -1;
std::ostringstream output;
output << toBinaryString(code);
- output << toBinaryString(MSG_TYPE_MESSAGE);
output << toBinaryString(static_cast<int>(message.size()));
output << message;
return SUCCESS;
}
-PipeWrapper::Status PipeWrapper::sendTime(int code,
- std::chrono::system_clock::duration time,
- std::chrono::system_clock::duration timeMax)
+PipeWrapper::Status PipeWrapper::sendPerformance(const ConstPerformanceResultPtr &performance)
{
+ int foo = 0;
if (m_pipefd[1] == PIPE_CLOSED) {
return ERROR;
}
+ if (!performance)
+ return writeHelp(&foo, sizeof(int));
- std::ostringstream output;
- output << toBinaryString(code);
- output << toBinaryString(MSG_TYPE_PERF_TIME);
- output << toBinaryString(time);
- output << toBinaryString(timeMax);
-
- std::string binary = output.str();
+ std::string binary = performance->ToBinaryString();
int size = binary.size();
if ((writeHelp(&size,
}
PipeWrapper::Status PipeWrapper::receive(int &code,
- int &msgType,
std::string &data,
- std::chrono::system_clock::duration &time,
- std::chrono::system_clock::duration &timeMax,
+ PerformanceResultPtr &performance,
time_t deadline)
{
if (m_pipefd[0] == PIPE_CLOSED) {
queue.AppendCopy(&buffer[0], size);
queue.FlattenConsume(&code, sizeof(int));
- queue.FlattenConsume(&msgType, sizeof(int));
-
- switch (msgType) {
- case MSG_TYPE_MESSAGE:
- queue.FlattenConsume(&size, sizeof(int));
-
- buffer.resize(size);
-
- queue.FlattenConsume(&buffer[0], size);
- data.assign(buffer.begin(), buffer.end());
- break;
- case MSG_TYPE_PERF_TIME:
- queue.FlattenConsume(&time, sizeof(std::chrono::system_clock::duration));
- queue.FlattenConsume(&timeMax, sizeof(std::chrono::system_clock::duration));
- break;
- default:
- return ERROR;
+ queue.FlattenConsume(&size, sizeof(int));
+
+ buffer.resize(size);
+
+ queue.FlattenConsume(&buffer[0], size);
+ data.assign(buffer.begin(), buffer.end());
+
+ if (code != CHILD_TEST_PASS)
+ return SUCCESS;
+
+ if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS) {
+ return ret;
+ }
+
+ if (size == 0) {
+ performance = nullptr;
+ return SUCCESS;
+ }
+
+ buffer.resize(size);
+
+ if ((ret = readHelp(buffer.data(), size, deadline)) != SUCCESS) {
+ return ret;
}
+
+ queue.AppendCopy(buffer.data(), size);
+
+ performance.reset(new PerformanceResult(queue));
} catch (DPL::BinaryQueue::Exception::Base &e) {
return ERROR;
}
return std::string(buffer, buffer + sizeof(int));
}
-std::string PipeWrapper::toBinaryString(std::chrono::system_clock::duration data)
-{
- char buffer[sizeof(std::chrono::system_clock::duration)];
- memcpy(buffer, &data, sizeof(std::chrono::system_clock::duration));
- return std::string(buffer, buffer + sizeof(std::chrono::system_clock::duration));
-}
-
void PipeWrapper::closeHelp(int desc)
{
if (m_pipefd[desc] != PIPE_CLOSED) {
{
PipeWrapper pipe;
if (!pipe.isReady()) {
- throw TestRunner::TestFailed("Pipe creation failed");
+ throw TestFailed("Pipe creation failed");
}
pid_t pid = fork();
if (pid == -1) {
- throw TestRunner::TestFailed("Child creation failed");
+ throw TestFailed("Child creation failed");
}
if (pid != 0) {
pipe.setUsage(PipeWrapper::READONLY);
int code;
- int msgType;
- std::chrono::system_clock::duration time_m;
- std::chrono::system_clock::duration timeMax_m;
std::string message;
+ PerformanceResultPtr performance;
- int pipeReturn = pipe.receive(code, msgType, message, time_m, timeMax_m, time(0) + 10);
+ int pipeReturn = pipe.receive(code, message, performance, time(0) + 10);
if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
pipe.closeAll();
waitpid(pid, &status, 0);
if (pipeReturn == PipeWrapper::TIMEOUT) {
- throw TestRunner::TestFailed("Timeout");
+ throw TestFailed("Timeout");
}
if (pipeReturn == PipeWrapper::ERROR) {
- throw TestRunner::TestFailed("Reading pipe error");
+ throw TestFailed("Reading pipe error");
}
- if (code == CHILD_TEST_PASS && msgType == MSG_TYPE_PERF_TIME) {
- DPL::Test::TestRunnerSingleton::Instance().setCurrentTestCasePerformanceResult(true,
- time_m,
- timeMax_m);
- }
+ TestRunnerSingleton::Instance().setCurrentTestCasePerformanceResult(performance);
if (code == CHILD_TEST_FAIL) {
- throw TestRunner::TestFailed(message);
+ throw TestFailed(message);
} else if (code == CHILD_TEST_IGNORED) {
- throw TestRunner::Ignored(message);
+ throw TestIgnored(message);
}
} else {
// child code
int code = CHILD_TEST_PASS;
std::string msg;
- bool isPerformanceTest;
- std::chrono::system_clock::duration time_m;
- std::chrono::system_clock::duration timeMax_m;
bool allowLogs = TestRunnerSingleton::Instance().GetAllowChildLogs();
try {
procChild();
- } catch (const DPL::Test::TestRunner::TestFailed &e) {
+ } catch (const DPL::Test::TestFailed &e) {
msg = e.GetMessage();
code = CHILD_TEST_FAIL;
- } catch (const DPL::Test::TestRunner::Ignored &e) {
+ } catch (const DPL::Test::TestIgnored &e) {
msg = e.GetMessage();
code = CHILD_TEST_IGNORED;
} catch (...) { // catch all exception generated by "user" code
closeOutput();
}
- DPL::Test::TestRunnerSingleton::Instance().getCurrentTestCasePerformanceResult(isPerformanceTest,
- time_m,
- timeMax_m);
-
- if (code == CHILD_TEST_PASS && isPerformanceTest){
- pipe.sendTime(code,
- time_m,
- timeMax_m);
- } else {
- pipe.send(code, msg);
+ pipe.send(code, msg);
+ if (code == CHILD_TEST_PASS){
+ pipe.sendPerformance(TestRunnerSingleton::Instance() \
+ .getCurrentTestCasePerformanceResult());
}
}
}