SET(DPL_TEST_ENGINE_SOURCES
${PROJECT_SOURCE_DIR}/modules/test/src/test_results_collector.cpp
${PROJECT_SOURCE_DIR}/modules/test/src/test_runner.cpp
+ ${PROJECT_SOURCE_DIR}/modules/test/src/test_runner_child.cpp
PARENT_SCOPE
)
SET(DPL_TEST_ENGINE_HEADERS
${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/test_results_collector.h
${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/test_runner.h
+ ${PROJECT_SOURCE_DIR}/modules/test/include/dpl/test/test_runner_child.h
PARENT_SCOPE
)
bool m_runIgnored;
public:
+ TestRunner()
+ : m_terminate(false)
+ {}
+
typedef void (*TestCase)();
private:
DPL::Atomic m_totalAssertions;
+ // Terminate without any logs.
+ // Some test requires to call fork function.
+ // Child process must not produce any logs and should die quietly.
+ bool m_terminate;
void Banner();
void InvalidArgs(const std::string& message = "Invalid arguments!");
void Usage();
//! \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;
typedef std::vector<std::string> ArgsList;
int ExecTestRunner(const ArgsList& args);
bool getRunIgnored() const;
+ // The runner will terminate as soon as possible (after current test).
+ void terminate();
};
typedef DPL::Singleton<TestRunner> TestRunnerSingleton;
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * @file test_runner_child.h
+ * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
+ * @version 1.0
+ * @brief This file is the header file of test runner
+ */
+#ifndef DPL_TEST_RUNNER_CHILD_H
+#define DPL_TEST_RUNNER_CHILD_H
+
+#include <dpl/test/test_runner.h>
+
+namespace DPL {
+namespace Test {
+
+void RunChildProc(TestRunner::TestCase procChild);
+
+} // namespace Test
+} // namespace DPL
+
+#define RUNNER_CHILD_TEST(Proc) \
+ void Proc(); \
+ void Proc##Child(); \
+ static int Static##Proc##Init() \
+ { \
+ DPL::Test::TestRunnerSingleton::Instance().RegisterTest(#Proc, &Proc); \
+ return 0; \
+ } \
+ const int DPL_UNUSED Static##Proc##InitVar = Static##Proc##Init(); \
+ void Proc(){ \
+ DPL::Test::RunChildProc(&Proc##Child); \
+ } \
+ void Proc##Child()
+
+#endif // DPL_TEST_RUNNER_CHILD_H
m_message = assertMsg.str();
}
+TestRunner::TestFailed::TestFailed(const std::string &message)
+{
+ m_message = message;
+}
+
void TestRunner::RegisterTest(const char *testName, TestCase proc)
{
m_testGroups[m_currentGroup].push_back(TestCaseStruct(testName, proc));
if (m_startTestId.empty()) {
RunTestCase(test);
}
+ if (m_terminate == true) {
+ // Terminate quietly without any logs
+ return;
+ }
}
}
}
return m_runIgnored;
}
+void TestRunner::terminate()
+{
+ m_terminate = true;
+}
+
}
} // namespace DPL
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * 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_runner.h>
+#include <dpl/test/test_runner_child.h>
+#include <dpl/test/test_results_collector.h>
+#include <dpl/binary_queue.h>
+#include <dpl/exception.h>
+#include <dpl/scoped_free.h>
+#include <dpl/foreach.h>
+#include <dpl/colors.h>
+#include <pcrecpp.h>
+#include <algorithm>
+#include <cstdio>
+#include <memory.h>
+#include <libgen.h>
+#include <cstring>
+#include <cstdlib>
+#include <ctime>
+#include <unistd.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <dpl/utils/wrt_global_settings.h>
+
+namespace {
+const int PIPE_CLOSED = -1;
+}
+
+namespace DPL
+{
+namespace Test
+{
+
+class PipeWrapper : DPL::Noncopyable {
+public:
+ enum Usage {
+ READONLY,
+ WRITEONLY
+ };
+
+ enum Status {
+ SUCCESS,
+ TIMEOUT,
+ ERROR
+ };
+
+ PipeWrapper(){
+ if (-1 == pipe(m_pipefd)) {
+ m_pipefd[0] = PIPE_CLOSED;
+ m_pipefd[1] = PIPE_CLOSED;
+ }
+ }
+
+ bool isReady(){
+ return m_pipefd[0] != PIPE_CLOSED || m_pipefd[1] != PIPE_CLOSED;
+ }
+
+ void setUsage(Usage usage) {
+ if (usage == READONLY) {
+ closeHelp(1);
+ }
+ if (usage == WRITEONLY) {
+ closeHelp(0);
+ }
+ }
+ ~PipeWrapper(){
+ closeHelp(0);
+ closeHelp(1);
+ }
+
+ Status send(int code, std::string &message) {
+ if (m_pipefd[1] == PIPE_CLOSED)
+ return ERROR;
+
+ std::ostringstream output;
+ output << toBinaryString(code);
+ output << toBinaryString(static_cast<int>(message.size()));
+ output << message;
+
+ std::string binary = output.str();
+ int size = binary.size();
+
+ if ((writeHelp(&size, sizeof(int)) == ERROR) || (writeHelp(binary.c_str(), size) == ERROR)) {
+ return ERROR;
+ }
+ return SUCCESS;
+ }
+
+ Status receive(int &code, std::string &data, time_t deadline) {
+ if (m_pipefd[0] == PIPE_CLOSED)
+ return ERROR;
+
+ int size;
+ Status ret;
+
+ if ((ret = readHelp(&size, sizeof(int), deadline)) != SUCCESS)
+ return ret;
+
+ std::vector<char> buffer;
+ buffer.resize(size);
+
+ if ((ret = readHelp(&buffer[0], size, deadline)) != SUCCESS)
+ return ret;
+
+ try {
+ DPL::BinaryQueue queue;
+ queue.AppendCopy(&buffer[0], size);
+
+ queue.FlattenConsume(&code, sizeof(int));
+ queue.FlattenConsume(&size, sizeof(int));
+
+ buffer.resize(size);
+
+ queue.FlattenConsume(&buffer[0], size);
+ data.assign(buffer.begin(), buffer.end());
+ } catch (DPL::BinaryQueue::Exception::Base &e) {
+ return ERROR;
+ }
+ return SUCCESS;
+ }
+
+ void closeAll() {
+ closeHelp(0);
+ closeHelp(1);
+ }
+private:
+ std::string toBinaryString(int data) {
+ char buffer[sizeof(int)];
+ memcpy(buffer, &data, sizeof(int));
+ return std::string(buffer, buffer+sizeof(int));
+ }
+
+
+ void closeHelp(int desc) {
+ if (m_pipefd[desc] != PIPE_CLOSED) {
+ TEMP_FAILURE_RETRY(close(m_pipefd[desc]));
+ m_pipefd[desc] = PIPE_CLOSED;
+ }
+ }
+
+ Status writeHelp(const void *buffer, int size) {
+ int ready = 0;
+ const char *p = static_cast<const char *>(buffer);
+ while (ready != size) {
+ int ret = write(m_pipefd[1], &p[ready], size-ready);
+
+ if (ret == -1 && (errno == EAGAIN || errno == EINTR))
+ continue;
+
+ if (ret == -1) {
+ closeHelp(1);
+ return ERROR;
+ }
+
+ ready += ret;
+ }
+ return SUCCESS;
+ }
+
+ Status readHelp(void *buf, int size, time_t deadline) {
+ int ready = 0;
+ char *buffer = static_cast<char*>(buf);
+ while (ready != size) {
+ time_t wait = deadline - time(0);
+ wait = wait < 1 ? 1 : wait;
+ pollfd fds = { m_pipefd[0], POLLIN, 0};
+
+ int pollReturn = poll(&fds, 1, wait * 1000);
+
+ if (pollReturn == 0) {
+ return TIMEOUT; // Timeout
+ }
+
+ if (pollReturn < -1) {
+ return ERROR;
+ }
+
+ int ret = read(m_pipefd[0], &buffer[ready], size-ready);
+
+ if (ret == -1 && (errno == EAGAIN || errno == EINTR))
+ continue;
+
+ if (ret == -1 || ret == 0) {
+ closeHelp(0);
+ return ERROR;
+ }
+
+ ready += ret;
+ }
+ return SUCCESS;
+ }
+
+ int m_pipefd[2];
+};
+
+void RunChildProc(TestRunner::TestCase procChild){
+ PipeWrapper pipe;
+ if (!pipe.isReady()) {
+ throw TestRunner::TestFailed("Pipe creation failed");
+ }
+
+ pid_t pid = fork();
+
+ if (pid == -1)
+ throw TestRunner::TestFailed("Child creation failed");
+
+ if (pid != 0) {
+ // parent code
+ pipe.setUsage(PipeWrapper::READONLY);
+
+ int code;
+ std::string message;
+
+ int pipeReturn = pipe.receive(code, message, time(0)+10);
+
+ if (pipeReturn != PipeWrapper::SUCCESS) { // Timeout or reading error
+ pipe.closeAll();
+ kill(pid, SIGINT);
+ }
+
+ int status;
+ waitpid(pid, &status, 0);
+
+ if (pipeReturn == PipeWrapper::TIMEOUT) {
+ throw TestRunner::TestFailed("Timeout");
+ }
+
+ if (pipeReturn == PipeWrapper::ERROR) {
+ throw TestRunner::TestFailed("Reading pipe error");
+ }
+
+ if (code == 0) {
+ throw TestRunner::TestFailed(message);
+ }
+ } else {
+ // child code
+
+ // End Runner after current test
+ TestRunnerSingleton::Instance().terminate();
+ int code = 1;
+ std::string msg;
+
+// close(0); // stdin
+ close(1); // stdout
+ close(2); // stderr
+
+ pipe.setUsage(PipeWrapper::WRITEONLY);
+
+ try {
+ procChild();
+ } catch (DPL::Test::TestRunner::TestFailed &e) {
+ msg = e.GetMessage();
+ code = 0;
+ } catch (...) { // Pokemon Catch... cache them all...
+ msg = "unhandled exeception";
+ code = 0;
+ }
+ pipe.send(code, msg);
+ }
+}
+
+} // namespace Test
+} // namespace DPL
ADD_SUBDIRECTORY(event)
#ADD_SUBDIRECTORY(localization) TODO localization mockups need to be fixed
ADD_SUBDIRECTORY(utils)
+ADD_SUBDIRECTORY(test)
--- /dev/null
+# Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# @file CMakeLists.txt
+# @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
+# @version 1.0
+# @brief
+#
+
+SET(TARGET_NAME "dpl-tests-test")
+
+# Set DPL tests sources
+SET(DPL_TESTS_UTIL_SOURCES
+ ${TESTS_DPL_DIR}/test/main.cpp
+ ${TESTS_DPL_DIR}/test/runner_child.cpp
+)
+
+#WRT_TEST_ADD_INTERNAL_DEPENDENCIES(${TARGET_NAME} ${TARGET_DPL_UTILS_EFL})
+WRT_TEST_BUILD(${TARGET_NAME} ${DPL_TESTS_UTIL_SOURCES})
+WRT_TEST_INSTALL(${TARGET_NAME})
--- /dev/null
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * @file main.cpp
+ * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
+ * @version 1.0
+ * @brief This file is the implementation file of main
+ */
+#include <dpl/test/test_runner.h>
+
+int main(int argc, char *argv[])
+{
+ return DPL::Test::TestRunnerSingleton::Instance().ExecTestRunner(argc, argv);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * @file widget_version.cpp
+ * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
+ * @version 1.0
+ * @brief Implementation file for test cases for engine internal tests
+ */
+#include <dpl/test/test_runner_child.h>
+#include <unistd.h>
+#include <vector>
+#include <sys/types.h>
+#include <signal.h>
+
+RUNNER_TEST_GROUP_INIT(DPL_TESTS_TEST_CHILD)
+
+RUNNER_TEST(t00_pass)
+{
+ RUNNER_ASSERT_MSG(1, "This test should pass");
+}
+
+RUNNER_CHILD_TEST(t01_pass)
+{
+ RUNNER_ASSERT_MSG(1, "This test should pass");
+}
+
+RUNNER_CHILD_TEST(t02_fail)
+{
+ RUNNER_ASSERT_MSG(0, "This test should fail");
+}
+
+RUNNER_CHILD_TEST(t03_fail_timeout)
+{
+ sleep(20);
+ RUNNER_ASSERT_MSG(1, "This test should fail");
+}
+
+RUNNER_CHILD_TEST(t04_fail)
+{
+ RUNNER_ASSERT_MSG(1, "This test should fail");
+ RUNNER_ASSERT_MSG(1, "This test should fail");
+ RUNNER_ASSERT_MSG(1, "This test should fail");
+ RUNNER_ASSERT_MSG(1, "This test should fail");
+ RUNNER_ASSERT_MSG(0, "This test should fail");
+}
+
+RUNNER_CHILD_TEST(t05_fail_child_died)
+{
+ kill(getpid(), SIGKILL);
+ RUNNER_ASSERT_MSG(1, "This test should fail");
+}
+
+RUNNER_CHILD_TEST(t06_pass_8_second_test)
+{
+ sleep(8);
+ RUNNER_ASSERT_MSG(1, "This test should pass");
+}
+
+RUNNER_CHILD_TEST(t07_fail_unknown_exception)
+{
+ throw("hello");
+}
+
+RUNNER_CHILD_TEST(t08_fail_unknown_exception)
+{
+ throw(1);
+}
+
+RUNNER_CHILD_TEST(t09_fail_you_should_see_text_normal_assert)
+{
+ RUNNER_ASSERT_MSG(0, "Normal assert");
+}
+
+RUNNER_CHILD_TEST(t10_pass)
+{
+ RUNNER_ASSERT_MSG(1, "Normal assert");
+}
+