Commit 58c573fe authored by Matthias Vallentin's avatar Matthias Vallentin Committed by Marian Triebe

Add new unit testing framework.

parent c75fde04
......@@ -329,10 +329,9 @@ set(LIBCAF_LIBRARIES "${LIBCAF_CORE_LIBRARY}"
# add unit tests if not being told otherwise
if(NOT CAF_NO_UNIT_TESTS)
enable_testing()
add_subdirectory(unit_testing)
add_dependencies(all_unit_tests libcaf_core libcaf_io)
if(NOT CAF_NO_OPENCL
AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libcaf_opencl/CMakeLists.txt")
add_subdirectory(libcaf_test)
add_dependencies(all_unit_tests libcaf_io)
if(NOT CAF_NO_OPENCL AND EXISTS "${CMAKE_SOURCE_DIR}/libcaf_opencl/CMakeLists.txt")
add_subdirectory(libcaf_opencl/unit_testing)
endif()
endif()
......
cmake_minimum_required(VERSION 2.8)
project(caf_test C CXX)
file(GLOB LIBCAF_TEST_HDRS "caf/test/*.hpp")
file(GLOB_RECURSE LIBCAF_TEST_SRCS src/unit_test.cpp)
# build shared library if not compiling static only
if (NOT CAF_BUILD_STATIC_ONLY)
add_library(libcaf_test SHARED ${LIBCAF_TEST_SRCS} ${LIBCAF_TEST_HDRS})
target_link_libraries(libcaf_test ${LD_FLAGS})
set(LIBRARY_SOVERSION ${CAF_VERSION_MAJOR})
set_target_properties(libcaf_test
PROPERTIES
SOVERSION ${LIBRARY_SOVERSION}
VERSION ${CAF_VERSION}
OUTPUT_NAME caf_test)
if(NOT WIN32)
install(TARGETS libcaf_test LIBRARY DESTINATION lib)
endif()
endif ()
# build static library only if --build-static or --build-static-only was set
if (CAF_BUILD_STATIC_ONLY OR CAF_BUILD_STATIC)
add_library(libcaf_testStatic STATIC ${LIBCAF_TEST_HDRS} ${LIBCAF_TEST_SRCS})
set_target_properties(libcaf_testStatic
PROPERTIES OUTPUT_NAME caf_test_static)
if(NOT WIN32)
install(TARGETS libcaf_testStatic ARCHIVE DESTINATION lib)
endif()
endif ()
link_directories(${LD_DIRS})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
# install includes
if(NOT WIN32)
install(DIRECTORY caf/ DESTINATION include/caf FILES_MATCHING PATTERN "*.hpp")
endif()
# The single unit test binary containing all tests.
file(GLOB_RECURSE tests test.cpp test/*.cpp)
add_executable(caf-test ${tests})
target_link_libraries(caf-test
libcaf_test
${LD_FLAGS}
${LIBCAF_CORE_LIBRARY}
${PTHREAD_LIBRARIES})
add_custom_target(all_unit_tests)
add_dependencies(caf-test all_unit_tests)
# Enumerate all test suites.
foreach(test ${tests})
file(STRINGS ${test} contents)
foreach(line ${contents})
if ("${line}" MATCHES "CAF_SUITE(.*)")
string(REGEX REPLACE ".*\"(.*)\".*" "\\1" suite ${line})
list(APPEND suites ${suite})
endif()
endforeach()
endforeach()
list(REMOVE_DUPLICATES suites)
# Creates one CMake test per test suite.
macro (make_test suite)
string(REPLACE " " "_" test_name ${suite})
set(caf_test ${EXECUTABLE_OUTPUT_PATH}/caf-test)
add_test(${test_name} ${caf_test} -n -v 3 -s "${suite}" ${ARGN})
endmacro ()
foreach(suite ${suites})
make_test("${suite}")
endforeach ()
CAF Unit Testing Framework
==========================
The CAF unit testing framework offers a simple API to write unit tests.
Concepts
--------
- A **check** represents a single verification of boolean operation.
- A **test** contains one or more checks.
- A **suite** groups tests together.
Example
-------
```cpp
#include <caf/test/unit_test.h>
CAF_SUITE("core")
CAF_TEST("multiply")
{
CAF_REQUIRE(0 * 1 == 0);
CAF_CHECK(42 + 42 == 84);
}
CAF_TEST("divide")
{
CAF_FAIL(0 / 1 == 0);
CAF_CHECK(1 / 1 == 0); // fails
}
```
This diff is collapsed.
This diff is collapsed.
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/test/unit_test.hpp"
#include "caf/test/unit_test_impl.hpp"
#include <cstdlib>
#include "caf/all.hpp"
#include "caf/test/unit_test.hpp"
int main(int argc, char* argv[]) {
using namespace caf;
// Default values.
auto colorize = true;
std::string log_file;
int verbosity_console = 3;
int verbosity_file = 3;
int max_runtime = 10;
std::regex suites{".*"};
std::regex not_suites;
std::regex tests{".*"};
std::regex not_tests;
// Our simple command line parser.
message_handler parser{
on("-n") >> [&] {
colorize = false;
},
on("-l", arg_match) >> [&](const std::string& file_name) {
log_file = file_name;
},
on("-v", arg_match) >> [&](const std::string& n) {
verbosity_console = static_cast<int>(std::strtol(n.c_str(), nullptr, 10));
},
on("-V", arg_match) >> [&](const std::string& n) {
verbosity_file = static_cast<int>(std::strtol(n.c_str(), nullptr, 10));
},
on("-r", arg_match) >> [&](const std::string& n) {
max_runtime = static_cast<int>(std::strtol(n.c_str(), nullptr, 10));
},
on("-s", arg_match) >> [&](const std::string& str) {
suites = str;
},
on("-S", arg_match) >> [&](const std::string& str) {
not_suites = str;
},
on("-t", arg_match) >> [&](const std::string& str) {
tests = str;
},
on("-T", arg_match) >> [&](const std::string& str) {
not_tests = str;
},
others() >> [&] {
std::cerr << "invalid command line argument" << std::endl;
std::exit(1);
}
};
auto is_opt_char = [](char c) {
switch (c) {
default:
return false;
case 'n':
case 'l':
case 'v':
case 'V':
case 'r':
case 's':
case 'S':
case 't':
case 'T':
return true;
};
};
for (int i = 1; i < argc; ++i) {
message_builder builder;
std::string arg{argv[i]};
if (arg.empty() || arg[0] != '-') {
std::cerr << "invalid option: " << arg << std::endl;
return 1;
}
builder.append(std::move(arg));
if (i + 1 < argc) {
auto next = argv[i + 1];
if (*next == '\0' || next[0] != '-' || ! is_opt_char(next[1])) {
builder.append(std::string{next});
++i;
}
}
builder.apply(parser);
}
auto result = caf::test::engine::run(colorize, log_file, verbosity_console,
verbosity_file, max_runtime, suites,
not_suites, tests, not_tests);
return result ? 0 : 1;
}
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE "example"
#include "caf/test/unit_test.hpp"
CAF_TEST("foo")
{
CAF_CHECK(true);
CAF_CHECK(!false);
}
CAF_TEST("bar")
{
auto i = 42;
i *= 2;
CAF_REQUIRE(i == 84);
}
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include <iostream>
#include "ping_pong.hpp"
#define CAF_SUITE "spawn"
#include "caf/all.hpp"
#include "caf/detail/logging.hpp"
#include "caf/test/unit_test.hpp"
using std::cout;
using std::endl;
using namespace caf;
using namespace caf;
namespace {
size_t s_pongs = 0;
behavior ping_behavior(local_actor* self, size_t num_pings) {
return {
on(atom("pong"), arg_match) >> [=](int value)->message {
if (!self->current_sender()) {
CAF_TEST_ERROR("current_sender() invalid!");
}
CAF_TEST_INFO("received {'pong', " << value << "}");
// cout << to_string(self->current_message()) << endl;
if (++s_pongs >= num_pings) {
CAF_TEST_INFO("reached maximum, send {'EXIT', user_defined} "
<< "to last sender and quit with normal reason");
self->send_exit(self->current_sender(),
exit_reason::user_shutdown);
self->quit();
}
return make_message(atom("ping"), value);
},
others() >> [=] {
self->quit(exit_reason::user_shutdown);
}
};
}
behavior pong_behavior(local_actor* self) {
return {
on(atom("ping"), arg_match) >> [](int value)->message {
return make_message(atom("pong"), value + 1);
},
others() >> [=] {
self->quit(exit_reason::user_shutdown);
}
};
}
} // namespace <anonymous>
size_t pongs() { return s_pongs; }
void ping(blocking_actor* self, size_t num_pings) {
s_pongs = 0;
self->receive_loop(ping_behavior(self, num_pings));
}
void event_based_ping(event_based_actor* self, size_t num_pings) {
s_pongs = 0;
self->become(ping_behavior(self, num_pings));
}
void pong(blocking_actor* self, actor ping_actor) {
self->send(ping_actor, atom("pong"), 0); // kickoff
self->receive_loop(pong_behavior(self));
}
void event_based_pong(event_based_actor* self, actor ping_actor) {
CAF_LOGF_TRACE("ping_actor = " << to_string(ping_actor));
CAF_REQUIRE(ping_actor != invalid_actor);
self->send(ping_actor, atom("pong"), 0); // kickoff
self->become(pong_behavior(self));
}
CAF_TEST("ping-pong") {
using namespace caf;
scoped_actor self;
self->trap_exit(true);
auto ping_actor = self->spawn<monitored+blocking_api>(ping, 10);
auto pong_actor = self->spawn<monitored+blocking_api>(pong, ping_actor);
self->link_to(pong_actor);
int i = 0;
int flags = 0;
self->delayed_send(self, std::chrono::seconds(1), atom("FooBar"));
// wait for DOWN and EXIT messages of pong
self->receive_for(i, 4) (
[&](const exit_msg& em) {
CAF_CHECK(em.source == pong_actor);
CAF_CHECK(em.reason == exit_reason::user_shutdown);
flags |= 0x01;
},
[&](const down_msg& dm) {
if (dm.source == pong_actor) {
flags |= 0x02;
CAF_CHECK(dm.reason == exit_reason::user_shutdown);
}
else if (dm.source == ping_actor) {
flags |= 0x04;
CAF_CHECK(dm.reason == exit_reason::normal);
}
},
[&](const atom_value& val) {
CAF_CHECK(val == atom("FooBar"));
flags |= 0x08;
},
others() >> [&]() {
// FIXME: abort test with error message.
//CAF_FAILURE("unexpected message: " << to_string(self->current_message()));
},
after(std::chrono::seconds(5)) >> [&]() {
// FIXME: abort test with error message.
//CAF_FAILURE("timeout in file " << __FILE__ << " in line " << __LINE__);
}
);
// wait for termination of all spawned actors
self->await_all_other_actors_done();
CAF_CHECK(flags == 0x0F);
CAF_CHECK(pongs() == 10);
}
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef PING_PONG_HPP
#define PING_PONG_HPP
//#include "caf/actor.hpp"
#include <cstddef>
#include "caf/fwd.hpp"
void ping(caf::blocking_actor*, size_t num_pings);
void event_based_ping(caf::event_based_actor*, size_t num_pings);
void pong(caf::blocking_actor*, caf::actor ping_actor);
// returns the number of messages ping received
size_t pongs();
#endif // PING_PONG_HPP
#include <iostream>
#include "test.hpp"
#include "ping_pong.hpp"
#include "caf/all.hpp"
#include "caf/detail/logging.hpp"
using std::cout;
using std::endl;
using namespace caf;
namespace {
size_t s_pongs = 0;
behavior ping_behavior(local_actor* self, size_t num_pings) {
return {
[=](pong_atom, int value) -> message {
if (!self->current_sender()) {
CAF_PRINT("last_sender() invalid!");
}
CAF_PRINT("received {'pong', " << value << "}");
// cout << to_string(self->current_message()) << endl;
if (++s_pongs >= num_pings) {
CAF_PRINT("reached maximum, send {'EXIT', user_defined} "
<< "to last sender and quit with normal reason");
self->send_exit(self->current_sender(),
exit_reason::user_shutdown);
self->quit();
}
return make_message(ping_atom::value, value);
},
others >> [=] {
CAF_PRINTERR("unexpected; " << to_string(self->current_message()));
self->quit(exit_reason::user_shutdown);
}
};
}
behavior pong_behavior(local_actor* self) {
return {
[](ping_atom, int value) -> message {
CAF_PRINT("received {'ping', " << value << "}");
return make_message(pong_atom::value, value + 1);
},
others >> [=] {
CAF_PRINTERR("unexpected; " << to_string(self->current_sender()));
self->quit(exit_reason::user_shutdown);
}
};
}
} // namespace <anonymous>
size_t pongs() { return s_pongs; }
void ping(blocking_actor* self, size_t num_pings) {
s_pongs = 0;
self->receive_loop(ping_behavior(self, num_pings));
}
void event_based_ping(event_based_actor* self, size_t num_pings) {
s_pongs = 0;
self->become(ping_behavior(self, num_pings));
}
void pong(blocking_actor* self, actor ping_actor) {
self->send(ping_actor, pong_atom::value, 0); // kickoff
self->receive_loop(pong_behavior(self));
}
#ifndef PING_PONG_HPP
#define PING_PONG_HPP
//#include "caf/actor.hpp"
#include <cstddef>
#include "caf/fwd.hpp"
void ping(caf::blocking_actor*, size_t num_pings);
void event_based_ping(caf::event_based_actor*, size_t num_pings);
void pong(caf::blocking_actor*, caf::actor ping_actor);
// returns the number of messages ping received
size_t pongs();
#endif // PING_PONG_HPP
......@@ -5,7 +5,6 @@
#include <functional>
#include "test.hpp"
#include "ping_pong.hpp"
#include "caf/all.hpp"
......@@ -713,7 +712,7 @@ void test_spawn() {
self->await_all_other_actors_done();
// test sending message to self via scoped_actor
self->send(self, atom("check"));
self->receive (
self->receive(
on(atom("check")) >> [] {
CAF_CHECKPOINT();
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment