Commit e2bea08c authored by Dominik Charousset's avatar Dominik Charousset

Integrate the new unit testing framework

parent a1ba72cc
...@@ -319,7 +319,8 @@ endfunction() ...@@ -319,7 +319,8 @@ endfunction()
# ... # ...
# ) # )
function(caf_add_component name) function(caf_add_component name)
set(varargs DEPENDENCIES HEADERS SOURCES TEST_SOURCES TEST_SUITES ENUM_TYPES) set(varargs DEPENDENCIES HEADERS SOURCES TEST_SOURCES TEST_SUITES ENUM_TYPES
LEGACY_TEST_SOURCES LEGACY_TEST_SUITES)
cmake_parse_arguments(CAF_ADD_COMPONENT "" "" "${varargs}" ${ARGN}) cmake_parse_arguments(CAF_ADD_COMPONENT "" "" "${varargs}" ${ARGN})
if(NOT CAF_ADD_COMPONENT_HEADERS) if(NOT CAF_ADD_COMPONENT_HEADERS)
message(FATAL_ERROR "Cannot add CAF component without at least one header.") message(FATAL_ERROR "Cannot add CAF component without at least one header.")
...@@ -365,6 +366,20 @@ function(caf_add_component name) ...@@ -365,6 +366,20 @@ function(caf_add_component name)
caf_add_test_suites(${tst_bin_target} ${CAF_ADD_COMPONENT_TEST_SUITES}) caf_add_test_suites(${tst_bin_target} ${CAF_ADD_COMPONENT_TEST_SUITES})
endif() endif()
endif() endif()
if(CAF_ENABLE_TESTING AND CAF_ADD_COMPONENT_LEGACY_TEST_SOURCES)
set(tst_bin_target "caf-${name}-legacy-test")
list(APPEND targets ${tst_bin_target})
add_executable(${tst_bin_target}
${CAF_ADD_COMPONENT_LEGACY_TEST_SOURCES}
$<TARGET_OBJECTS:${obj_lib_target}>)
target_link_libraries(${tst_bin_target} PRIVATE libcaf_test
${CAF_ADD_COMPONENT_DEPENDENCIES})
target_include_directories(${tst_bin_target} PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/test")
if(CAF_ADD_COMPONENT_LEGACY_TEST_SUITES)
caf_add_test_suites(${tst_bin_target} ${CAF_ADD_COMPONENT_LEGACY_TEST_SUITES})
endif()
endif()
target_link_libraries(${pub_lib_target} ${CAF_ADD_COMPONENT_DEPENDENCIES}) target_link_libraries(${pub_lib_target} ${CAF_ADD_COMPONENT_DEPENDENCIES})
if(CAF_ADD_COMPONENT_ENUM_TYPES) if(CAF_ADD_COMPONENT_ENUM_TYPES)
foreach(enum_name ${CAF_ADD_COMPONENT_ENUM_TYPES}) foreach(enum_name ${CAF_ADD_COMPONENT_ENUM_TYPES})
......
...@@ -218,8 +218,12 @@ caf_add_component( ...@@ -218,8 +218,12 @@ caf_add_component(
src/uuid.cpp src/uuid.cpp
TEST_SOURCES TEST_SOURCES
test/core-test.cpp test/core-test.cpp
test/nasty.cpp
TEST_SUITES TEST_SUITES
hash.fnv
LEGACY_TEST_SOURCES
test/core-legacy-test.cpp
test/nasty.cpp
LEGACY_TEST_SUITES
action action
actor_clock actor_clock
actor_factory actor_factory
...@@ -308,7 +312,6 @@ caf_add_component( ...@@ -308,7 +312,6 @@ caf_add_component(
flow.single flow.single
function_view function_view
handles handles
hash.fnv
hash.sha1 hash.sha1
intrusive.drr_cached_queue intrusive.drr_cached_queue
intrusive.drr_queue intrusive.drr_queue
...@@ -391,5 +394,5 @@ if(NOT CAF_USE_STD_FORMAT) ...@@ -391,5 +394,5 @@ if(NOT CAF_USE_STD_FORMAT)
endif() endif()
if(CAF_ENABLE_TESTING AND CAF_ENABLE_EXCEPTIONS) if(CAF_ENABLE_TESTING AND CAF_ENABLE_EXCEPTIONS)
caf_add_test_suites(caf-core-test custom_exception_handler) caf_add_test_suites(caf-core-legacy-test custom_exception_handler)
endif() endif()
#define CAF_TEST_NO_MAIN
#include "caf/test/unit_test_impl.hpp"
#include "core-test.hpp"
#include <atomic>
namespace {
/// A trivial disposable with an atomic flag.
class trivial_impl : public caf::ref_counted, public caf::disposable::impl {
public:
trivial_impl() : flag_(false) {
// nop
}
void dispose() override {
flag_ = true;
}
bool disposed() const noexcept override {
return flag_.load();
}
void ref_disposable() const noexcept override {
ref();
}
void deref_disposable() const noexcept override {
deref();
}
friend void intrusive_ptr_add_ref(const trivial_impl* ptr) noexcept {
ptr->ref();
}
friend void intrusive_ptr_release(const trivial_impl* ptr) noexcept {
ptr->deref();
}
private:
std::atomic<bool> flag_;
};
} // namespace
namespace caf::flow {
std::string to_string(observer_state x) {
switch (x) {
default:
return "???";
case observer_state::idle:
return "caf::flow::observer_state::idle";
case observer_state::subscribed:
return "caf::flow::observer_state::subscribed";
case observer_state::completed:
return "caf::flow::observer_state::completed";
case observer_state::aborted:
return "caf::flow::observer_state::aborted";
}
}
bool from_string(std::string_view in, observer_state& out) {
if (in == "caf::flow::observer_state::idle") {
out = observer_state::idle;
return true;
}
if (in == "caf::flow::observer_state::subscribed") {
out = observer_state::subscribed;
return true;
}
if (in == "caf::flow::observer_state::completed") {
out = observer_state::completed;
return true;
}
if (in == "caf::flow::observer_state::aborted") {
out = observer_state::aborted;
return true;
}
return false;
}
bool from_integer(std::underlying_type_t<observer_state> in,
observer_state& out) {
auto result = static_cast<observer_state>(in);
switch (result) {
default:
return false;
case observer_state::idle:
case observer_state::subscribed:
case observer_state::completed:
case observer_state::aborted:
out = result;
return true;
}
}
disposable make_trivial_disposable() {
return disposable{make_counted<trivial_impl>()};
}
void passive_subscription_impl::request(size_t n) {
demand += n;
}
void passive_subscription_impl::dispose() {
disposed_flag = true;
}
bool passive_subscription_impl::disposed() const noexcept {
return disposed_flag;
}
} // namespace caf::flow
std::string to_string(level lvl) {
switch (lvl) {
case level::all:
return "all";
case level::trace:
return "trace";
case level::debug:
return "debug";
case level::warning:
return "warning";
case level::error:
return "error";
default:
return "???";
}
}
bool from_string(std::string_view str, level& lvl) {
auto set = [&](level value) {
lvl = value;
return true;
};
if (str == "all")
return set(level::all);
else if (str == "trace")
return set(level::trace);
else if (str == "debug")
return set(level::debug);
else if (str == "warning")
return set(level::warning);
else if (str == "error")
return set(level::error);
else
return false;
}
bool from_integer(uint8_t val, level& lvl) {
if (val < 5) {
lvl = static_cast<level>(val);
return true;
} else {
return false;
}
}
int main(int argc, char** argv) {
using namespace caf;
init_global_meta_objects<id_block::core_test>();
core::init_global_meta_objects();
return test::main(argc, argv);
}
#define CAF_TEST_NO_MAIN // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/unit_test_impl.hpp" #include "caf/test/caf_test_main.hpp"
#include "core-test.hpp" CAF_TEST_MAIN()
#include <atomic>
namespace {
/// A trivial disposable with an atomic flag.
class trivial_impl : public caf::ref_counted, public caf::disposable::impl {
public:
trivial_impl() : flag_(false) {
// nop
}
void dispose() override {
flag_ = true;
}
bool disposed() const noexcept override {
return flag_.load();
}
void ref_disposable() const noexcept override {
ref();
}
void deref_disposable() const noexcept override {
deref();
}
friend void intrusive_ptr_add_ref(const trivial_impl* ptr) noexcept {
ptr->ref();
}
friend void intrusive_ptr_release(const trivial_impl* ptr) noexcept {
ptr->deref();
}
private:
std::atomic<bool> flag_;
};
} // namespace
namespace caf::flow {
std::string to_string(observer_state x) {
switch (x) {
default:
return "???";
case observer_state::idle:
return "caf::flow::observer_state::idle";
case observer_state::subscribed:
return "caf::flow::observer_state::subscribed";
case observer_state::completed:
return "caf::flow::observer_state::completed";
case observer_state::aborted:
return "caf::flow::observer_state::aborted";
}
}
bool from_string(std::string_view in, observer_state& out) {
if (in == "caf::flow::observer_state::idle") {
out = observer_state::idle;
return true;
}
if (in == "caf::flow::observer_state::subscribed") {
out = observer_state::subscribed;
return true;
}
if (in == "caf::flow::observer_state::completed") {
out = observer_state::completed;
return true;
}
if (in == "caf::flow::observer_state::aborted") {
out = observer_state::aborted;
return true;
}
return false;
}
bool from_integer(std::underlying_type_t<observer_state> in,
observer_state& out) {
auto result = static_cast<observer_state>(in);
switch (result) {
default:
return false;
case observer_state::idle:
case observer_state::subscribed:
case observer_state::completed:
case observer_state::aborted:
out = result;
return true;
}
}
disposable make_trivial_disposable() {
return disposable{make_counted<trivial_impl>()};
}
void passive_subscription_impl::request(size_t n) {
demand += n;
}
void passive_subscription_impl::dispose() {
disposed_flag = true;
}
bool passive_subscription_impl::disposed() const noexcept {
return disposed_flag;
}
} // namespace caf::flow
std::string to_string(level lvl) {
switch (lvl) {
case level::all:
return "all";
case level::trace:
return "trace";
case level::debug:
return "debug";
case level::warning:
return "warning";
case level::error:
return "error";
default:
return "???";
}
}
bool from_string(std::string_view str, level& lvl) {
auto set = [&](level value) {
lvl = value;
return true;
};
if (str == "all")
return set(level::all);
else if (str == "trace")
return set(level::trace);
else if (str == "debug")
return set(level::debug);
else if (str == "warning")
return set(level::warning);
else if (str == "error")
return set(level::error);
else
return false;
}
bool from_integer(uint8_t val, level& lvl) {
if (val < 5) {
lvl = static_cast<level>(val);
return true;
} else {
return false;
}
}
int main(int argc, char** argv) {
using namespace caf;
init_global_meta_objects<id_block::core_test>();
core::init_global_meta_objects();
return test::main(argc, argv);
}
...@@ -2,49 +2,57 @@ ...@@ -2,49 +2,57 @@
// the main distribution directory for license terms and copyright or visit // the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE. // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE hash.fnv
#include "caf/hash/fnv.hpp" #include "caf/hash/fnv.hpp"
#include "core-test.hpp" #include "caf/test/test.hpp"
#include <string> #include <string>
using namespace caf; using namespace std::literals;
using namespace std::string_literals;
template <class... Ts> SUITE("hash.fnv") {
auto fnv32_hash(Ts&&... xs) {
return hash::fnv<uint32_t>::compute(std::forward<Ts>(xs)...);
}
template <class... Ts>
auto fnv64_hash(Ts&&... xs) {
return hash::fnv<uint64_t>::compute(std::forward<Ts>(xs)...);
}
CAF_TEST(FNV hashes build incrementally) { TEST("hash::fnv can build hashe values incrementally") {
hash::fnv<uint32_t> f; caf::hash::fnv<uint32_t> f;
CHECK_EQ(f.result, 0x811C9DC5u); check_eq(f.result, 0x811C9DC5u);
f.value('a'); f.value('a');
CHECK_EQ(f.result, 0xE40C292Cu); check_eq(f.result, 0xE40C292Cu);
f.value('b'); f.value('b');
CHECK_EQ(f.result, 0x4D2505CAu); check_eq(f.result, 0x4D2505CAu);
f.value('c'); f.value('c');
CHECK_EQ(f.result, 0x1A47E90Bu); check_eq(f.result, 0x1A47E90Bu);
f.value('d'); f.value('d');
CHECK_EQ(f.result, 0xCE3479BDu); check_eq(f.result, 0xCE3479BDu);
} }
CAF_TEST(FNV supports uint32 hashing) { TEST("fnv::compute generates a hash value in one step") {
CHECK_EQ(fnv32_hash(), 0x811C9DC5u); SECTION("32-bit") {
CHECK_EQ(fnv32_hash("abcd"s), 0xCE3479BDu); using hash_type = caf::hash::fnv<uint32_t>;
CHECK_EQ(fnv32_hash("C++ Actor Framework"s), 0x2FF91FE5u); check_eq(hash_type::compute(), 0x811C9DC5u);
check_eq(hash_type::compute("abcd"s), 0xCE3479BDu);
check_eq(hash_type::compute("C++ Actor Framework"s), 0x2FF91FE5u);
}
SECTION("64-bit") {
using hash_type = caf::hash::fnv<uint64_t>;
check_eq(hash_type::compute(), 0xCBF29CE484222325ull);
check_eq(hash_type::compute("abcd"s), 0xFC179F83EE0724DDull);
check_eq(hash_type::compute("C++ Actor Framework"s), 0xA229A760C3AF69C5ull);
}
} }
CAF_TEST(FNV supports uint64 hashing) { struct foo {
CHECK_EQ(fnv64_hash(), 0xCBF29CE484222325ull); std::string bar;
CHECK_EQ(fnv64_hash("abcd"s), 0xFC179F83EE0724DDull); };
CHECK_EQ(fnv64_hash("C++ Actor Framework"s), 0xA229A760C3AF69C5ull);
template <class Inspector>
bool inspect(Inspector& f, foo& x) {
return f.object(x).fields(f.field("bar", x.bar));
}
TEST("fnv::compute can create hash values for types with inspect overloads") {
using hash_type = caf::hash::fnv<uint32_t>;
check_eq(hash_type::compute(foo{"abcd"}), 0xCE3479BDu);
check_eq(hash_type::compute(foo{"C++ Actor Framework"}), 0x2FF91FE5u);
} }
} // SUITE("hash.fnv")
...@@ -61,6 +61,10 @@ caf_add_component( ...@@ -61,6 +61,10 @@ caf_add_component(
TEST_SOURCES TEST_SOURCES
test/io-test.cpp test/io-test.cpp
TEST_SUITES TEST_SUITES
io.network.receive_buffer
LEGACY_TEST_SOURCES
test/io-legacy-test.cpp
LEGACY_TEST_SUITES
detail.prometheus_broker detail.prometheus_broker
io.basp.message_queue io.basp.message_queue
io.basp_broker io.basp_broker
...@@ -69,7 +73,6 @@ caf_add_component( ...@@ -69,7 +73,6 @@ caf_add_component(
io.monitor io.monitor
io.network.default_multiplexer io.network.default_multiplexer
io.network.ip_endpoint io.network.ip_endpoint
io.receive_buffer
io.remote_actor io.remote_actor
io.remote_group io.remote_group
io.remote_spawn io.remote_spawn
...@@ -77,5 +80,5 @@ caf_add_component( ...@@ -77,5 +80,5 @@ caf_add_component(
io.worker) io.worker)
if(CAF_ENABLE_TESTING AND UNIX) if(CAF_ENABLE_TESTING AND UNIX)
caf_add_test_suites(caf-io-test io.middleman) caf_add_test_suites(caf-io-legacy-test io.middleman)
endif() endif()
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#pragma once #pragma once
#include <algorithm>
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include <limits> #include <limits>
......
#define CAF_TEST_NO_MAIN
#include "caf/test/unit_test_impl.hpp"
#include "io-test.hpp"
int main(int argc, char** argv) {
using namespace caf;
init_global_meta_objects<id_block::io_test>();
io::middleman::init_global_meta_objects();
core::init_global_meta_objects();
return test::main(argc, argv);
}
#define CAF_TEST_NO_MAIN // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/unit_test_impl.hpp" #include "caf/io/middleman.hpp"
#include "caf/test/caf_test_main.hpp"
#include "io-test.hpp" CAF_TEST_MAIN(caf::io::middleman)
int main(int argc, char** argv) {
using namespace caf;
init_global_meta_objects<id_block::io_test>();
io::middleman::init_global_meta_objects();
core::init_global_meta_objects();
return test::main(argc, argv);
}
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/io/network/receive_buffer.hpp"
#include "caf/test/test.hpp"
#include <algorithm>
#include <string_view>
using namespace std::literals;
using caf::io::network::receive_buffer;
SUITE("io.network.receive_buffer") {
TEST("construction") {
SECTION("default-constructed buffers are empty") {
receive_buffer uut;
check_eq(uut.size(), 0u);
check_eq(uut.capacity(), 0u);
check(uut.data() == nullptr);
check(uut.empty());
}
SECTION("constructing with a size > 0 allocates memory") {
receive_buffer uut(1024);
check_eq(uut.size(), 1024u);
check_eq(uut.capacity(), 1024u);
check(uut.data() != nullptr);
check(!uut.empty());
}
SECTION("move-constructing from a buffer transfers ownership") {
receive_buffer src(1024);
receive_buffer uut{std::move(src)};
check_eq(uut.size(), 1024u);
check_eq(uut.capacity(), 1024u);
check(uut.data() != nullptr);
check(!uut.empty());
}
}
TEST("reserve allocates memory if necessary") {
SECTION("reserve(0) is a no-op") {
receive_buffer uut;
uut.reserve(0);
check_eq(uut.size(), 0u);
check_eq(uut.capacity(), 0u);
check(uut.data() == nullptr);
check(uut.empty());
}
SECTION("reserve(n) allocates memory if n > capacity") {
receive_buffer uut;
uut.reserve(1024);
check_eq(uut.size(), 0u);
check_eq(uut.capacity(), 1024u);
check(uut.data() != nullptr);
check(uut.begin() == uut.end());
check(uut.empty());
}
SECTION("reserve(n) is a no-op if n <= capacity") {
receive_buffer uut;
uut.reserve(1024);
check_eq(uut.size(), 0u);
check_eq(uut.capacity(), 1024u);
check(uut.data() != nullptr);
check(uut.begin() == uut.end());
check(uut.empty());
auto data = uut.data();
uut.reserve(512);
check_eq(uut.size(), 0u);
check_eq(uut.capacity(), 1024u);
check(uut.data() == data);
}
}
TEST("resize adds or removes elements if necessary") {
SECTION("resize(0) is a no-op") {
receive_buffer uut;
uut.resize(0);
check_eq(uut.size(), 0u);
check_eq(uut.capacity(), 0u);
check(uut.empty());
}
SECTION("resize(n) is a no-op if n == size") {
receive_buffer uut(1024);
check_eq(uut.size(), 1024u);
uut.resize(1024);
check_eq(uut.size(), 1024u);
check_eq(uut.capacity(), 1024u);
check(uut.data() != nullptr);
check(!uut.empty());
}
SECTION("resize(n) adds elements if n > size") {
receive_buffer uut;
uut.resize(1024);
check_eq(uut.size(), 1024u);
check_eq(uut.capacity(), 1024u);
check(uut.data() != nullptr);
check(!uut.empty());
}
SECTION("resize(n) removes elements if n < size") {
receive_buffer uut(1024);
check_eq(uut.size(), 1024u);
uut.resize(512);
check_eq(uut.size(), 512u);
check_eq(uut.capacity(), 1024u);
check(uut.data() != nullptr);
check(!uut.empty());
}
}
TEST("clear removes all elements") {
receive_buffer uut(1024);
check_eq(uut.size(), 1024u);
uut.clear();
check_eq(uut.size(), 0u);
check_eq(uut.capacity(), 1024u);
check(uut.data() != nullptr);
check(uut.empty());
}
TEST("push_back appends elements") {
receive_buffer uut;
uut.push_back('h');
uut.push_back('e');
uut.push_back('l');
uut.push_back('l');
uut.push_back('o');
check_eq(uut.size(), 5u);
check_eq(uut.capacity(), 8u);
check(uut.data() != nullptr);
check(!uut.empty());
check_eq(std::string_view(uut.data(), uut.size()), "hello");
}
TEST("insert adds elements at the given position") {
receive_buffer uut;
uut.insert(uut.begin(), 'o');
uut.insert(uut.begin(), 'l');
uut.insert(uut.begin(), 'l');
uut.insert(uut.begin(), 'e');
uut.insert(uut.begin(), 'h');
check_eq(std::string_view(uut.data(), uut.size()), "hello");
auto world = "world"sv;
uut.insert(uut.end(), world.begin(), world.end());
uut.insert(uut.begin() + 5, ' ');
check_eq(std::string_view(uut.data(), uut.size()), "hello world");
}
TEST("shrink_to_fit reduces capacity to size") {
auto str = "hello world"sv;
receive_buffer uut;
uut.reserve(512);
check_eq(uut.capacity(), 512u);
uut.insert(uut.end(), str.begin(), str.end());
uut.shrink_to_fit();
check_eq(uut.capacity(), str.size());
}
TEST("swap exchanges the content of two buffers") {
auto str1 = "hello"sv;
auto str2 = "world"sv;
receive_buffer buf1;
receive_buffer buf2;
buf1.insert(buf1.end(), str1.begin(), str1.end());
buf2.insert(buf2.end(), str2.begin(), str2.end());
auto* buf1_data = buf1.data();
auto* buf2_data = buf2.data();
check_eq(std::string_view(buf1.data(), buf1.size()), "hello");
check_eq(std::string_view(buf2.data(), buf2.size()), "world");
buf1.swap(buf2);
check_eq(std::string_view(buf1.data(), buf1.size()), "world");
check_eq(std::string_view(buf2.data(), buf2.size()), "hello");
check(buf1.data() == buf2_data);
check(buf2.data() == buf1_data);
}
} // SUITE("io.network.receive_buffer")
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/config.hpp"
#define CAF_SUITE io_receive_buffer
#include "io-test.hpp"
#include <algorithm>
#include "caf/io/network/receive_buffer.hpp"
using namespace caf;
using caf::io::network::receive_buffer;
namespace {
struct fixture {
receive_buffer a;
receive_buffer b;
std::vector<char> vec;
fixture() : b(1024ul), vec{'h', 'a', 'l', 'l', 'o'} {
// nop
}
std::string as_string(const receive_buffer& xs) {
std::string result;
for (auto& x : xs)
result += static_cast<char>(x);
return result;
}
};
} // namespace
BEGIN_FIXTURE_SCOPE(fixture)
CAF_TEST(constructors) {
CHECK_EQ(a.size(), 0ul);
CHECK_EQ(a.capacity(), 0ul);
CHECK(a.data() == nullptr);
CHECK(a.empty());
CHECK_EQ(b.size(), 1024ul);
CHECK_EQ(b.capacity(), 1024ul);
CHECK(b.data() != nullptr);
CHECK(!b.empty());
receive_buffer other{std::move(b)};
CHECK_EQ(other.size(), 1024ul);
CHECK_EQ(other.capacity(), 1024ul);
CHECK(other.data() != nullptr);
CHECK(!other.empty());
}
CAF_TEST(reserve) {
a.reserve(0);
CHECK_EQ(a.size(), 0ul);
CHECK_EQ(a.capacity(), 0ul);
CHECK(a.data() == nullptr);
CHECK(a.empty());
a.reserve(1024);
CHECK_EQ(a.size(), 0ul);
CHECK_EQ(a.capacity(), 1024ul);
CHECK(a.data() != nullptr);
CHECK(a.begin() == a.end());
CHECK(a.empty());
a.reserve(512);
CHECK_EQ(a.size(), 0ul);
CHECK_EQ(a.capacity(), 1024ul);
CHECK(a.data() != nullptr);
CHECK(a.begin() == a.end());
CHECK(a.empty());
}
CAF_TEST(resize) {
a.resize(512);
CHECK_EQ(a.size(), 512ul);
CHECK_EQ(a.capacity(), 512ul);
CHECK(a.data() != nullptr);
CHECK(!a.empty());
b.resize(512);
CHECK_EQ(b.size(), 512ul);
CHECK_EQ(b.capacity(), 1024ul);
CHECK(b.data() != nullptr);
CHECK(!b.empty());
a.resize(1024);
std::fill(a.begin(), a.end(), 'a');
auto cnt = 0;
CHECK(std::all_of(a.begin(), a.end(), [&](char c) {
++cnt;
return c == 'a';
}));
CHECK_EQ(cnt, 1024);
a.resize(10);
cnt = 0;
CHECK(std::all_of(a.begin(), a.end(), [&](char c) {
++cnt;
return c == 'a';
}));
CHECK_EQ(cnt, 10);
a.resize(1024);
cnt = 0;
CHECK(std::all_of(a.begin(), a.end(), [&](char c) {
++cnt;
return c == 'a';
}));
CHECK_EQ(cnt, 1024);
}
CAF_TEST(push_back) {
for (auto c : vec)
a.push_back(c);
CHECK_EQ(vec.size(), a.size());
CHECK_EQ(a.capacity(), 8ul);
CHECK(a.data() != nullptr);
CHECK(!a.empty());
CHECK(std::equal(vec.begin(), vec.end(), a.begin()));
}
CAF_TEST(insert) {
for (auto c : vec)
a.insert(a.end(), c);
CHECK_EQ(as_string(a), "hallo");
a.insert(a.begin(), '!');
CHECK_EQ(as_string(a), "!hallo");
a.insert(a.begin() + 4, '-');
CHECK_EQ(as_string(a), "!hal-lo");
std::string foo = "foo:";
a.insert(a.begin() + 1, foo.begin(), foo.end());
CHECK_EQ(as_string(a), "!foo:hal-lo");
std::string bar = ":bar";
a.insert(a.end(), bar.begin(), bar.end());
CHECK_EQ(as_string(a), "!foo:hal-lo:bar");
}
CAF_TEST(shrink_to_fit) {
a.shrink_to_fit();
CHECK_EQ(a.size(), 0ul);
CHECK_EQ(a.capacity(), 0ul);
CHECK(a.data() == nullptr);
CHECK(a.empty());
}
CAF_TEST(swap) {
for (auto c : vec)
a.push_back(c);
std::swap(a, b);
CHECK_EQ(a.size(), 1024ul);
CHECK_EQ(a.capacity(), 1024ul);
CHECK(a.data() != nullptr);
CHECK_EQ(b.size(), vec.size());
CHECK_EQ(std::distance(b.begin(), b.end()),
static_cast<receive_buffer::difference_type>(vec.size()));
CHECK_EQ(b.capacity(), 8ul);
CHECK(b.data() != nullptr);
CHECK_EQ(*(b.data() + 0), 'h');
CHECK_EQ(*(b.data() + 1), 'a');
CHECK_EQ(*(b.data() + 2), 'l');
CHECK_EQ(*(b.data() + 3), 'l');
CHECK_EQ(*(b.data() + 4), 'o');
}
END_FIXTURE_SCOPE()
...@@ -94,11 +94,14 @@ caf_add_component( ...@@ -94,11 +94,14 @@ caf_add_component(
src/net/web_socket/server.cpp src/net/web_socket/server.cpp
src/net/web_socket/upper_layer.cpp src/net/web_socket/upper_layer.cpp
TEST_SOURCES TEST_SOURCES
${CMAKE_CURRENT_BINARY_DIR}/test/pem.cpp
test/net-test.cpp test/net-test.cpp
TEST_SUITES TEST_SUITES
detail.convert_ip_endpoint
detail.rfc6455 detail.rfc6455
LEGACY_TEST_SOURCES
${CMAKE_CURRENT_BINARY_DIR}/test/pem.cpp
test/net-legacy-test.cpp
LEGACY_TEST_SUITES
detail.convert_ip_endpoint
net.accept_socket net.accept_socket
net.actor_shell net.actor_shell
net.datagram_socket net.datagram_socket
......
This diff is collapsed.
#include "net-test.hpp"
#include "caf/error.hpp"
#include "caf/init_global_meta_objects.hpp"
#include "caf/net/middleman.hpp"
#include "caf/net/ssl/startup.hpp"
#include "caf/net/this_host.hpp"
#include "caf/raise_error.hpp"
#define CAF_TEST_NO_MAIN
#include "caf/test/unit_test_impl.hpp"
using namespace caf;
// -- mock_stream_transport ----------------------------------------------------
net::multiplexer& mock_stream_transport::mpx() noexcept {
return *mpx_;
}
bool mock_stream_transport::can_send_more() const noexcept {
return true;
}
bool mock_stream_transport::is_reading() const noexcept {
return max_read_size > 0;
}
void mock_stream_transport::write_later() {
// nop
}
void mock_stream_transport::shutdown() {
// nop
}
void mock_stream_transport::switch_protocol(upper_layer_ptr new_up) {
next.swap(new_up);
}
bool mock_stream_transport::switching_protocol() const noexcept {
return next != nullptr;
}
void mock_stream_transport::configure_read(net::receive_policy policy) {
min_read_size = policy.min_size;
max_read_size = policy.max_size;
}
void mock_stream_transport::begin_output() {
// nop
}
byte_buffer& mock_stream_transport::output_buffer() {
return output;
}
bool mock_stream_transport::end_output() {
return true;
}
ptrdiff_t mock_stream_transport::handle_input() {
ptrdiff_t result = 0;
auto switch_to_next_protocol = [this] {
assert(next);
// Switch to the new protocol and initialize it.
configure_read(net::receive_policy::stop());
up.reset(next.release());
if (auto err = up->start(this)) {
up.reset();
return false;
}
return true;
};
// Loop until we have drained the buffer as much as we can.
while (max_read_size > 0 && input.size() >= min_read_size) {
auto n = std::min(input.size(), size_t{max_read_size});
auto bytes = make_span(input.data(), n);
auto delta = bytes.subspan(delta_offset);
auto consumed = up->consume(bytes, delta);
if (consumed < 0) {
// Negative values indicate that the application encountered an
// unrecoverable error.
return result;
} else if (static_cast<size_t>(consumed) > n) {
// Must not happen. An application cannot handle more data then we pass
// to it.
up->abort(make_error(sec::logic_error, "consumed > buffer.size"));
return result;
} else if (consumed == 0) {
if (next) {
// When switching protocol, the new layer has never seen the data, so we
// might just re-invoke the same data again.
if (!switch_to_next_protocol())
return -1;
} else {
// See whether the next iteration would change what we pass to the
// application (max_read_size_ may have changed). Otherwise, we'll try
// again later.
delta_offset = static_cast<ptrdiff_t>(n);
if (n == std::min(input.size(), size_t{max_read_size})) {
return result;
}
// else: "Fall through".
}
} else {
if (next && !switch_to_next_protocol())
return -1;
// Shove the unread bytes to the beginning of the buffer and continue
// to the next loop iteration.
result += consumed;
auto del = static_cast<size_t>(consumed);
delta_offset = static_cast<ptrdiff_t>(n - del);
input.erase(input.begin(), input.begin() + del);
}
}
return result;
}
// -- mock_web_socket_app ------------------------------------------------------
mock_web_socket_app::mock_web_socket_app(bool request_messages_on_start)
: request_messages_on_start(request_messages_on_start) {
// nop
}
caf::error mock_web_socket_app::start(caf::net::web_socket::lower_layer* ll) {
down = ll;
if (request_messages_on_start)
down->request_messages();
return caf::none;
}
void mock_web_socket_app::prepare_send() {
// nop
}
bool mock_web_socket_app::done_sending() {
return true;
}
caf::error
mock_web_socket_app::accept(const caf::net::http::request_header& hdr) {
// Store the request information in cfg to evaluate them later.
auto& ws = cfg["web-socket"].as_dictionary();
put(ws, "method", to_rfc_string(hdr.method()));
put(ws, "path", std::string{hdr.path()});
put(ws, "query", hdr.query());
put(ws, "fragment", hdr.fragment());
put(ws, "http-version", hdr.version());
if (hdr.num_fields() > 0) {
auto& fields = ws["fields"].as_dictionary();
hdr.for_each_field([&fields](auto key, auto val) {
put(fields, std::string{key}, std::string{val});
});
}
return caf::none;
}
void mock_web_socket_app::abort(const caf::error& reason) {
abort_reason = reason;
}
ptrdiff_t mock_web_socket_app::consume_text(std::string_view text) {
text_input.insert(text_input.end(), text.begin(), text.end());
return static_cast<ptrdiff_t>(text.size());
}
ptrdiff_t mock_web_socket_app::consume_binary(caf::byte_span bytes) {
binary_input.insert(binary_input.end(), bytes.begin(), bytes.end());
return static_cast<ptrdiff_t>(bytes.size());
}
// -- barrier ------------------------------------------------------------------
void barrier::arrive_and_wait() {
std::unique_lock<std::mutex> guard{mx_};
auto new_count = ++count_;
if (new_count == num_threads_) {
cv_.notify_all();
} else if (new_count > num_threads_) {
count_ = 1;
cv_.wait(guard, [this] { return count_.load() == num_threads_; });
} else {
cv_.wait(guard, [this] { return count_.load() == num_threads_; });
}
}
// -- main --------------------------------------------------------------------
int main(int argc, char** argv) {
net::this_host::startup();
net::ssl::startup();
using namespace caf;
net::middleman::init_global_meta_objects();
core::init_global_meta_objects();
auto result = test::main(argc, argv);
net::ssl::cleanup();
net::this_host::cleanup();
return result;
}
#include "net-test.hpp" // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/error.hpp"
#include "caf/init_global_meta_objects.hpp"
#include "caf/net/middleman.hpp" #include "caf/net/middleman.hpp"
#include "caf/net/ssl/startup.hpp" #include "caf/test/caf_test_main.hpp"
#include "caf/net/this_host.hpp"
#include "caf/raise_error.hpp"
#define CAF_TEST_NO_MAIN CAF_TEST_MAIN(caf::net::middleman)
#include "caf/test/unit_test_impl.hpp"
using namespace caf;
// -- mock_stream_transport ----------------------------------------------------
net::multiplexer& mock_stream_transport::mpx() noexcept {
return *mpx_;
}
bool mock_stream_transport::can_send_more() const noexcept {
return true;
}
bool mock_stream_transport::is_reading() const noexcept {
return max_read_size > 0;
}
void mock_stream_transport::write_later() {
// nop
}
void mock_stream_transport::shutdown() {
// nop
}
void mock_stream_transport::switch_protocol(upper_layer_ptr new_up) {
next.swap(new_up);
}
bool mock_stream_transport::switching_protocol() const noexcept {
return next != nullptr;
}
void mock_stream_transport::configure_read(net::receive_policy policy) {
min_read_size = policy.min_size;
max_read_size = policy.max_size;
}
void mock_stream_transport::begin_output() {
// nop
}
byte_buffer& mock_stream_transport::output_buffer() {
return output;
}
bool mock_stream_transport::end_output() {
return true;
}
ptrdiff_t mock_stream_transport::handle_input() {
ptrdiff_t result = 0;
auto switch_to_next_protocol = [this] {
assert(next);
// Switch to the new protocol and initialize it.
configure_read(net::receive_policy::stop());
up.reset(next.release());
if (auto err = up->start(this)) {
up.reset();
return false;
}
return true;
};
// Loop until we have drained the buffer as much as we can.
while (max_read_size > 0 && input.size() >= min_read_size) {
auto n = std::min(input.size(), size_t{max_read_size});
auto bytes = make_span(input.data(), n);
auto delta = bytes.subspan(delta_offset);
auto consumed = up->consume(bytes, delta);
if (consumed < 0) {
// Negative values indicate that the application encountered an
// unrecoverable error.
return result;
} else if (static_cast<size_t>(consumed) > n) {
// Must not happen. An application cannot handle more data then we pass
// to it.
up->abort(make_error(sec::logic_error, "consumed > buffer.size"));
return result;
} else if (consumed == 0) {
if (next) {
// When switching protocol, the new layer has never seen the data, so we
// might just re-invoke the same data again.
if (!switch_to_next_protocol())
return -1;
} else {
// See whether the next iteration would change what we pass to the
// application (max_read_size_ may have changed). Otherwise, we'll try
// again later.
delta_offset = static_cast<ptrdiff_t>(n);
if (n == std::min(input.size(), size_t{max_read_size})) {
return result;
}
// else: "Fall through".
}
} else {
if (next && !switch_to_next_protocol())
return -1;
// Shove the unread bytes to the beginning of the buffer and continue
// to the next loop iteration.
result += consumed;
auto del = static_cast<size_t>(consumed);
delta_offset = static_cast<ptrdiff_t>(n - del);
input.erase(input.begin(), input.begin() + del);
}
}
return result;
}
// -- mock_web_socket_app ------------------------------------------------------
mock_web_socket_app::mock_web_socket_app(bool request_messages_on_start)
: request_messages_on_start(request_messages_on_start) {
// nop
}
caf::error mock_web_socket_app::start(caf::net::web_socket::lower_layer* ll) {
down = ll;
if (request_messages_on_start)
down->request_messages();
return caf::none;
}
void mock_web_socket_app::prepare_send() {
// nop
}
bool mock_web_socket_app::done_sending() {
return true;
}
caf::error
mock_web_socket_app::accept(const caf::net::http::request_header& hdr) {
// Store the request information in cfg to evaluate them later.
auto& ws = cfg["web-socket"].as_dictionary();
put(ws, "method", to_rfc_string(hdr.method()));
put(ws, "path", std::string{hdr.path()});
put(ws, "query", hdr.query());
put(ws, "fragment", hdr.fragment());
put(ws, "http-version", hdr.version());
if (hdr.num_fields() > 0) {
auto& fields = ws["fields"].as_dictionary();
hdr.for_each_field([&fields](auto key, auto val) {
put(fields, std::string{key}, std::string{val});
});
}
return caf::none;
}
void mock_web_socket_app::abort(const caf::error& reason) {
abort_reason = reason;
}
ptrdiff_t mock_web_socket_app::consume_text(std::string_view text) {
text_input.insert(text_input.end(), text.begin(), text.end());
return static_cast<ptrdiff_t>(text.size());
}
ptrdiff_t mock_web_socket_app::consume_binary(caf::byte_span bytes) {
binary_input.insert(binary_input.end(), bytes.begin(), bytes.end());
return static_cast<ptrdiff_t>(bytes.size());
}
// -- barrier ------------------------------------------------------------------
void barrier::arrive_and_wait() {
std::unique_lock<std::mutex> guard{mx_};
auto new_count = ++count_;
if (new_count == num_threads_) {
cv_.notify_all();
} else if (new_count > num_threads_) {
count_ = 1;
cv_.wait(guard, [this] { return count_.load() == num_threads_; });
} else {
cv_.wait(guard, [this] { return count_.load() == num_threads_; });
}
}
// -- main --------------------------------------------------------------------
int main(int argc, char** argv) {
net::this_host::startup();
net::ssl::startup();
using namespace caf;
net::middleman::init_global_meta_objects();
core::init_global_meta_objects();
auto result = test::main(argc, argv);
net::ssl::cleanup();
net::this_host::cleanup();
return result;
}
...@@ -4,12 +4,13 @@ ...@@ -4,12 +4,13 @@
#pragma once #pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp" #include "caf/test/block.hpp"
namespace caf::test { namespace caf::test {
/// Represents an `AND_GIVEN` block. /// Represents an `AND_GIVEN` block.
class and_given : public block { class CAF_TEST_EXPORT and_given : public block {
public: public:
using block::block; using block::block;
......
...@@ -4,12 +4,13 @@ ...@@ -4,12 +4,13 @@
#pragma once #pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp" #include "caf/test/block.hpp"
namespace caf::test { namespace caf::test {
/// Represents an `AND_THEN` block. /// Represents an `AND_THEN` block.
class and_then : public block { class CAF_TEST_EXPORT and_then : public block {
public: public:
using block::block; using block::block;
......
...@@ -4,12 +4,13 @@ ...@@ -4,12 +4,13 @@
#pragma once #pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp" #include "caf/test/block.hpp"
namespace caf::test { namespace caf::test {
/// Represents an `AND_WHEN` block. /// Represents an `AND_WHEN` block.
class and_when : public block { class CAF_TEST_EXPORT and_when : public block {
public: public:
using block::block; using block::block;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#pragma once #pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block_type.hpp" #include "caf/test/block_type.hpp"
#include "caf/test/fwd.hpp" #include "caf/test/fwd.hpp"
...@@ -13,7 +14,7 @@ ...@@ -13,7 +14,7 @@
namespace caf::test { namespace caf::test {
/// A factory for creating runnable test definitions. /// A factory for creating runnable test definitions.
class factory { class CAF_TEST_EXPORT factory {
public: public:
friend class registry; friend class registry;
......
...@@ -4,12 +4,13 @@ ...@@ -4,12 +4,13 @@
#pragma once #pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp" #include "caf/test/block.hpp"
namespace caf::test { namespace caf::test {
/// Represents a `GIVEN` block. /// Represents a `GIVEN` block.
class given : public block { class CAF_TEST_EXPORT given : public block {
public: public:
using block::block; using block::block;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#pragma once #pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block_type.hpp" #include "caf/test/block_type.hpp"
#include "caf/test/factory.hpp" #include "caf/test/factory.hpp"
#include "caf/test/fwd.hpp" #include "caf/test/fwd.hpp"
...@@ -16,7 +17,7 @@ ...@@ -16,7 +17,7 @@
namespace caf::test { namespace caf::test {
/// A registry for our factories. /// A registry for our factories.
class registry { class CAF_TEST_EXPORT registry {
public: public:
/// Maps test names to factories. Elements are sorted by the order of their /// Maps test names to factories. Elements are sorted by the order of their
/// registration. /// registration.
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/detail/format.hpp" #include "caf/detail/format.hpp"
#include "caf/detail/source_location.hpp" #include "caf/detail/source_location.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/test/binary_predicate.hpp" #include "caf/test/binary_predicate.hpp"
#include "caf/test/block_type.hpp" #include "caf/test/block_type.hpp"
#include "caf/test/fwd.hpp" #include "caf/test/fwd.hpp"
...@@ -18,7 +19,7 @@ ...@@ -18,7 +19,7 @@
namespace caf::test { namespace caf::test {
/// A runnable definition of a test case or scenario. /// A runnable definition of a test case or scenario.
class runnable { class CAF_TEST_EXPORT runnable {
public: public:
/// Creates a new runnable. /// Creates a new runnable.
/// @param ctx The test context. /// @param ctx The test context.
......
...@@ -4,12 +4,13 @@ ...@@ -4,12 +4,13 @@
#pragma once #pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/registry.hpp" #include "caf/test/registry.hpp"
namespace caf::test { namespace caf::test {
/// Implements the main loop for running tests. /// Implements the main loop for running tests.
class runner { class CAF_TEST_EXPORT runner {
public: public:
/// Bundles the result of a command line parsing operation. /// Bundles the result of a command line parsing operation.
struct parse_cli_result { struct parse_cli_result {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#pragma once #pragma once
#include "caf/detail/pp.hpp" #include "caf/detail/pp.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/test/and_given.hpp" #include "caf/test/and_given.hpp"
#include "caf/test/and_then.hpp" #include "caf/test/and_then.hpp"
#include "caf/test/and_when.hpp" #include "caf/test/and_when.hpp"
...@@ -21,7 +22,7 @@ ...@@ -21,7 +22,7 @@
namespace caf::test { namespace caf::test {
class scenario : public block { class CAF_TEST_EXPORT scenario : public block {
public: public:
using block::block; using block::block;
......
...@@ -4,11 +4,12 @@ ...@@ -4,11 +4,12 @@
#pragma once #pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp" #include "caf/test/block.hpp"
namespace caf::test { namespace caf::test {
class section : public block { class CAF_TEST_EXPORT section : public block {
public: public:
using block::block; using block::block;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#pragma once #pragma once
#include "caf/detail/pp.hpp" #include "caf/detail/pp.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp" #include "caf/test/block.hpp"
#include "caf/test/context.hpp" #include "caf/test/context.hpp"
#include "caf/test/factory.hpp" #include "caf/test/factory.hpp"
...@@ -17,7 +18,7 @@ ...@@ -17,7 +18,7 @@
namespace caf::test { namespace caf::test {
/// Represents a `TEST` block. /// Represents a `TEST` block.
class test : public block { class CAF_TEST_EXPORT test : public block {
public: public:
using block::block; using block::block;
......
...@@ -4,13 +4,14 @@ ...@@ -4,13 +4,14 @@
#pragma once #pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp" #include "caf/test/block.hpp"
#include "caf/test/block_type.hpp" #include "caf/test/block_type.hpp"
namespace caf::test { namespace caf::test {
/// Represents a `THEN` block. /// Represents a `THEN` block.
class then : public block { class CAF_TEST_EXPORT then : public block {
public: public:
using block::block; using block::block;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
namespace caf::test { namespace caf::test {
inline namespace legacy {
#ifdef CAF_ENABLE_EXCEPTIONS #ifdef CAF_ENABLE_EXCEPTIONS
...@@ -493,6 +494,7 @@ void require_bin(bool result, const char* file, size_t line, const char* expr, ...@@ -493,6 +494,7 @@ void require_bin(bool result, const char* file, size_t line, const char* expr,
const std::string& lhs, const std::string& rhs); const std::string& lhs, const std::string& rhs);
} // namespace detail } // namespace detail
} // namespace legacy
} // namespace caf::test } // namespace caf::test
// on the global namespace so that it can hidden via namespace-scoping // on the global namespace so that it can hidden via namespace-scoping
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "caf/test/unit_test.hpp" #include "caf/test/unit_test.hpp"
namespace caf::test { namespace caf::test {
inline namespace legacy {
#ifdef CAF_ENABLE_EXCEPTIONS #ifdef CAF_ENABLE_EXCEPTIONS
...@@ -657,6 +658,7 @@ int main(int argc, char** argv) { ...@@ -657,6 +658,7 @@ int main(int argc, char** argv) {
return result ? 0 : 1; return result ? 0 : 1;
} }
} // namespace legacy
} // namespace caf::test } // namespace caf::test
#ifndef CAF_TEST_NO_MAIN #ifndef CAF_TEST_NO_MAIN
......
...@@ -4,12 +4,13 @@ ...@@ -4,12 +4,13 @@
#pragma once #pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp" #include "caf/test/block.hpp"
#include "caf/test/block_type.hpp" #include "caf/test/block_type.hpp"
namespace caf::test { namespace caf::test {
class when : public block { class CAF_TEST_EXPORT when : public block {
public: public:
using block::block; using block::block;
......
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