Unverified Commit 0556808c authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #1480

Integrate the new unit testing framework
parents a1ba72cc 01188a27
......@@ -319,7 +319,8 @@ endfunction()
# ...
# )
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})
if(NOT CAF_ADD_COMPONENT_HEADERS)
message(FATAL_ERROR "Cannot add CAF component without at least one header.")
......@@ -365,6 +366,20 @@ function(caf_add_component name)
caf_add_test_suites(${tst_bin_target} ${CAF_ADD_COMPONENT_TEST_SUITES})
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})
if(CAF_ADD_COMPONENT_ENUM_TYPES)
foreach(enum_name ${CAF_ADD_COMPONENT_ENUM_TYPES})
......
......@@ -218,8 +218,12 @@ caf_add_component(
src/uuid.cpp
TEST_SOURCES
test/core-test.cpp
test/nasty.cpp
TEST_SUITES
hash.fnv
LEGACY_TEST_SOURCES
test/core-legacy-test.cpp
test/nasty.cpp
LEGACY_TEST_SUITES
action
actor_clock
actor_factory
......@@ -308,7 +312,6 @@ caf_add_component(
flow.single
function_view
handles
hash.fnv
hash.sha1
intrusive.drr_cached_queue
intrusive.drr_queue
......@@ -391,5 +394,5 @@ if(NOT CAF_USE_STD_FORMAT)
endif()
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()
#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"
#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);
}
CAF_TEST_MAIN()
......@@ -2,49 +2,57 @@
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE hash.fnv
#include "caf/hash/fnv.hpp"
#include "core-test.hpp"
#include "caf/test/test.hpp"
#include <string>
using namespace caf;
using namespace std::string_literals;
using namespace std::literals;
template <class... Ts>
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)...);
}
SUITE("hash.fnv") {
CAF_TEST(FNV hashes build incrementally) {
hash::fnv<uint32_t> f;
CHECK_EQ(f.result, 0x811C9DC5u);
TEST("hash::fnv can build hashe values incrementally") {
caf::hash::fnv<uint32_t> f;
check_eq(f.result, 0x811C9DC5u);
f.value('a');
CHECK_EQ(f.result, 0xE40C292Cu);
check_eq(f.result, 0xE40C292Cu);
f.value('b');
CHECK_EQ(f.result, 0x4D2505CAu);
check_eq(f.result, 0x4D2505CAu);
f.value('c');
CHECK_EQ(f.result, 0x1A47E90Bu);
check_eq(f.result, 0x1A47E90Bu);
f.value('d');
CHECK_EQ(f.result, 0xCE3479BDu);
check_eq(f.result, 0xCE3479BDu);
}
CAF_TEST(FNV supports uint32 hashing) {
CHECK_EQ(fnv32_hash(), 0x811C9DC5u);
CHECK_EQ(fnv32_hash("abcd"s), 0xCE3479BDu);
CHECK_EQ(fnv32_hash("C++ Actor Framework"s), 0x2FF91FE5u);
TEST("fnv::compute generates a hash value in one step") {
SECTION("32-bit") {
using hash_type = caf::hash::fnv<uint32_t>;
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) {
CHECK_EQ(fnv64_hash(), 0xCBF29CE484222325ull);
CHECK_EQ(fnv64_hash("abcd"s), 0xFC179F83EE0724DDull);
CHECK_EQ(fnv64_hash("C++ Actor Framework"s), 0xA229A760C3AF69C5ull);
struct foo {
std::string bar;
};
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(
TEST_SOURCES
test/io-test.cpp
TEST_SUITES
io.network.receive_buffer
LEGACY_TEST_SOURCES
test/io-legacy-test.cpp
LEGACY_TEST_SUITES
detail.prometheus_broker
io.basp.message_queue
io.basp_broker
......@@ -69,7 +73,6 @@ caf_add_component(
io.monitor
io.network.default_multiplexer
io.network.ip_endpoint
io.receive_buffer
io.remote_actor
io.remote_group
io.remote_spawn
......@@ -77,5 +80,5 @@ caf_add_component(
io.worker)
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()
......@@ -4,6 +4,7 @@
#pragma once
#include <algorithm>
#include <cstddef>
#include <cstring>
#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"
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);
}
CAF_TEST_MAIN(caf::io::middleman)
// 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()
......@@ -10,116 +10,119 @@ caf_add_component(
net
DEPENDENCIES
PUBLIC
$<$<CXX_COMPILER_ID:MSVC>:ws2_32>
CAF::core
OpenSSL::Crypto
OpenSSL::SSL
$<$<CXX_COMPILER_ID:MSVC>:ws2_32>
CAF::core
OpenSSL::Crypto
OpenSSL::SSL
PRIVATE
CAF::internal
CAF::internal
ENUM_TYPES
net.http.method
net.http.status
net.octet_stream.errc
net.ssl.dtls
net.ssl.errc
net.ssl.format
net.ssl.tls
net.web_socket.status
net.http.method
net.http.status
net.octet_stream.errc
net.ssl.dtls
net.ssl.errc
net.ssl.format
net.ssl.tls
net.web_socket.status
HEADERS
${CAF_NET_HEADERS}
${CAF_NET_HEADERS}
SOURCES
src/detail/convert_ip_endpoint.cpp
src/detail/pollset_updater.cpp
src/detail/rfc6455.cpp
src/net/abstract_actor_shell.cpp
src/net/actor_shell.cpp
src/net/datagram_socket.cpp
src/net/dsl/config_base.cpp
src/net/generic_lower_layer.cpp
src/net/generic_upper_layer.cpp
src/net/http/config.cpp
src/net/http/lower_layer.cpp
src/net/http/method.cpp
src/net/http/request.cpp
src/net/http/request_header.cpp
src/net/http/responder.cpp
src/net/http/response.cpp
src/net/http/route.cpp
src/net/http/router.cpp
src/net/http/server.cpp
src/net/http/server_factory.cpp
src/net/http/status.cpp
src/net/http/upper_layer.cpp
src/net/http/v1.cpp
src/net/ip.cpp
src/net/lp/default_trait.cpp
src/net/lp/frame.cpp
src/net/lp/framing.cpp
src/net/lp/lower_layer.cpp
src/net/lp/upper_layer.cpp
src/net/middleman.cpp
src/net/multiplexer.cpp
src/net/network_socket.cpp
src/net/octet_stream/lower_layer.cpp
src/net/octet_stream/policy.cpp
src/net/octet_stream/transport.cpp
src/net/octet_stream/upper_layer.cpp
src/net/pipe_socket.cpp
src/net/prometheus.cpp
src/net/socket.cpp
src/net/socket_event_layer.cpp
src/net/socket_manager.cpp
src/net/ssl/connection.cpp
src/net/ssl/context.cpp
src/net/ssl/dtls.cpp
src/net/ssl/errc.cpp
src/net/ssl/format.cpp
src/net/ssl/password.cpp
src/net/ssl/startup.cpp
src/net/ssl/tcp_acceptor.cpp
src/net/ssl/tls.cpp
src/net/ssl/transport.cpp
src/net/ssl/verify.cpp
src/net/stream_socket.cpp
src/net/tcp_accept_socket.cpp
src/net/tcp_stream_socket.cpp
src/net/this_host.cpp
src/net/udp_datagram_socket.cpp
src/net/web_socket/client.cpp
src/net/web_socket/default_trait.cpp
src/net/web_socket/frame.cpp
src/net/web_socket/framing.cpp
src/net/web_socket/handshake.cpp
src/net/web_socket/lower_layer.cpp
src/net/web_socket/server.cpp
src/net/web_socket/upper_layer.cpp
src/detail/convert_ip_endpoint.cpp
src/detail/pollset_updater.cpp
src/detail/rfc6455.cpp
src/net/abstract_actor_shell.cpp
src/net/actor_shell.cpp
src/net/datagram_socket.cpp
src/net/dsl/config_base.cpp
src/net/generic_lower_layer.cpp
src/net/generic_upper_layer.cpp
src/net/http/config.cpp
src/net/http/lower_layer.cpp
src/net/http/method.cpp
src/net/http/request.cpp
src/net/http/request_header.cpp
src/net/http/responder.cpp
src/net/http/response.cpp
src/net/http/route.cpp
src/net/http/router.cpp
src/net/http/server.cpp
src/net/http/server_factory.cpp
src/net/http/status.cpp
src/net/http/upper_layer.cpp
src/net/http/v1.cpp
src/net/ip.cpp
src/net/lp/default_trait.cpp
src/net/lp/frame.cpp
src/net/lp/framing.cpp
src/net/lp/lower_layer.cpp
src/net/lp/upper_layer.cpp
src/net/middleman.cpp
src/net/multiplexer.cpp
src/net/network_socket.cpp
src/net/octet_stream/lower_layer.cpp
src/net/octet_stream/policy.cpp
src/net/octet_stream/transport.cpp
src/net/octet_stream/upper_layer.cpp
src/net/pipe_socket.cpp
src/net/prometheus.cpp
src/net/socket.cpp
src/net/socket_event_layer.cpp
src/net/socket_manager.cpp
src/net/ssl/connection.cpp
src/net/ssl/context.cpp
src/net/ssl/dtls.cpp
src/net/ssl/errc.cpp
src/net/ssl/format.cpp
src/net/ssl/password.cpp
src/net/ssl/startup.cpp
src/net/ssl/tcp_acceptor.cpp
src/net/ssl/tls.cpp
src/net/ssl/transport.cpp
src/net/ssl/verify.cpp
src/net/stream_socket.cpp
src/net/tcp_accept_socket.cpp
src/net/tcp_stream_socket.cpp
src/net/this_host.cpp
src/net/udp_datagram_socket.cpp
src/net/web_socket/client.cpp
src/net/web_socket/default_trait.cpp
src/net/web_socket/frame.cpp
src/net/web_socket/framing.cpp
src/net/web_socket/handshake.cpp
src/net/web_socket/lower_layer.cpp
src/net/web_socket/server.cpp
src/net/web_socket/upper_layer.cpp
TEST_SOURCES
${CMAKE_CURRENT_BINARY_DIR}/test/pem.cpp
test/net-test.cpp
test/net-test.cpp
TEST_SUITES
detail.convert_ip_endpoint
detail.rfc6455
net.accept_socket
net.actor_shell
net.datagram_socket
net.http.router
net.http.server
net.ip
net.length_prefix_framing
net.lp.frame
net.multiplexer
net.network_socket
net.octet_stream.transport
net.pipe_socket
net.socket
net.socket_guard
net.ssl.transport
net.stream_socket
net.tcp_socket
net.typed_actor_shell
net.udp_datagram_socket
net.web_socket.client
net.web_socket.frame
net.web_socket.framing
net.web_socket.handshake
net.web_socket.server)
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.actor_shell
net.datagram_socket
net.http.router
net.http.server
net.ip
net.length_prefix_framing
net.lp.frame
net.multiplexer
net.network_socket
net.octet_stream.transport
net.pipe_socket
net.socket
net.socket_guard
net.ssl.transport
net.stream_socket
net.tcp_socket
net.typed_actor_shell
net.udp_datagram_socket
net.web_socket.client
net.web_socket.frame
net.web_socket.framing
net.web_socket.handshake
net.web_socket.server)
......@@ -2,11 +2,9 @@
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE detail.rfc6455
#include "caf/detail/rfc6455.hpp"
#include "net-test.hpp"
#include "caf/test/test.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/span.hpp"
......@@ -18,172 +16,170 @@
using namespace caf;
namespace {
struct fixture {
using impl = detail::rfc6455;
using impl = detail::rfc6455;
auto bytes(std::initializer_list<uint8_t> xs) {
byte_buffer result;
for (auto x : xs)
result.emplace_back(static_cast<std::byte>(x));
return result;
}
template <class T>
auto take(const T& xs, size_t num_bytes) {
auto n = std::min(xs.size(), num_bytes);
return std::vector<typename T::value_type>{xs.begin(), xs.begin() + n};
}
};
SUITE("detail.rfc6455") {
} // namespace
auto bytes(std::initializer_list<uint8_t> xs) {
byte_buffer result;
for (auto x : xs)
result.emplace_back(static_cast<std::byte>(x));
return result;
}
BEGIN_FIXTURE_SCOPE(fixture)
template <class T>
auto take(const T& xs, size_t num_bytes) {
auto n = std::min(xs.size(), num_bytes);
return std::vector<typename T::value_type>{xs.begin(), xs.begin() + n};
}
TEST_CASE("masking") {
TEST("masking") {
auto key = uint32_t{0xDEADC0DE};
auto data = bytes({0x12, 0x34, 0x45, 0x67, 0x89, 0x9A});
auto masked_data = data;
MESSAGE("masking XORs the repeated key to data");
impl::mask_data(key, masked_data);
CHECK_EQ(masked_data, bytes({
0x12 ^ 0xDE,
0x34 ^ 0xAD,
0x45 ^ 0xC0,
0x67 ^ 0xDE,
0x89 ^ 0xDE,
0x9A ^ 0xAD,
}));
MESSAGE("masking masked data again gives the original data");
impl::mask_data(key, masked_data);
CHECK_EQ(masked_data, data);
SECTION("masking XORs the repeated key to data") {
auto masked_data = data;
impl::mask_data(key, masked_data);
check_eq(masked_data, bytes({
0x12 ^ 0xDE,
0x34 ^ 0xAD,
0x45 ^ 0xC0,
0x67 ^ 0xDE,
0x89 ^ 0xDE,
0x9A ^ 0xAD,
}));
}
SECTION("masking masked data again gives the original data") {
auto masked_data = data;
impl::mask_data(key, masked_data);
impl::mask_data(key, masked_data);
check_eq(masked_data, data);
}
}
TEST_CASE("decoding a frame with RSV bits fails") {
TEST("decoding a frame with RSV bits fails") {
std::vector<uint8_t> data;
byte_buffer out = bytes({
0xF2, // FIN + RSV + binary frame opcode
0x00, // data size = 0
});
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), -1);
check_eq(impl::decode_header(out, hdr), -1);
}
TEST_CASE("no mask key and no data") {
TEST("decode a header with no mask key and no data") {
std::vector<uint8_t> data;
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0, as_bytes(make_span(data)), out);
CHECK_EQ(out, bytes({
check_eq(out, bytes({
0x82, // FIN + binary frame opcode
0x00, // data size = 0
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 2);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0u);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 2);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0u);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
TEST_CASE("valid mask key and no data") {
TEST("decode a header with valid mask key but no data") {
std::vector<uint8_t> data;
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0xDEADC0DE,
as_bytes(make_span(data)), out);
CHECK_EQ(out, bytes({
check_eq(out, bytes({
0x82, // FIN + binary frame opcode
0x80, // MASKED + data size = 0
0xDE, 0xAD, 0xC0, 0xDE, // mask key,
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 6);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0xDEADC0DE);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 6);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0xDEADC0DE);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
TEST_CASE("no mask key plus small data") {
TEST("decode a header with no mask key plus small data") {
std::vector<uint8_t> data{0x12, 0x34, 0x45, 0x67};
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0, as_bytes(make_span(data)), out);
CHECK_EQ(out, bytes({
check_eq(out, bytes({
0x82, // FIN + binary frame opcode
0x04, // data size = 4
0x12, 0x34, 0x45, 0x67, // masked data
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 2);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0u);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 2);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0u);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
TEST_CASE("valid mask key plus small data") {
TEST("decode a header with valid mask key plus small data") {
std::vector<uint8_t> data{0x12, 0x34, 0x45, 0x67};
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0xDEADC0DE,
as_bytes(make_span(data)), out);
CHECK_EQ(out, bytes({
check_eq(out, bytes({
0x82, // FIN + binary frame opcode
0x84, // MASKED + data size = 4
0xDE, 0xAD, 0xC0, 0xDE, // mask key,
0x12, 0x34, 0x45, 0x67, // masked data
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 6);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0xDEADC0DE);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 6);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0xDEADC0DE);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
TEST_CASE("no mask key plus upper bound on small data") {
TEST("decode a header with no mask key plus upper bound on small data") {
std::vector<uint8_t> data;
data.resize(125, 0xFF);
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0, as_bytes(make_span(data)), out);
CHECK_EQ(take(out, 6), bytes({
check_eq(take(out, 6), bytes({
0x82, // FIN + binary frame opcode
0x7D, // data size = 125
0xFF, 0xFF, 0xFF, 0xFF, // masked data
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 2);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0u);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 2);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0u);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
TEST_CASE("valid mask key plus upper bound on small data") {
TEST("decode a header with valid mask key plus upper bound on small data") {
std::vector<uint8_t> data;
data.resize(125, 0xFF);
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0xDEADC0DE,
as_bytes(make_span(data)), out);
CHECK_EQ(take(out, 10), bytes({
check_eq(take(out, 10), bytes({
0x82, // FIN + binary frame opcode
0xFD, // MASKED + data size = 125
0xDE, 0xAD, 0xC0, 0xDE, // mask key,
0xFF, 0xFF, 0xFF, 0xFF, // masked data
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 6);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0xDEADC0DE);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 6);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0xDEADC0DE);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
TEST_CASE("no mask key plus medium data") {
TEST("decode a header with no mask key plus medium data") {
std::vector<uint8_t> data;
data.resize(126, 0xFF);
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0, as_bytes(make_span(data)), out);
CHECK_EQ(take(out, 8),
check_eq(take(out, 8),
bytes({
0x82, // FIN + binary frame opcode
0x7E, // 126 -> uint16 size
......@@ -191,20 +187,20 @@ TEST_CASE("no mask key plus medium data") {
0xFF, 0xFF, 0xFF, 0xFF, // first 4 masked bytes
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 4);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0u);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 4);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0u);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
TEST_CASE("valid mask key plus medium data") {
TEST("decode a header with valid mask key plus medium data") {
std::vector<uint8_t> data;
data.resize(126, 0xFF);
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0xDEADC0DE,
as_bytes(make_span(data)), out);
CHECK_EQ(take(out, 12),
check_eq(take(out, 12),
bytes({
0x82, // FIN + binary frame opcode
0xFE, // MASKED + 126 -> uint16 size
......@@ -213,19 +209,19 @@ TEST_CASE("valid mask key plus medium data") {
0xFF, 0xFF, 0xFF, 0xFF, // first 4 masked bytes
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 8);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0xDEADC0DE);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 8);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0xDEADC0DE);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
TEST_CASE("no mask key plus upper bound on medium data") {
TEST("decode a header with no mask key plus upper bound on medium data") {
std::vector<uint8_t> data;
data.resize(65535, 0xFF);
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0, as_bytes(make_span(data)), out);
CHECK_EQ(take(out, 8),
check_eq(take(out, 8),
bytes({
0x82, // FIN + binary frame opcode
0x7E, // 126 -> uint16 size
......@@ -233,20 +229,20 @@ TEST_CASE("no mask key plus upper bound on medium data") {
0xFF, 0xFF, 0xFF, 0xFF, // first 4 masked bytes
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 4);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0u);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 4);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0u);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
TEST_CASE("valid mask key plus upper bound on medium data") {
TEST("decode a header with valid mask key plus upper bound on medium data") {
std::vector<uint8_t> data;
data.resize(65535, 0xFF);
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0xDEADC0DE,
as_bytes(make_span(data)), out);
CHECK_EQ(take(out, 12),
check_eq(take(out, 12),
bytes({
0x82, // FIN + binary frame opcode
0xFE, // 126 -> uint16 size
......@@ -255,19 +251,19 @@ TEST_CASE("valid mask key plus upper bound on medium data") {
0xFF, 0xFF, 0xFF, 0xFF, // first 4 masked bytes
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 8);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0xDEADC0DE);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 8);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0xDEADC0DE);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
TEST_CASE("no mask key plus large data") {
TEST("decode a header with no mask key plus large data") {
std::vector<uint8_t> data;
data.resize(65536, 0xFF);
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0, as_bytes(make_span(data)), out);
CHECK_EQ(take(out, 14),
check_eq(take(out, 14),
bytes({
0x82, // FIN + binary frame opcode
0x7F, // 127 -> uint64 size
......@@ -275,20 +271,20 @@ TEST_CASE("no mask key plus large data") {
0xFF, 0xFF, 0xFF, 0xFF, // first 4 masked bytes
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 10);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0u);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 10);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0u);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
TEST_CASE("valid mask key plus large data") {
TEST("decode a header with valid mask key plus large data") {
std::vector<uint8_t> data;
data.resize(65536, 0xFF);
byte_buffer out;
impl::assemble_frame(impl::binary_frame, 0xDEADC0DE,
as_bytes(make_span(data)), out);
CHECK_EQ(take(out, 18),
check_eq(take(out, 18),
bytes({
0x82, // FIN + binary frame opcode
0xFF, // MASKED + 127 -> uint64 size
......@@ -297,11 +293,11 @@ TEST_CASE("valid mask key plus large data") {
0xFF, 0xFF, 0xFF, 0xFF, // first 4 masked bytes
}));
impl::header hdr;
CHECK_EQ(impl::decode_header(out, hdr), 14);
CHECK_EQ(hdr.fin, true);
CHECK_EQ(hdr.mask_key, 0xDEADC0DE);
CHECK_EQ(hdr.opcode, impl::binary_frame);
CHECK_EQ(hdr.payload_len, data.size());
check_eq(impl::decode_header(out, hdr), 14);
check_eq(hdr.fin, true);
check_eq(hdr.mask_key, 0xDEADC0DE);
check_eq(hdr.opcode, impl::binary_frame);
check_eq(hdr.payload_len, data.size());
}
END_FIXTURE_SCOPE()
} // SUITE("detail.rfc6455")
#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/ssl/startup.hpp"
#include "caf/net/this_host.hpp"
#include "caf/raise_error.hpp"
#include "caf/test/caf_test_main.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;
}
CAF_TEST_MAIN(caf::net::middleman)
......@@ -4,12 +4,13 @@
#pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp"
namespace caf::test {
/// Represents an `AND_GIVEN` block.
class and_given : public block {
class CAF_TEST_EXPORT and_given : public block {
public:
using block::block;
......
......@@ -4,12 +4,13 @@
#pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp"
namespace caf::test {
/// Represents an `AND_THEN` block.
class and_then : public block {
class CAF_TEST_EXPORT and_then : public block {
public:
using block::block;
......
......@@ -4,12 +4,13 @@
#pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp"
namespace caf::test {
/// Represents an `AND_WHEN` block.
class and_when : public block {
class CAF_TEST_EXPORT and_when : public block {
public:
using block::block;
......
......@@ -4,6 +4,7 @@
#pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block_type.hpp"
#include "caf/test/fwd.hpp"
......@@ -13,7 +14,7 @@
namespace caf::test {
/// A factory for creating runnable test definitions.
class factory {
class CAF_TEST_EXPORT factory {
public:
friend class registry;
......@@ -34,7 +35,7 @@ public:
virtual std::unique_ptr<runnable> make(context_ptr state) = 0;
protected:
std::unique_ptr<factory> next_;
factory* next_ = nullptr;
std::string_view suite_name_;
std::string_view description_;
block_type type_;
......
......@@ -4,12 +4,13 @@
#pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp"
namespace caf::test {
/// Represents a `GIVEN` block.
class given : public block {
class CAF_TEST_EXPORT given : public block {
public:
using block::block;
......
......@@ -4,6 +4,7 @@
#pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block_type.hpp"
#include "caf/test/factory.hpp"
#include "caf/test/fwd.hpp"
......@@ -16,8 +17,16 @@
namespace caf::test {
/// A registry for our factories.
class registry {
class CAF_TEST_EXPORT registry {
public:
constexpr registry() noexcept = default;
registry(const registry&) = delete;
registry& operator=(const registry&) = delete;
~registry();
/// Maps test names to factories. Elements are sorted by the order of their
/// registration.
using tests_map = unordered_flat_map<std::string_view, factory*>;
......@@ -37,21 +46,16 @@ public:
this->type_);
}
};
auto ptr = std::make_unique<impl>(suite_name, description, type);
auto result = reinterpret_cast<ptrdiff_t>(ptr.get());
if (head_ == nullptr) {
head_ = std::move(ptr);
tail_ = head_.get();
} else {
tail_->next_ = std::move(ptr);
tail_ = tail_->next_.get();
}
return result;
return instance().add(new impl(suite_name, description, type));
}
private:
static std::unique_ptr<factory> head_;
static factory* tail_;
ptrdiff_t add(factory* new_factory);
static registry& instance();
factory* head_ = nullptr;
factory* tail_ = nullptr;
};
} // namespace caf::test
......@@ -8,6 +8,7 @@
#include "caf/deep_to_string.hpp"
#include "caf/detail/format.hpp"
#include "caf/detail/source_location.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/test/binary_predicate.hpp"
#include "caf/test/block_type.hpp"
#include "caf/test/fwd.hpp"
......@@ -18,7 +19,7 @@
namespace caf::test {
/// A runnable definition of a test case or scenario.
class runnable {
class CAF_TEST_EXPORT runnable {
public:
/// Creates a new runnable.
/// @param ctx The test context.
......
......@@ -4,12 +4,13 @@
#pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/registry.hpp"
namespace caf::test {
/// Implements the main loop for running tests.
class runner {
class CAF_TEST_EXPORT runner {
public:
/// Bundles the result of a command line parsing operation.
struct parse_cli_result {
......
......@@ -5,6 +5,7 @@
#pragma once
#include "caf/detail/pp.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/test/and_given.hpp"
#include "caf/test/and_then.hpp"
#include "caf/test/and_when.hpp"
......@@ -21,7 +22,7 @@
namespace caf::test {
class scenario : public block {
class CAF_TEST_EXPORT scenario : public block {
public:
using block::block;
......
......@@ -4,11 +4,12 @@
#pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp"
namespace caf::test {
class section : public block {
class CAF_TEST_EXPORT section : public block {
public:
using block::block;
......
......@@ -5,6 +5,7 @@
#pragma once
#include "caf/detail/pp.hpp"
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp"
#include "caf/test/context.hpp"
#include "caf/test/factory.hpp"
......@@ -17,7 +18,7 @@
namespace caf::test {
/// Represents a `TEST` block.
class test : public block {
class CAF_TEST_EXPORT test : public block {
public:
using block::block;
......
......@@ -4,13 +4,14 @@
#pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp"
#include "caf/test/block_type.hpp"
namespace caf::test {
/// Represents a `THEN` block.
class then : public block {
class CAF_TEST_EXPORT then : public block {
public:
using block::block;
......
......@@ -26,6 +26,7 @@
#include "caf/detail/type_traits.hpp"
namespace caf::test {
inline namespace legacy {
#ifdef CAF_ENABLE_EXCEPTIONS
......@@ -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);
} // namespace detail
} // namespace legacy
} // namespace caf::test
// on the global namespace so that it can hidden via namespace-scoping
......
......@@ -28,6 +28,7 @@
#include "caf/test/unit_test.hpp"
namespace caf::test {
inline namespace legacy {
#ifdef CAF_ENABLE_EXCEPTIONS
......@@ -657,6 +658,7 @@ int main(int argc, char** argv) {
return result ? 0 : 1;
}
} // namespace legacy
} // namespace caf::test
#ifndef CAF_TEST_NO_MAIN
......
......@@ -4,12 +4,13 @@
#pragma once
#include "caf/detail/test_export.hpp"
#include "caf/test/block.hpp"
#include "caf/test/block_type.hpp"
namespace caf::test {
class when : public block {
class CAF_TEST_EXPORT when : public block {
public:
using block::block;
......
......@@ -9,9 +9,17 @@
namespace caf::test {
registry::~registry() {
while (head_ != nullptr) {
auto next = head_->next_;
delete head_;
head_ = next;
}
}
registry::suites_map registry::suites() {
suites_map result;
for (auto ptr = head_.get(); ptr != nullptr; ptr = ptr->next_.get()) {
for (auto* ptr = instance().head_; ptr != nullptr; ptr = ptr->next_) {
auto& suite = result[ptr->suite_name_];
if (auto [iter, ok] = suite.emplace(ptr->description_, ptr); !ok) {
auto msg = detail::format("duplicate test name in suite {}: {}",
......@@ -22,8 +30,23 @@ registry::suites_map registry::suites() {
return result;
}
std::unique_ptr<factory> registry::head_;
ptrdiff_t registry::add(factory* new_factory) {
if (head_ == nullptr)
head_ = new_factory;
else
tail_->next_ = new_factory;
tail_ = new_factory;
return reinterpret_cast<ptrdiff_t>(new_factory);
}
namespace {
registry default_instance;
factory* registry::tail_;
} // namespace
registry& registry::instance() {
return default_instance;
}
} // namespace caf::test
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