Initial Release
This commit is contained in:
commit
5a00e06cb9
81 changed files with 12670 additions and 0 deletions
315
test/cbor_match.cpp
Normal file
315
test/cbor_match.cpp
Normal file
|
@ -0,0 +1,315 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include <thespian/instance.hpp>
|
||||
#include <thespian/trace.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
using cbor::A;
|
||||
using cbor::array;
|
||||
using cbor::buffer;
|
||||
using cbor::extract;
|
||||
using cbor::M;
|
||||
using cbor::map;
|
||||
using cbor::type;
|
||||
using std::move;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
using std::stringstream;
|
||||
using thespian::context;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::link;
|
||||
using thespian::ok;
|
||||
using thespian::result;
|
||||
|
||||
namespace {
|
||||
|
||||
auto test() -> result {
|
||||
auto verbose = env().is("verbose");
|
||||
auto log = env().proc("log");
|
||||
result _;
|
||||
|
||||
buffer m = array("five", 5, "four", 4, array("three", 3));
|
||||
|
||||
if (verbose) {
|
||||
_ = log.send("message:", m.to_json());
|
||||
_ = log.send("buffer:", m.hexdump());
|
||||
}
|
||||
|
||||
check(m.hexdump() ==
|
||||
"21: 85 64 66 69 76 65 05 64 66 6f 75 72 04 82 65 74 68 72 65 65 03");
|
||||
|
||||
check(m.to_json() == R"(["five",5,"four",4,["three",3]])");
|
||||
|
||||
check(m(type::string, type::number, type::string, type::number, type::array));
|
||||
check(!m(type::string, type::number, type::string, type::number));
|
||||
check(!m(type::number, type::string, type::number, type::array, type::any));
|
||||
check(m(type::any, type::number, type::string, type::number, type::any));
|
||||
|
||||
check(m("five", type::number, type::string, 4, type::any));
|
||||
check(m("five", 5, "four", 4, type::array));
|
||||
check(m("five", 5, type::more));
|
||||
check(m("five", 5, "four", 4, type::array, type::more));
|
||||
check(!m("five", 5, "four", 4, type::array, type::any));
|
||||
|
||||
check(array().is_null());
|
||||
check(!array(1).is_null());
|
||||
{
|
||||
buffer b;
|
||||
b.array_header(5)
|
||||
.push("five")
|
||||
.push(5)
|
||||
.push("four")
|
||||
.push(4)
|
||||
.array_header(2)
|
||||
.push("three")
|
||||
.push(3);
|
||||
check(m == b);
|
||||
}
|
||||
{
|
||||
buffer b = array(false, true, 5, "five");
|
||||
check(b == array(false, true, 5, "five"));
|
||||
check(b(false, true, 5, "five"));
|
||||
check(!b(true, false, 5, "five"));
|
||||
bool t = false;
|
||||
check(b(extract(t), true, 5, "five"));
|
||||
check(!t);
|
||||
check(b(false, extract(t), 5, "five"));
|
||||
check(t);
|
||||
}
|
||||
{
|
||||
buffer::range r;
|
||||
check(
|
||||
m(type::string, type::number, type::string, type::number, extract(r)));
|
||||
check(r(type::string, type::number));
|
||||
check(r("three", 3));
|
||||
}
|
||||
{
|
||||
int64_t i{};
|
||||
check(m(type::string, type::number, type::string, type::number,
|
||||
A(type::string, extract(i))));
|
||||
check(i == 3);
|
||||
}
|
||||
{
|
||||
buffer ref = array(array(124), array("blip"));
|
||||
check(ref(A(type::more), A(type::more)));
|
||||
}
|
||||
{
|
||||
int i{0};
|
||||
auto dump = [&](string_view val) {
|
||||
++i;
|
||||
if (verbose)
|
||||
_ = log.send("value", i, ":", val);
|
||||
};
|
||||
buffer a = array("five", "four", "three", "two", "one");
|
||||
for (const auto val : a)
|
||||
dump(val);
|
||||
check(i == 5);
|
||||
}
|
||||
{
|
||||
int i{0};
|
||||
auto dump = [&](auto val) {
|
||||
++i;
|
||||
if (verbose)
|
||||
_ = log.send("value", i, ":", val);
|
||||
};
|
||||
for (const auto val : m)
|
||||
val.visit(dump);
|
||||
check(i == 5);
|
||||
|
||||
buffer::range r;
|
||||
check(m(type::any, type::any, type::any, type::any, extract(r)));
|
||||
for (const auto val : r)
|
||||
val.visit(dump);
|
||||
check(i == 7);
|
||||
}
|
||||
{
|
||||
buffer a = array(array(2, 3, 4, 5), array(6, 7, 8, 9));
|
||||
check(a(A(2, 3, 4, 5), A(6, 7, 8, 9)));
|
||||
|
||||
buffer::range a1;
|
||||
buffer::range a2;
|
||||
check(a(extract(a1), extract(a2)));
|
||||
|
||||
int i{0};
|
||||
auto dump = [&](uint32_t val) {
|
||||
++i;
|
||||
if (verbose)
|
||||
_ = log.send("value", i, ":", val);
|
||||
};
|
||||
for (const auto val : a1)
|
||||
dump(val);
|
||||
}
|
||||
{
|
||||
buffer a = array(array(2, 3, 4, 5), array(6, 7, 8, 9));
|
||||
buffer::range a1;
|
||||
buffer::range a2;
|
||||
check(a(extract(a1), extract(a2)));
|
||||
|
||||
buffer b = array(a1);
|
||||
check(b(A(2, 3, 4, 5)));
|
||||
|
||||
buffer b2;
|
||||
b2.push(a2);
|
||||
check(b2(6, 7, 8, 9));
|
||||
}
|
||||
{
|
||||
buffer a = array(1, 2, map(3, array(3)));
|
||||
|
||||
if (verbose) {
|
||||
_ = log.send("map buffer:", a.hexdump());
|
||||
_ = log.send("map json:", a.to_json());
|
||||
}
|
||||
check(a(1, 2, M(3, A(3))));
|
||||
buffer::range a1;
|
||||
check(a(1, 2, extract(a1)));
|
||||
if (verbose)
|
||||
_ = log.send("map range json:", a1.to_json());
|
||||
check(a1.to_json() == "{3:[3]}");
|
||||
}
|
||||
{
|
||||
buffer a;
|
||||
a.push_json(R"(["five", 5, "four", 4, ["three", 3]])");
|
||||
if (verbose) {
|
||||
_ = log.send("buffer:", a.hexdump());
|
||||
_ = log.send("message:", a.to_json());
|
||||
}
|
||||
check(a.hexdump() == "21: 85 64 66 69 76 65 05 64 66 6f 75 72 04 82 "
|
||||
"65 74 68 72 65 65 03");
|
||||
}
|
||||
{
|
||||
buffer a;
|
||||
a.push_json(R"({"five": 5, "four": 4, "three": [{3:3}]})");
|
||||
string json = a.to_json();
|
||||
if (verbose) {
|
||||
_ = log.send("buffer:", a.hexdump());
|
||||
_ = log.send("json:", json);
|
||||
}
|
||||
check(a.hexdump() == "23: a3 64 66 69 76 65 05 64 66 6f 75 72 04 65 "
|
||||
"74 68 72 65 65 81 a1 03 03");
|
||||
check(json == R"({"five":5,"four":4,"three":[{3:3}]})");
|
||||
}
|
||||
{
|
||||
buffer a = array("string\r\n", "\tstring\t", "\r\nstring");
|
||||
string json = a.to_json();
|
||||
if (verbose) {
|
||||
_ = log.send("escaped buffer:", a.hexdump());
|
||||
_ = log.send("escaped json:", json);
|
||||
}
|
||||
check(a.hexdump() == "28: 83 68 73 74 72 69 6e 67 0d 0a 68 09 73 74 "
|
||||
"72 69 6e 67 09 68 0d 0a 73 74 72 69 6e 67");
|
||||
check(json == R"(["string\r\n","\tstring\t","\r\nstring"])");
|
||||
}
|
||||
{
|
||||
buffer a;
|
||||
a.push_json(R"(["string\r\n", "\tstring\t", "\r\nstring"])");
|
||||
string json = a.to_json();
|
||||
if (verbose) {
|
||||
_ = log.send("escaped2 buffer:", a.hexdump());
|
||||
_ = log.send("escaped2 json:", json);
|
||||
}
|
||||
check(a.hexdump() == "28: 83 68 73 74 72 69 6e 67 0d 0a 68 09 73 74 "
|
||||
"72 69 6e 67 09 68 0d 0a 73 74 72 69 6e 67");
|
||||
check(json == R"(["string\r\n","\tstring\t","\r\nstring"])");
|
||||
}
|
||||
{
|
||||
buffer a = array(array());
|
||||
buffer::range r;
|
||||
check(a(extract(r)));
|
||||
int i{0};
|
||||
auto dump = [&](uint32_t /*val*/) { ++i; };
|
||||
for (const auto v1 : r)
|
||||
dump(v1);
|
||||
check(i == 0);
|
||||
check(r.is_null());
|
||||
check(r.to_json() == "null");
|
||||
}
|
||||
{
|
||||
int64_t i{-1};
|
||||
uint64_t sz{0};
|
||||
buffer a = array(i, sz);
|
||||
check(a(extract(i), extract(sz)));
|
||||
check(i == -1);
|
||||
check(sz == 0);
|
||||
}
|
||||
{
|
||||
int64_t i{-1};
|
||||
uint64_t sz{0};
|
||||
buffer a = array(i);
|
||||
check(a(extract(i)));
|
||||
check(not a(extract(sz)));
|
||||
}
|
||||
{
|
||||
int32_t i{-1};
|
||||
uint32_t sz{0};
|
||||
buffer a = array(i);
|
||||
check(a(extract(i)));
|
||||
check(not a(extract(sz)));
|
||||
}
|
||||
{
|
||||
int16_t i{-1};
|
||||
uint16_t sz{0};
|
||||
buffer a = array(i);
|
||||
check(a(extract(i)));
|
||||
check(not a(extract(sz)));
|
||||
}
|
||||
{
|
||||
int8_t i{-1};
|
||||
uint8_t sz{0};
|
||||
buffer a = array(i);
|
||||
check(a(extract(i)));
|
||||
check(not a(extract(sz)));
|
||||
}
|
||||
{
|
||||
uint16_t ui{1000};
|
||||
uint8_t sz{0};
|
||||
buffer a = array(ui);
|
||||
check(a(extract(ui)));
|
||||
check(not a(extract(sz)));
|
||||
}
|
||||
{
|
||||
uint64_t ui{18446744073709551615ULL};
|
||||
int64_t i{0};
|
||||
buffer a = array(ui);
|
||||
check(a(extract(ui)));
|
||||
check(not a(extract(i)));
|
||||
}
|
||||
{
|
||||
buffer a = array(array(1, 2));
|
||||
buffer b = array(array(1, 2));
|
||||
buffer::range ra;
|
||||
check(a(extract(ra)));
|
||||
buffer::range rb;
|
||||
check(b(extract(rb)));
|
||||
check(ra == rb);
|
||||
}
|
||||
{
|
||||
buffer a = array(array(1));
|
||||
buffer b = array(array(1, 2));
|
||||
buffer::range ra;
|
||||
check(a(extract(ra)));
|
||||
buffer::range rb;
|
||||
check(b(extract(rb)));
|
||||
check(not(ra == rb));
|
||||
}
|
||||
if (verbose)
|
||||
_ = log.send("done");
|
||||
return ok();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto cbor_match(context &ctx, bool &result, env_t env_) -> ::result {
|
||||
return to_result(ctx.spawn_link(
|
||||
[=]() {
|
||||
link(env().proc("log"));
|
||||
return test();
|
||||
},
|
||||
[&](auto s) {
|
||||
check(s == "noreceive");
|
||||
result = true;
|
||||
},
|
||||
"cbor_match", move(env_)));
|
||||
};
|
196
test/debug.cpp
Normal file
196
test/debug.cpp
Normal file
|
@ -0,0 +1,196 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "thespian/handle.hpp"
|
||||
#include <thespian/debug.hpp>
|
||||
#include <thespian/instance.hpp>
|
||||
#include <thespian/socket.hpp>
|
||||
#include <thespian/tcp.hpp>
|
||||
#include <thespian/trace.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
using cbor::buffer;
|
||||
using cbor::extract;
|
||||
using std::make_shared;
|
||||
using std::make_unique;
|
||||
using std::move;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
using std::stringstream;
|
||||
using std::unique_ptr;
|
||||
using thespian::context;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::error;
|
||||
using thespian::exit;
|
||||
using thespian::expected;
|
||||
using thespian::handle;
|
||||
using thespian::link;
|
||||
using thespian::ok;
|
||||
using thespian::receive;
|
||||
using thespian::result;
|
||||
using thespian::self;
|
||||
using thespian::socket;
|
||||
using thespian::spawn_link;
|
||||
using thespian::trap;
|
||||
using thespian::unexpected;
|
||||
using thespian::tcp::connector;
|
||||
|
||||
namespace {
|
||||
|
||||
struct debuggee {
|
||||
auto receive(const handle &from, const buffer &m) {
|
||||
if (m("ping")) {
|
||||
return from.send("pong");
|
||||
}
|
||||
if (m("shutdown"))
|
||||
return exit("debuggee_shutdown");
|
||||
return unexpected(m);
|
||||
return ok();
|
||||
}
|
||||
|
||||
static auto start() -> expected<handle, error> {
|
||||
return spawn_link(
|
||||
[=]() {
|
||||
::receive([p{make_shared<debuggee>()}](auto from, auto m) {
|
||||
return p->receive(move(from), move(m));
|
||||
});
|
||||
return ok();
|
||||
},
|
||||
"debuggee");
|
||||
}
|
||||
};
|
||||
|
||||
struct controller {
|
||||
static constexpr string_view tag{"debug_test_controller"};
|
||||
handle debug_tcp;
|
||||
handle debuggee_;
|
||||
connector c;
|
||||
unique_ptr<thespian::socket> s;
|
||||
size_t pong_count{};
|
||||
bool success_{false};
|
||||
string prev_buf;
|
||||
|
||||
explicit controller(handle d, handle debuggee)
|
||||
: debug_tcp{std::move(d)}, debuggee_{std::move(debuggee)},
|
||||
c{connector::create(tag)} {
|
||||
trap(true);
|
||||
}
|
||||
|
||||
auto receive(const handle & /*from*/, const buffer &m) {
|
||||
int fd{};
|
||||
string buf;
|
||||
int written{};
|
||||
int err{};
|
||||
string_view err_msg{};
|
||||
|
||||
if (m("pong")) {
|
||||
pong_count++;
|
||||
if (pong_count == 2)
|
||||
c.connect(in6addr_loopback, 4242);
|
||||
} else if (m("connector", tag, "connected", extract(fd))) {
|
||||
s = make_unique<thespian::socket>(socket::create(tag, fd));
|
||||
s->read();
|
||||
s->write("\n");
|
||||
s->write("debuggee [\"ping\"]\n");
|
||||
} else if (m("socket", tag, "read_error", extract(err), extract(err_msg))) {
|
||||
return exit("read_error", err_msg);
|
||||
} else if (m("socket", tag, "read_complete", extract(buf))) {
|
||||
if (buf.empty()) {
|
||||
s->close();
|
||||
return ok();
|
||||
}
|
||||
s->read();
|
||||
buf.swap(prev_buf);
|
||||
buf.append(prev_buf);
|
||||
string::size_type pos{};
|
||||
while (not buf.empty() and pos != string::npos) {
|
||||
pos = buf.find_first_of('\n');
|
||||
if (pos != string::npos) {
|
||||
auto ret = self().send("dispatch", string(buf.data(), pos));
|
||||
if (not ret)
|
||||
return ret;
|
||||
buf.erase(0, pos + 1);
|
||||
}
|
||||
}
|
||||
prev_buf = buf;
|
||||
} else if (m("dispatch", extract(buf))) {
|
||||
if (buf == "debuggee [\"pong\"]") {
|
||||
s->write("debuggee [\"shutdown\"]\n");
|
||||
}
|
||||
} else if (m("socket", tag, "write_error", extract(err),
|
||||
extract(err_msg))) {
|
||||
return exit("write_error", err_msg);
|
||||
} else if (m("socket", tag, "write_complete", extract(written))) {
|
||||
;
|
||||
} else if (m("socket", tag, "closed")) {
|
||||
if (success_)
|
||||
return exit("success");
|
||||
auto ret = debuggee_.send("shutdown");
|
||||
if (not ret)
|
||||
return ret;
|
||||
return exit("closed");
|
||||
} else if (m("exit", "debuggee_shutdown")) {
|
||||
success_ = true;
|
||||
s->close();
|
||||
} else if (m("connector", tag, "cancelled"))
|
||||
return exit("cancelled");
|
||||
else if (m("connector", tag, "error", extract(buf)))
|
||||
return exit("connect_error", buf);
|
||||
|
||||
else
|
||||
return unexpected(m);
|
||||
return ok();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
auto debug(context &ctx, bool &result, env_t env_) -> ::result {
|
||||
thespian::debug::enable(ctx);
|
||||
return to_result(ctx.spawn_link(
|
||||
[&ctx]() {
|
||||
link(env().proc("log"));
|
||||
auto ret = thespian::debug::tcp::create(ctx, 4242, "");
|
||||
if (not ret)
|
||||
return to_result(ret);
|
||||
auto debug_tcp = ret.value();
|
||||
ret = spawn_link(
|
||||
[&ctx, debug_tcp]() {
|
||||
trap(true);
|
||||
::receive([&ctx, debug_tcp](auto, auto /*m*/) -> ::result {
|
||||
auto ret = debug_tcp.send("shutdown");
|
||||
if (not ret)
|
||||
return ret;
|
||||
thespian::debug::disable(ctx);
|
||||
return exit();
|
||||
});
|
||||
return ok();
|
||||
},
|
||||
"controller_guard");
|
||||
if (not ret)
|
||||
return to_result(ret);
|
||||
auto ret2 = debug_tcp.send("ping");
|
||||
if (not ret2)
|
||||
return ret2;
|
||||
ret = debuggee::start();
|
||||
if (not ret)
|
||||
return to_result(ret);
|
||||
auto debuggee = ret.value();
|
||||
ret2 = debuggee.send("ping");
|
||||
if (not ret2)
|
||||
return ret2;
|
||||
receive([p{make_shared<controller>(debug_tcp, debuggee)}](auto from,
|
||||
auto m) {
|
||||
return p->receive(move(from), move(m));
|
||||
});
|
||||
return ok();
|
||||
},
|
||||
[&](auto s) {
|
||||
if (s == "success")
|
||||
result = true;
|
||||
},
|
||||
"debug", move(env_)));
|
||||
}
|
93
test/endpoint_tcp.cpp
Normal file
93
test/endpoint_tcp.cpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "cbor/cbor.hpp"
|
||||
#include "thespian/env.hpp"
|
||||
#include "thespian/handle.hpp"
|
||||
#include <thespian/endpoint.hpp>
|
||||
#include <thespian/instance.hpp>
|
||||
#include <thespian/socket.hpp>
|
||||
#include <thespian/tcp.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
using cbor::any;
|
||||
using cbor::buffer;
|
||||
using cbor::extract;
|
||||
using std::make_shared;
|
||||
using std::move;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
using std::stringstream;
|
||||
using thespian::context;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::exit;
|
||||
using thespian::handle;
|
||||
using thespian::link;
|
||||
using thespian::ok;
|
||||
using thespian::receive;
|
||||
using thespian::result;
|
||||
using thespian::unexpected;
|
||||
using thespian::endpoint::tcp::listen;
|
||||
|
||||
namespace {
|
||||
|
||||
struct controller {
|
||||
handle ep_listen;
|
||||
handle ep_connect;
|
||||
size_t ping_count{};
|
||||
size_t pong_count{};
|
||||
bool success_{false};
|
||||
|
||||
explicit controller(handle ep_l) : ep_listen{move(ep_l)} {}
|
||||
|
||||
auto receive(const handle &from, const buffer &m) {
|
||||
port_t port{0};
|
||||
if (m("port", extract(port))) {
|
||||
ep_connect =
|
||||
thespian::endpoint::tcp::connect(in6addr_loopback, port).value();
|
||||
return ok();
|
||||
}
|
||||
if (m("connected")) {
|
||||
ping_count++;
|
||||
return ep_connect.send("ping", ping_count);
|
||||
}
|
||||
if (m("ping", any)) {
|
||||
pong_count++;
|
||||
return from.send("pong", pong_count);
|
||||
}
|
||||
if (m("pong", any)) {
|
||||
if (ping_count > 10) {
|
||||
return exit(ping_count == pong_count ? "success" : "closed");
|
||||
}
|
||||
ping_count++;
|
||||
return from.send("ping", ping_count);
|
||||
}
|
||||
return unexpected(m);
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
} // namespace
|
||||
|
||||
auto endpoint_tcp(context &ctx, bool &result, env_t env_) -> ::result {
|
||||
return to_result(ctx.spawn_link(
|
||||
[=]() {
|
||||
link(env().proc("log"));
|
||||
handle ep_listen = listen(in6addr_loopback, 0).value();
|
||||
auto ret = ep_listen.send("get", "port");
|
||||
if (not ret)
|
||||
return ret;
|
||||
|
||||
receive([p{make_shared<controller>(ep_listen)}](auto from, auto m) {
|
||||
return p->receive(move(from), move(m));
|
||||
});
|
||||
return ok();
|
||||
},
|
||||
[&](auto s) {
|
||||
if (s == "success")
|
||||
result = true;
|
||||
},
|
||||
"endpoint_tcp", move(env_)));
|
||||
}
|
104
test/endpoint_unx.cpp
Normal file
104
test/endpoint_unx.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include <thespian/endpoint.hpp>
|
||||
#include <thespian/instance.hpp>
|
||||
#include <thespian/socket.hpp>
|
||||
#include <thespian/timeout.hpp>
|
||||
#include <thespian/unx.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
using cbor::array;
|
||||
using cbor::buffer;
|
||||
using cbor::extract;
|
||||
using std::make_shared;
|
||||
using std::move;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
using std::stringstream;
|
||||
using thespian::context;
|
||||
using thespian::create_timeout;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::exit;
|
||||
using thespian::handle;
|
||||
using thespian::link;
|
||||
using thespian::ok;
|
||||
using thespian::receive;
|
||||
using thespian::result;
|
||||
using thespian::timeout;
|
||||
using thespian::unexpected;
|
||||
using thespian::endpoint::unx::connect;
|
||||
using thespian::endpoint::unx::listen;
|
||||
using thespian::unx::mode;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
struct controller {
|
||||
handle ep_listen;
|
||||
handle ep_connect;
|
||||
size_t ping_count{};
|
||||
size_t pong_count{};
|
||||
bool success_{false};
|
||||
timeout t{create_timeout(100ms, array("connect"))};
|
||||
|
||||
explicit controller(handle ep_l) : ep_listen{move(ep_l)} {}
|
||||
|
||||
auto receive(const handle &from, const buffer &m) {
|
||||
string_view path;
|
||||
if (m("connect")) {
|
||||
auto ret = ep_listen.send("get", "path");
|
||||
if (not ret)
|
||||
return ret;
|
||||
return ok();
|
||||
}
|
||||
if (m("path", extract(path))) {
|
||||
ep_connect = connect(path).value();
|
||||
return ok();
|
||||
}
|
||||
if (m("connected")) {
|
||||
ping_count++;
|
||||
return ep_connect.send("ping");
|
||||
}
|
||||
if (m("ping")) {
|
||||
pong_count++;
|
||||
return from.send("pong");
|
||||
}
|
||||
if (m("pong")) {
|
||||
if (ping_count > 10) {
|
||||
return exit(ping_count == pong_count ? "success" : "closed");
|
||||
}
|
||||
ping_count++;
|
||||
return ep_connect.send("ping");
|
||||
}
|
||||
return unexpected(m);
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
} // namespace
|
||||
|
||||
auto endpoint_unx(context &ctx, bool &result, env_t env_) -> ::result {
|
||||
stringstream ss;
|
||||
ss << "/net/vdbonline/thespian/endpoint_t_" << getpid();
|
||||
string path = ss.str();
|
||||
return to_result(ctx.spawn_link(
|
||||
[path]() {
|
||||
link(env().proc("log"));
|
||||
handle ep_listen = listen(path).value();
|
||||
receive([p{make_shared<controller>(ep_listen)}](auto from, auto m) {
|
||||
return p->receive(move(from), move(m));
|
||||
});
|
||||
return ok();
|
||||
},
|
||||
[&](auto s) {
|
||||
if (s == "success")
|
||||
result = true;
|
||||
},
|
||||
"endpoint_unx", move(env_)));
|
||||
}
|
127
test/hub_filter.cpp
Normal file
127
test/hub_filter.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "thespian/env.hpp"
|
||||
#include "thespian/handle.hpp"
|
||||
#include <thespian/hub.hpp>
|
||||
#include <thespian/instance.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
using cbor::extract;
|
||||
using cbor::type;
|
||||
using std::map;
|
||||
using std::move;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
using std::stringstream;
|
||||
using thespian::context;
|
||||
using thespian::env_t;
|
||||
using thespian::handle;
|
||||
using thespian::hub;
|
||||
using thespian::ok;
|
||||
using thespian::receive;
|
||||
using thespian::result;
|
||||
using thespian::self;
|
||||
using thespian::spawn_link;
|
||||
|
||||
namespace {
|
||||
|
||||
auto sub_plain(const hub &h, const handle &controller, string name) -> result {
|
||||
auto ret = h.subscribe();
|
||||
if (not ret)
|
||||
return ret;
|
||||
ret = controller.send("ready", name);
|
||||
if (not ret)
|
||||
return ret;
|
||||
receive([=](auto /*from*/, auto m) {
|
||||
string_view cmd;
|
||||
check(m("parmA", "parmB", "parmC", extract(cmd)));
|
||||
check(cmd == "continue" || cmd == "done");
|
||||
if (cmd == "done") {
|
||||
auto ret = controller.send(cmd, name);
|
||||
if (not ret)
|
||||
return ret;
|
||||
}
|
||||
return ok();
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
|
||||
auto sub_filtered(const hub &h, const handle &controller, string name)
|
||||
-> result {
|
||||
auto ret = h.subscribe(
|
||||
[](auto m) { return m(type::any, type::any, type::any, "done"); });
|
||||
if (not ret)
|
||||
return ret;
|
||||
ret = controller.send("ready", name);
|
||||
if (not ret)
|
||||
return ret;
|
||||
receive([=](auto /*from*/, auto m) {
|
||||
check(m("parmA", "parmB", "parmC", "done"));
|
||||
auto ret = controller.send("done", name);
|
||||
if (not ret)
|
||||
return ret;
|
||||
return ok();
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
|
||||
map<string, auto(*)(const hub &, const handle &, string)->result>
|
||||
subscription_defs // NOLINT
|
||||
= {
|
||||
{"sub_plain", sub_plain},
|
||||
{"sub_filtered", sub_filtered},
|
||||
};
|
||||
using submap = map<string, handle>;
|
||||
|
||||
auto controller() -> result {
|
||||
thespian::link(thespian::env().proc("log"));
|
||||
auto h = hub::create("hub").value();
|
||||
handle c = self();
|
||||
auto ret = h.subscribe();
|
||||
if (not ret)
|
||||
return ret;
|
||||
|
||||
submap subs;
|
||||
for (const auto &s : subscription_defs) {
|
||||
subs.insert({s.first, spawn_link(
|
||||
[=]() {
|
||||
s.second(h, c, s.first);
|
||||
return ok();
|
||||
},
|
||||
s.first)
|
||||
.value()});
|
||||
}
|
||||
submap not_ready = subs;
|
||||
submap done = subs;
|
||||
|
||||
receive([=](auto, auto m) mutable {
|
||||
string name;
|
||||
if (m("ready", extract(name))) {
|
||||
not_ready.erase(name);
|
||||
if (not_ready.empty()) {
|
||||
h.broadcast("parmA", "parmB", "parmC", "continue");
|
||||
h.broadcast("parmA", "parmB", "parmC", "continue");
|
||||
h.broadcast("parmA", "parmB", "parmC", "done");
|
||||
}
|
||||
} else if (m("done", extract(name))) {
|
||||
done.erase(name);
|
||||
if (done.empty())
|
||||
return h.shutdown();
|
||||
}
|
||||
return ok();
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto hub_filter(context &ctx, bool &result, env_t env) -> ::result {
|
||||
return to_result(ctx.spawn_link(
|
||||
controller,
|
||||
[&](auto s) {
|
||||
if (s == "shutdown")
|
||||
result = true;
|
||||
},
|
||||
"hub_filter", move(env)));
|
||||
}
|
225
test/ip_tcp_client_server.cpp
Normal file
225
test/ip_tcp_client_server.cpp
Normal file
|
@ -0,0 +1,225 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "thespian/handle.hpp"
|
||||
#include <cbor/cbor_in.hpp>
|
||||
#include <thespian/debug.hpp>
|
||||
#include <thespian/instance.hpp>
|
||||
#include <thespian/socket.hpp>
|
||||
#include <thespian/tcp.hpp>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
using cbor::buffer;
|
||||
using cbor::extract;
|
||||
using std::make_shared;
|
||||
using std::move;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
using std::stringstream;
|
||||
using thespian::context;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::exit;
|
||||
using thespian::handle;
|
||||
using thespian::link;
|
||||
using thespian::ok;
|
||||
using thespian::result;
|
||||
using thespian::self;
|
||||
using thespian::socket;
|
||||
using thespian::spawn_link;
|
||||
using thespian::unexpected;
|
||||
using thespian::tcp::acceptor;
|
||||
using thespian::tcp::connector;
|
||||
|
||||
namespace {
|
||||
|
||||
struct client_connection {
|
||||
thespian::socket socket;
|
||||
handle connector;
|
||||
|
||||
explicit client_connection(int fd, handle connector)
|
||||
: socket{socket::create("client_connection", fd)},
|
||||
connector(move(connector)) {
|
||||
socket.read();
|
||||
}
|
||||
|
||||
auto receive(const buffer &m) {
|
||||
int written{};
|
||||
int err{};
|
||||
string_view buf{};
|
||||
|
||||
if (m("socket", "client_connection", "read_complete", extract(buf))) {
|
||||
check(buf == "ping");
|
||||
check(err == 0);
|
||||
socket.write("pong");
|
||||
} else if (m("socket", "client_connection", "write_complete",
|
||||
extract(written))) {
|
||||
check(written == 4);
|
||||
check(err == 0);
|
||||
// socket.close(); // let server close
|
||||
} else if (m("socket", "client_connection", "closed")) {
|
||||
auto ret = connector.send("client_connection", "done");
|
||||
if (not ret)
|
||||
return ret;
|
||||
return exit("success");
|
||||
} else {
|
||||
return unexpected(m);
|
||||
}
|
||||
return ok();
|
||||
}
|
||||
|
||||
[[nodiscard]] static auto init(int fd, const handle &connector) {
|
||||
return spawn_link(
|
||||
[fd, connector]() {
|
||||
::thespian::receive(
|
||||
[p{make_shared<client_connection>(fd, connector)}](
|
||||
auto /*from*/, auto m) { return p->receive(move(m)); });
|
||||
return ok();
|
||||
},
|
||||
"client_connection");
|
||||
}
|
||||
};
|
||||
|
||||
struct client {
|
||||
connector connector;
|
||||
handle server;
|
||||
port_t server_port;
|
||||
|
||||
explicit client(handle server, port_t server_port)
|
||||
: connector{connector::create("client")}, server(move(server)),
|
||||
server_port(server_port) {
|
||||
connector.connect(in6addr_loopback, server_port);
|
||||
}
|
||||
|
||||
auto receive(const buffer &m) -> thespian::result {
|
||||
int fd{};
|
||||
if (m("connector", "client", "connected", extract(fd))) {
|
||||
return to_result(client_connection::init(fd, self()));
|
||||
}
|
||||
if (m("connector_connection", "done")) {
|
||||
return server.send("client", "done");
|
||||
}
|
||||
return unexpected(m);
|
||||
}
|
||||
|
||||
static auto init(handle server, port_t server_port) -> result {
|
||||
::thespian::receive(
|
||||
[p{make_shared<client>(server, server_port)}](auto /*from*/, auto m) {
|
||||
return p->receive(move(m));
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
};
|
||||
|
||||
struct server_connection {
|
||||
thespian::socket socket;
|
||||
handle server;
|
||||
|
||||
explicit server_connection(int fd, handle server)
|
||||
: socket{socket::create("server_connection", fd)}, server(move(server)) {
|
||||
socket.write("ping");
|
||||
}
|
||||
|
||||
auto receive(const buffer &m) {
|
||||
int written{};
|
||||
int err{};
|
||||
string_view buf{};
|
||||
|
||||
if (m("socket", "server_connection", "write_complete", extract(written))) {
|
||||
check(written == 4);
|
||||
check(err == 0);
|
||||
socket.read();
|
||||
} else if (m("socket", "server_connection", "read_complete",
|
||||
extract(buf))) {
|
||||
check(buf == "pong");
|
||||
check(err == 0);
|
||||
socket.close();
|
||||
} else if (m("socket", "server_connection", "closed")) {
|
||||
auto ret = server.send("server_connection", "done");
|
||||
if (not ret)
|
||||
return ret;
|
||||
return exit("success");
|
||||
} else {
|
||||
return unexpected(m);
|
||||
}
|
||||
return ok();
|
||||
}
|
||||
|
||||
[[nodiscard]] static auto init(int fd, const handle &server) {
|
||||
return spawn_link(
|
||||
[fd, server]() {
|
||||
::thespian::receive(
|
||||
[p{make_shared<server_connection>(fd, server)}](
|
||||
auto /*from*/, auto m) { return p->receive(move(m)); });
|
||||
return ok();
|
||||
},
|
||||
"server_connection");
|
||||
}
|
||||
};
|
||||
|
||||
struct server {
|
||||
acceptor acceptor;
|
||||
bool client_done{false};
|
||||
bool server_connection_done{false};
|
||||
bool acceptor_closed{false};
|
||||
port_t server_port;
|
||||
|
||||
explicit server()
|
||||
: acceptor{acceptor::create("server")},
|
||||
server_port(acceptor.listen(in6addr_loopback, 0)) {}
|
||||
|
||||
auto receive(const buffer &m) {
|
||||
int fd{};
|
||||
if (m("acceptor", "server", "accept", extract(fd))) {
|
||||
auto ret = server_connection::init(fd, self());
|
||||
if (not ret)
|
||||
return to_result(ret);
|
||||
acceptor.close();
|
||||
} else if (m("acceptor", "server", "closed")) {
|
||||
acceptor_closed = true;
|
||||
} else if (m("client", "done")) {
|
||||
client_done = true;
|
||||
} else if (m("server_connection", "done")) {
|
||||
server_connection_done = true;
|
||||
} else {
|
||||
return unexpected(m);
|
||||
}
|
||||
if (acceptor_closed and client_done and server_connection_done)
|
||||
return exit("success");
|
||||
return ok();
|
||||
}
|
||||
|
||||
[[nodiscard]] static auto init() {
|
||||
link(env().proc("log"));
|
||||
auto log = env().proc("log");
|
||||
auto _ = log.send("server starting");
|
||||
auto p{make_shared<server>()};
|
||||
_ = log.send("server listening on port", p->server_port);
|
||||
auto ret = spawn_link(
|
||||
[server{self()}, server_port{p->server_port}]() {
|
||||
return client::init(server, server_port);
|
||||
},
|
||||
"ip_tcp_client");
|
||||
if (not ret)
|
||||
return to_result(ret);
|
||||
thespian::receive(
|
||||
[p](auto /*from*/, auto m) { return p->receive(move(m)); });
|
||||
return ok();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
auto ip_tcp_client_server(context &ctx, bool &result, env_t env) -> ::result {
|
||||
thespian::debug::enable(ctx);
|
||||
return to_result(ctx.spawn_link(
|
||||
server::init,
|
||||
[&](auto s) {
|
||||
if (s == "success") {
|
||||
result = true;
|
||||
} else {
|
||||
auto _ = env.proc("log").send("failed:", s);
|
||||
}
|
||||
},
|
||||
"ip_tcp_client_server", move(env)));
|
||||
}
|
122
test/ip_udp_echo.cpp
Normal file
122
test/ip_udp_echo.cpp
Normal file
|
@ -0,0 +1,122 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "thespian/env.hpp"
|
||||
#include "thespian/handle.hpp"
|
||||
#include <thespian/instance.hpp>
|
||||
#include <thespian/timeout.hpp>
|
||||
#include <thespian/udp.hpp>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
using cbor::array;
|
||||
using cbor::buffer;
|
||||
using cbor::extract;
|
||||
using cbor::more;
|
||||
using std::make_shared;
|
||||
using std::move;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
using std::stringstream;
|
||||
using thespian::context;
|
||||
using thespian::create_timeout;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::exit;
|
||||
using thespian::link;
|
||||
using thespian::ok;
|
||||
using thespian::receive;
|
||||
using thespian::result;
|
||||
using thespian::udp;
|
||||
using thespian::unexpected;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
struct controller {
|
||||
port_t server_port{0};
|
||||
port_t client_port{0};
|
||||
udp client;
|
||||
udp server;
|
||||
size_t ping_count{};
|
||||
size_t pong_count{};
|
||||
bool success_{false};
|
||||
thespian::timeout server_receive_timeout{
|
||||
create_timeout(500s, array("udp", "server", "timeout"))};
|
||||
|
||||
explicit controller()
|
||||
: client{udp::create("client")}, server{udp::create("server")} {
|
||||
server_port = server.open(in6addr_loopback, server_port);
|
||||
client.open(in6addr_loopback, client_port);
|
||||
auto sent = client.sendto("ping", in6addr_loopback, server_port);
|
||||
if (sent != 4)
|
||||
abort();
|
||||
}
|
||||
controller(controller &&) = delete;
|
||||
controller(const controller &) = delete;
|
||||
|
||||
~controller() { server_receive_timeout.cancel(); }
|
||||
|
||||
auto operator=(controller &&) -> controller & = delete;
|
||||
auto operator=(const controller &) -> controller & = delete;
|
||||
|
||||
auto client_receive(const buffer &m) {
|
||||
in6_addr remote_ip{};
|
||||
port_t remote_port{};
|
||||
if (m("udp", "client", "receive", "pong", extract(remote_ip),
|
||||
extract(remote_port))) {
|
||||
check(remote_port == server_port);
|
||||
client.close();
|
||||
} else if (m("udp", "client", "closed")) {
|
||||
return exit("success");
|
||||
} else {
|
||||
return unexpected(m);
|
||||
}
|
||||
return ok();
|
||||
}
|
||||
|
||||
auto server_receive(const buffer &m) {
|
||||
in6_addr remote_ip{};
|
||||
port_t remote_port{};
|
||||
if (m("udp", "server", "receive", "ping", extract(remote_ip),
|
||||
extract(remote_port))) {
|
||||
auto sent = server.sendto("pong", remote_ip, remote_port);
|
||||
if (sent != 4)
|
||||
return exit("unexpected_sendto_size", sent);
|
||||
server.close();
|
||||
} else if (m("udp", "server", "timeout")) {
|
||||
return exit("timeout");
|
||||
} else if (m("udp", "server", "closed")) {
|
||||
;
|
||||
} else {
|
||||
return unexpected(m);
|
||||
}
|
||||
return ok();
|
||||
}
|
||||
|
||||
auto receive(const buffer &m) {
|
||||
if (m("udp", "client", more))
|
||||
return client_receive(m);
|
||||
if (m("udp", "server", more))
|
||||
return server_receive(m);
|
||||
return unexpected(m);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
auto ip_udp_echo(context &ctx, bool &result, env_t env_) -> ::result {
|
||||
return to_result(ctx.spawn_link(
|
||||
[=]() {
|
||||
link(env().proc("log"));
|
||||
receive([p{make_shared<controller>()}](auto /*from*/, auto m) {
|
||||
return p->receive(move(m));
|
||||
});
|
||||
return ok();
|
||||
},
|
||||
[&](auto s) {
|
||||
if (s == "success")
|
||||
result = true;
|
||||
},
|
||||
"ip_udp_echo", move(env_)));
|
||||
}
|
81
test/metronome_test.cpp
Normal file
81
test/metronome_test.cpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "thespian/env.hpp"
|
||||
#include <thespian/instance.hpp>
|
||||
#include <thespian/metronome.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
using cbor::buffer;
|
||||
using std::move;
|
||||
using std::shared_ptr;
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono::system_clock;
|
||||
using thespian::context;
|
||||
using thespian::create_metronome;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::exit;
|
||||
using thespian::exit_handler;
|
||||
using thespian::handle;
|
||||
using thespian::link;
|
||||
using thespian::metronome;
|
||||
using thespian::ok;
|
||||
using thespian::receive;
|
||||
using thespian::result;
|
||||
using thespian::unexpected;
|
||||
|
||||
using clk = std::chrono::system_clock;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
auto initA() {
|
||||
link(env().proc("log"));
|
||||
struct state_t {
|
||||
int i{0};
|
||||
system_clock::time_point start{clk::now()};
|
||||
metronome met{create_metronome(10ms)};
|
||||
state_t() { met.start(); }
|
||||
handle log{env().proc("log")};
|
||||
|
||||
auto receive(const buffer &m) {
|
||||
if (m("tick", i))
|
||||
++i;
|
||||
else
|
||||
return unexpected(m);
|
||||
{
|
||||
auto end = clk::now();
|
||||
auto us = duration_cast<microseconds>(end - start).count();
|
||||
auto ret = log.send("tick@", us, "µs");
|
||||
if (not ret)
|
||||
return ret;
|
||||
}
|
||||
if (i == 5) {
|
||||
auto end = clk::now();
|
||||
auto us = duration_cast<microseconds>(end - start);
|
||||
met.stop();
|
||||
return exit(us > 50000us ? "done" : "failed");
|
||||
}
|
||||
return ok();
|
||||
}
|
||||
};
|
||||
shared_ptr<state_t> state{new state_t};
|
||||
receive([state](auto, auto m) mutable { return state->receive(m); });
|
||||
return ok();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto metronome_test(context &ctx, bool &result, env_t env) -> ::result {
|
||||
return to_result(ctx.spawn_link(
|
||||
initA,
|
||||
[&](auto s) {
|
||||
if (s == "done")
|
||||
result = true;
|
||||
},
|
||||
"metronome", move(env)));
|
||||
}
|
140
test/perf_cbor.cpp
Normal file
140
test/perf_cbor.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "thespian/env.hpp"
|
||||
#include "thespian/handle.hpp"
|
||||
#include <thespian/instance.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
using cbor::A;
|
||||
using cbor::array;
|
||||
using cbor::buffer;
|
||||
using cbor::extract;
|
||||
using cbor::type;
|
||||
using std::move;
|
||||
using std::string_view;
|
||||
using std::to_string;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::microseconds;
|
||||
using thespian::context;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::link;
|
||||
using thespian::ok;
|
||||
using thespian::result;
|
||||
|
||||
using clk = std::chrono::system_clock;
|
||||
|
||||
namespace {
|
||||
|
||||
auto count{10000UL}; // NOLINT
|
||||
|
||||
auto time(const char *name, void (*f)()) -> void {
|
||||
auto start = clk::now();
|
||||
|
||||
for (uint64_t i{0}; i < count; ++i)
|
||||
f();
|
||||
|
||||
auto end = clk::now();
|
||||
auto us = duration_cast<microseconds>(end - start).count();
|
||||
|
||||
auto _ = env().proc("log").send(to_string((count * 1.0L) / us), name, "/µs");
|
||||
}
|
||||
|
||||
const auto ma = array("five", 5, "four", 4, array("three", 3));
|
||||
|
||||
auto test() -> result {
|
||||
link(env().proc("log"));
|
||||
auto _ = env().proc("log").send("buffer size:", ma.size());
|
||||
|
||||
time("noop", []() {});
|
||||
|
||||
time("encode_template", []() {
|
||||
buffer m{array("five", 5, "four", 4, array("three", 3))};
|
||||
check(m.size() == 21);
|
||||
});
|
||||
|
||||
time("encode_chain", []() {
|
||||
buffer b;
|
||||
b.array_header(5)
|
||||
.push("five")
|
||||
.push(5)
|
||||
.push("four")
|
||||
.push(4)
|
||||
.array_header(2)
|
||||
.push("three")
|
||||
.push(3);
|
||||
check(b.size() == 21);
|
||||
});
|
||||
|
||||
time("encode_chain_reserve", []() {
|
||||
buffer b;
|
||||
b.reserve(21);
|
||||
b.array_header(5)
|
||||
.push("five")
|
||||
.push(5)
|
||||
.push("four")
|
||||
.push(4)
|
||||
.array_header(2)
|
||||
.push("three")
|
||||
.push(3);
|
||||
check(b.size() == 21);
|
||||
});
|
||||
|
||||
time("match", []() {
|
||||
check(ma(type::string, type::number, type::string, type::number,
|
||||
type::array));
|
||||
});
|
||||
|
||||
time("match2", []() {
|
||||
check(ma(type::string, type::number, type::string, type::number,
|
||||
A(type::string, type::number)));
|
||||
});
|
||||
|
||||
time("extract_int", []() {
|
||||
int64_t i{};
|
||||
check(ma(type::string, extract(i), type::more));
|
||||
check(i == 5);
|
||||
});
|
||||
|
||||
time("extract_string", []() {
|
||||
string_view s;
|
||||
check(ma(extract(s), type::more));
|
||||
});
|
||||
|
||||
time("extract_int2", []() {
|
||||
int64_t i{};
|
||||
check(ma(type::string, type::number, type::string, type::number,
|
||||
A(type::string, extract(i))));
|
||||
check(i == 3);
|
||||
});
|
||||
|
||||
time("extract_string2", []() {
|
||||
string_view s;
|
||||
check(ma(type::string, type::number, type::string, type::number,
|
||||
A(extract(s), type::number)));
|
||||
});
|
||||
|
||||
time("extract_array", []() {
|
||||
buffer::range r;
|
||||
check(
|
||||
ma(type::string, type::number, type::string, type::number, extract(r)));
|
||||
check(r(type::string, type::number));
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto perf_cbor(context &ctx, bool &result, env_t env) -> ::result {
|
||||
if (env.is("long_run"))
|
||||
count = 10000000;
|
||||
return to_result(ctx.spawn_link(
|
||||
test,
|
||||
[&](auto s) {
|
||||
check(s == "noreceive");
|
||||
result = true;
|
||||
},
|
||||
"perf_cbor", move(env)));
|
||||
}
|
153
test/perf_hub.cpp
Normal file
153
test/perf_hub.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "thespian/env.hpp"
|
||||
#include "thespian/handle.hpp"
|
||||
#include <thespian/hub.hpp>
|
||||
#include <thespian/instance.hpp>
|
||||
#include <thespian/trace.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
using cbor::extract;
|
||||
using std::move;
|
||||
using std::set;
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
using std::underlying_type_t;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::milliseconds;
|
||||
using thespian::context;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::handle;
|
||||
using thespian::hub;
|
||||
using thespian::link;
|
||||
using thespian::ok;
|
||||
using thespian::receive;
|
||||
using thespian::result;
|
||||
using thespian::self;
|
||||
using thespian::spawn_link;
|
||||
using thespian::unexpected;
|
||||
|
||||
using clk = std::chrono::system_clock;
|
||||
|
||||
namespace {
|
||||
|
||||
enum class tag {
|
||||
ready = 1,
|
||||
done = 2,
|
||||
};
|
||||
|
||||
auto tag_to_int(tag e) noexcept -> int {
|
||||
return static_cast<underlying_type_t<tag>>(e);
|
||||
}
|
||||
auto tag_from_int(int i) -> tag {
|
||||
return static_cast<tag>(static_cast<int>(i));
|
||||
}
|
||||
|
||||
auto subscriber(const hub &h, const handle &controller, const int num,
|
||||
const int messages) -> result {
|
||||
auto ret = h.subscribe();
|
||||
if (not ret)
|
||||
return ret;
|
||||
ret = controller.send(tag_to_int(tag::ready), num);
|
||||
if (not ret)
|
||||
return ret;
|
||||
int count{1};
|
||||
receive([=](auto /*from*/, auto m) mutable {
|
||||
if (!m("parmA", "parmB", "parmC", count))
|
||||
std::abort();
|
||||
if (count == messages) {
|
||||
auto ret = controller.send(tag_to_int(tag::done), num);
|
||||
if (not ret)
|
||||
return ret;
|
||||
}
|
||||
++count;
|
||||
return ok();
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
|
||||
auto controller(const int subscriptions, const int messages) -> result {
|
||||
const auto log = env().proc("log");
|
||||
const auto verbose = env().is("verbose");
|
||||
auto ret = hub::create("hub");
|
||||
if (not ret)
|
||||
return to_result(ret);
|
||||
auto h = ret.value();
|
||||
handle c = self();
|
||||
|
||||
auto _ = log.send("subscribing", subscriptions);
|
||||
|
||||
set<int> subs;
|
||||
for (auto i{0}; i < subscriptions; ++i) {
|
||||
auto ret =
|
||||
spawn_link([=]() -> result { return subscriber(h, c, i, messages); },
|
||||
"subscriber");
|
||||
if (not ret)
|
||||
return to_result(ret);
|
||||
subs.insert(i);
|
||||
}
|
||||
_ = log.send("all", "spawned");
|
||||
set<int> not_ready = subs;
|
||||
set<int> done = subs;
|
||||
clk::time_point start;
|
||||
|
||||
receive([=](auto, auto m) mutable {
|
||||
int tag_{}, num{};
|
||||
if (!m(extract(tag_), extract(num)))
|
||||
return unexpected(m);
|
||||
tag tag = tag_from_int(tag_);
|
||||
switch (tag) {
|
||||
case tag::ready:
|
||||
not_ready.erase(num);
|
||||
if (not_ready.empty()) {
|
||||
if (verbose)
|
||||
_ = log.send("all", "ready");
|
||||
for (int i = 1; i <= messages; ++i)
|
||||
h.broadcast("parmA", "parmB", "parmC", i);
|
||||
start = clk::now();
|
||||
if (verbose)
|
||||
_ = log.send("broadcasting", messages);
|
||||
}
|
||||
break;
|
||||
|
||||
case tag::done:
|
||||
done.erase(num);
|
||||
if (done.empty()) {
|
||||
if (verbose) {
|
||||
clk::time_point end = clk::now();
|
||||
auto ms = duration_cast<milliseconds>(end - start).count();
|
||||
double msgs = subscriptions * messages + subscriptions;
|
||||
_ = log.send("all", "done");
|
||||
_ = log.send("time:", ms, "ms");
|
||||
_ = log.send(
|
||||
static_cast<int>((msgs / static_cast<double>(ms)) * 1000),
|
||||
"msg/s");
|
||||
}
|
||||
return h.shutdown();
|
||||
}
|
||||
}
|
||||
return ok();
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
auto perf_hub(context &ctx, bool &result, env_t env_) -> ::result {
|
||||
const auto long_run = env_.is("long_run");
|
||||
const auto subscriptions = long_run ? 1000 : 100;
|
||||
const auto messages = long_run ? 10000 : 100;
|
||||
return to_result(ctx.spawn_link(
|
||||
[=]() {
|
||||
link(env().proc("log"));
|
||||
return controller(subscriptions, messages);
|
||||
},
|
||||
[&](auto s) {
|
||||
if (s == "shutdown")
|
||||
result = true;
|
||||
},
|
||||
"controller", move(env_)));
|
||||
}
|
79
test/perf_ring.cpp
Normal file
79
test/perf_ring.cpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "thespian/env.hpp"
|
||||
#include <thespian/instance.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
using cbor::extract;
|
||||
using std::move;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
using thespian::context;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::exit;
|
||||
using thespian::handle;
|
||||
using thespian::link;
|
||||
using thespian::ok;
|
||||
using thespian::receive;
|
||||
using thespian::result;
|
||||
using thespian::self;
|
||||
using thespian::spawn_link;
|
||||
|
||||
namespace {
|
||||
|
||||
auto slave(const handle &last, int n) -> result {
|
||||
handle next;
|
||||
if (n)
|
||||
next = spawn_link([=]() { return slave(last, n - 1); }, "slave").value();
|
||||
|
||||
receive([n, next, last](auto, auto m) {
|
||||
return n ? next.send_raw(move(m)) : last.send_raw(move(m));
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
|
||||
auto controller(const int slaves) -> result {
|
||||
auto verbose = env().is("long_run");
|
||||
int loop = 10;
|
||||
handle last = self();
|
||||
auto ret = spawn_link([=]() { return slave(last, slaves); }, "slave");
|
||||
if (not ret)
|
||||
return to_result(ret);
|
||||
handle first = ret.value();
|
||||
auto ret2 = first.send("forward");
|
||||
if (not ret2)
|
||||
return ret2;
|
||||
receive([loop, first, verbose](auto, auto m) mutable {
|
||||
if (loop) {
|
||||
if (verbose)
|
||||
auto _ = env().proc("log").send(loop);
|
||||
--loop;
|
||||
return first.send_raw(move(m));
|
||||
}
|
||||
string_view s;
|
||||
check(m(extract(s)));
|
||||
return exit(s);
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
auto perf_ring(context &ctx, bool &result, env_t env_) -> ::result {
|
||||
auto long_run = env_.is("long_run");
|
||||
auto slaves{long_run ? 100000 : 1000};
|
||||
return to_result(ctx.spawn_link(
|
||||
[=]() {
|
||||
link(env().proc("log"));
|
||||
return controller(slaves);
|
||||
},
|
||||
[&](auto s) {
|
||||
if (s == "forward") {
|
||||
result = true;
|
||||
} else {
|
||||
auto _ = env_.proc("log").send("failed:", s);
|
||||
}
|
||||
},
|
||||
"perf_ring", move(env_)));
|
||||
}
|
73
test/perf_spawn.cpp
Normal file
73
test/perf_spawn.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "thespian/env.hpp"
|
||||
#include <thespian/instance.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
using cbor::extract;
|
||||
using std::move;
|
||||
using thespian::context;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::exit;
|
||||
using thespian::link;
|
||||
using thespian::ok;
|
||||
using thespian::receive;
|
||||
using thespian::result;
|
||||
using thespian::spawn_link;
|
||||
using thespian::unexpected;
|
||||
|
||||
namespace {
|
||||
|
||||
auto slave(const int slaves, int spawned) -> result {
|
||||
receive([slaves, spawned](auto, auto m) mutable {
|
||||
int n{0};
|
||||
if (!m(extract(n)))
|
||||
return unexpected(m);
|
||||
if (n) {
|
||||
++spawned;
|
||||
return thespian::to_result(
|
||||
spawn_link([=]() { return slave(slaves, spawned); }, "slave")
|
||||
.value()
|
||||
.send(n - 1));
|
||||
}
|
||||
if (spawned != slaves) {
|
||||
auto _ = env().proc("log").send("spawned", spawned, "slaves !=", slaves);
|
||||
return exit("failed");
|
||||
}
|
||||
if (env().is("verbose"))
|
||||
auto _ = env().proc("log").send("spawned", slaves, "slaves");
|
||||
return exit("done");
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
|
||||
auto controller(const int slaves) -> result {
|
||||
auto ret = spawn_link([=]() { return slave(slaves, 1); }, "slave");
|
||||
if (not ret)
|
||||
return to_result(ret);
|
||||
auto ret2 = ret.value().send(slaves - 1);
|
||||
if (not ret2)
|
||||
return ret2;
|
||||
receive([](auto, auto) { return exit(); });
|
||||
return ok();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
auto perf_spawn(context &ctx, bool &result, env_t env_) -> ::result {
|
||||
auto slaves{env_.is("long_run") ? 1000000 : 1000};
|
||||
return to_result((ctx.spawn_link(
|
||||
[=]() {
|
||||
link(env().proc("log"));
|
||||
return controller(slaves);
|
||||
},
|
||||
[&](auto s) {
|
||||
if (s == "done") {
|
||||
result = true;
|
||||
} else {
|
||||
auto _ = env_.proc("log").send(s);
|
||||
}
|
||||
},
|
||||
"perf_spawn", move(env_))));
|
||||
}
|
120
test/spawn_exit.cpp
Normal file
120
test/spawn_exit.cpp
Normal file
|
@ -0,0 +1,120 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "thespian/env.hpp"
|
||||
#include "thespian/handle.hpp"
|
||||
|
||||
#include <thespian/instance.hpp>
|
||||
|
||||
using std::move;
|
||||
using thespian::behaviour;
|
||||
using thespian::context;
|
||||
using thespian::env;
|
||||
using thespian::env_t;
|
||||
using thespian::exit;
|
||||
using thespian::exit_handler;
|
||||
using thespian::ok;
|
||||
using thespian::receive;
|
||||
using thespian::result;
|
||||
using thespian::self;
|
||||
using thespian::spawn;
|
||||
using thespian::spawn_link;
|
||||
using thespian::to_result;
|
||||
using thespian::trap;
|
||||
using thespian::unexpected;
|
||||
|
||||
namespace {
|
||||
|
||||
auto initE() {
|
||||
check(env().str("initAsays") == "yes");
|
||||
check(env().str("initBsays") == "");
|
||||
check(env().str("initCsays") == "no");
|
||||
check(env().num("intval") == 42);
|
||||
check(!env().proc("A").expired());
|
||||
link(env().proc("A"));
|
||||
auto ret = env().proc("A").send("shutdown");
|
||||
if (not ret)
|
||||
return ret;
|
||||
receive([](auto, auto) { return ok(); });
|
||||
return ok();
|
||||
}
|
||||
|
||||
auto initD() {
|
||||
auto ret = spawn(initE, "E");
|
||||
if (not ret)
|
||||
return to_result(ret);
|
||||
receive([](auto, auto m) {
|
||||
if (m("die"))
|
||||
return exit("died");
|
||||
return unexpected(m);
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
|
||||
auto initC() {
|
||||
env().str("initCsays") = "no";
|
||||
auto ret = spawn_link(initD, "D").value().send("die");
|
||||
if (not ret)
|
||||
return to_result(ret);
|
||||
trap(true);
|
||||
receive([](auto /*from*/, auto m) {
|
||||
if (m("exit", "died"))
|
||||
return exit();
|
||||
return unexpected(m);
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
|
||||
auto initB() {
|
||||
env().str("initBsays") = "noyoudont";
|
||||
receive([](auto from, auto m) {
|
||||
if (m("shutdown")) {
|
||||
auto ret = from.send("done");
|
||||
if (not ret)
|
||||
return ret;
|
||||
return exit();
|
||||
}
|
||||
return unexpected(m);
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
|
||||
auto initA() {
|
||||
link(env().proc("log"));
|
||||
env().str("initAsays") = "yes";
|
||||
env().num("intval") = 42;
|
||||
env().proc("A") = self();
|
||||
auto ret = spawn_link(initB, "B").value().send("shutdown");
|
||||
if (not ret)
|
||||
return ret;
|
||||
spawn_link(initC, "C").value();
|
||||
receive([i{0}](auto, auto m) mutable {
|
||||
if (m("shutdown") || m("done"))
|
||||
++i;
|
||||
else
|
||||
return unexpected(m);
|
||||
if (i == 2)
|
||||
return exit("done");
|
||||
return ok();
|
||||
});
|
||||
return ok();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto spawn_exit(context &ctx, bool &result, env_t env) -> ::result {
|
||||
|
||||
behaviour b;
|
||||
check(!b);
|
||||
b = []() { return ok(); };
|
||||
check(!!b);
|
||||
b = behaviour{};
|
||||
check(!b);
|
||||
|
||||
return to_result(ctx.spawn_link(
|
||||
initA,
|
||||
[&](auto s) {
|
||||
if (s == "done")
|
||||
result = true;
|
||||
},
|
||||
"spawn_exit", move(env)));
|
||||
}
|
161
test/tests.cpp
Normal file
161
test/tests.cpp
Normal file
|
@ -0,0 +1,161 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <thespian/backtrace.h>
|
||||
#include <thespian/context.hpp>
|
||||
#include <thespian/debug.hpp>
|
||||
#include <thespian/env.hpp>
|
||||
#include <thespian/instance.hpp>
|
||||
#include <thespian/timeout.hpp>
|
||||
#include <thespian/trace.hpp>
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using cbor::buffer;
|
||||
using std::cerr;
|
||||
using std::cout;
|
||||
using std::lock_guard;
|
||||
using std::map;
|
||||
using std::move;
|
||||
using std::mutex;
|
||||
using std::ostream;
|
||||
using std::string;
|
||||
using thespian::env_t;
|
||||
using thespian::handle;
|
||||
using thespian::ok;
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
auto operator<<(ostream &s, const cbor::buffer::value_accessor::unvisitable_type
|
||||
& /*unused*/) -> ostream & {
|
||||
return s << "unvisitable_type";
|
||||
}
|
||||
|
||||
struct logger {
|
||||
thespian::timeout t{thespian::create_timeout(60s, cbor::array("timeout"))};
|
||||
const char *name;
|
||||
mutex &trace_m;
|
||||
bool verbose;
|
||||
|
||||
explicit logger(const char *name, mutex &trace_m, bool verbose)
|
||||
: name(name), trace_m(trace_m), verbose(verbose) {}
|
||||
|
||||
auto receive(const buffer &m) {
|
||||
if (verbose) {
|
||||
std::lock_guard<mutex> lock(trace_m);
|
||||
cout << name << ": ";
|
||||
auto dump = [&](auto val) { cout << val << " "; };
|
||||
for (const auto val : m)
|
||||
val.visit(dump);
|
||||
cout << '\n';
|
||||
}
|
||||
return ok();
|
||||
}
|
||||
|
||||
static auto start(thespian::context &ctx, const char *name, mutex &trace_m,
|
||||
bool verbose, env_t env) -> handle {
|
||||
return ctx
|
||||
.spawn(
|
||||
[name, &trace_m, verbose]() {
|
||||
thespian::receive(
|
||||
[p{make_shared<logger>(name, trace_m, verbose)}](
|
||||
auto, auto m) { return p->receive(move(m)); });
|
||||
return ok();
|
||||
},
|
||||
string("logger_") + name, move(env))
|
||||
.value();
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" auto runtestcase(const char *name) -> int {
|
||||
mutex trace_m;
|
||||
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) // NOLINT
|
||||
abort();
|
||||
|
||||
auto gdb = getenv("JITDEBUG"); // NOLINT
|
||||
|
||||
if (gdb) {
|
||||
if (strcmp(gdb, "on") != 0)
|
||||
install_debugger();
|
||||
else if (strcmp(gdb, "backtrace") != 0)
|
||||
install_backtrace();
|
||||
}
|
||||
|
||||
auto ctx = thespian::context::create();
|
||||
|
||||
map<string, testcase *> tests;
|
||||
tests["cbor_match"] = cbor_match;
|
||||
tests["debug"] = debug;
|
||||
tests["endpoint_unx"] = endpoint_unx;
|
||||
tests["endpoint_tcp"] = endpoint_tcp;
|
||||
tests["hub_filter"] = hub_filter;
|
||||
tests["ip_tcp_client_server"] = ip_tcp_client_server;
|
||||
tests["ip_udp_echo"] = ip_udp_echo;
|
||||
tests["metronome_test"] = metronome_test;
|
||||
tests["perf_cbor"] = perf_cbor;
|
||||
tests["perf_hub"] = perf_hub;
|
||||
tests["perf_ring"] = perf_ring;
|
||||
tests["perf_spawn"] = perf_spawn;
|
||||
tests["spawn_exit"] = spawn_exit;
|
||||
tests["timeout_test"] = timeout_test;
|
||||
|
||||
env_t env{};
|
||||
env_t log_env{};
|
||||
auto trace = [&](const buffer &buf) {
|
||||
lock_guard<mutex> lock(trace_m);
|
||||
cout << buf.to_json() << '\n';
|
||||
};
|
||||
log_env.on_trace(trace);
|
||||
env.on_trace(trace);
|
||||
if (getenv("TRACE")) { // NOLINT
|
||||
thespian::debug::enable(*ctx);
|
||||
env.enable_all_channels();
|
||||
log_env.enable_all_channels();
|
||||
}
|
||||
if (getenv("TRACE_LIFETIME")) { // NOLINT
|
||||
thespian::debug::enable(*ctx);
|
||||
env.enable(thespian::channel::lifetime);
|
||||
log_env.enable(thespian::channel::lifetime);
|
||||
}
|
||||
if (getenv("TRACE_EXECUTE")) { // NOLINT
|
||||
env.enable(thespian::channel::execute);
|
||||
}
|
||||
if (getenv("TRACE_SEND")) { // NOLINT
|
||||
env.enable(thespian::channel::send);
|
||||
}
|
||||
if (getenv("TRACE_RECEIVE")) { // NOLINT
|
||||
env.enable(thespian::channel::receive);
|
||||
}
|
||||
|
||||
bool long_run = getenv("LONGRUN"); // NOLINT
|
||||
bool verbose = getenv("VERBOSE"); // NOLINT
|
||||
env.is("long_run") = long_run;
|
||||
env.is("verbose") = verbose;
|
||||
|
||||
if (verbose)
|
||||
cout << '\n';
|
||||
|
||||
env.proc("log") = logger::start(*ctx, name, trace_m, verbose, move(log_env));
|
||||
|
||||
auto test = tests.find(name);
|
||||
if (test == tests.end()) {
|
||||
cerr << "invalid testcase: " << name << '\n';
|
||||
return 1;
|
||||
}
|
||||
bool result{false};
|
||||
test->second(*ctx, result, move(env));
|
||||
ctx->run();
|
||||
return result ? 0 : 1;
|
||||
}
|
2
test/tests.h
Normal file
2
test/tests.h
Normal file
|
@ -0,0 +1,2 @@
|
|||
int runtestcase(const char *name); // NOLINT
|
||||
|
30
test/tests.hpp
Normal file
30
test/tests.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <thespian/context.hpp>
|
||||
#include <thespian/env.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string_view>
|
||||
|
||||
constexpr auto check(bool expression) -> void {
|
||||
if (!expression)
|
||||
std::abort();
|
||||
}
|
||||
|
||||
using testcase = auto(thespian::context &ctx, bool &result, thespian::env_t env)
|
||||
-> thespian::result;
|
||||
|
||||
testcase cbor_match;
|
||||
testcase debug;
|
||||
testcase endpoint_tcp;
|
||||
testcase endpoint_unx;
|
||||
testcase hub_filter;
|
||||
testcase ip_tcp_client_server;
|
||||
testcase ip_udp_echo;
|
||||
testcase metronome_test;
|
||||
testcase perf_cbor;
|
||||
testcase perf_hub;
|
||||
testcase perf_ring;
|
||||
testcase perf_spawn;
|
||||
testcase spawn_exit;
|
||||
testcase timeout_test;
|
8
test/tests.zig
Normal file
8
test/tests.zig
Normal file
|
@ -0,0 +1,8 @@
|
|||
const std = @import("std");
|
||||
pub const cpp = @import("tests_cpp.zig");
|
||||
pub const cbor = @import("tests_cbor.zig");
|
||||
pub const thespian = @import("tests_thespian.zig");
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
}
|
435
test/tests_cbor.zig
Normal file
435
test/tests_cbor.zig
Normal file
|
@ -0,0 +1,435 @@
|
|||
const std = @import("std");
|
||||
const cbor_mod = @import("cbor");
|
||||
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const expectEqualDeep = std.testing.expectEqualDeep;
|
||||
const expectError = std.testing.expectError;
|
||||
|
||||
const fmt = cbor_mod.fmt;
|
||||
const toJson = cbor_mod.toJson;
|
||||
const toJsonPretty = cbor_mod.toJsonPretty;
|
||||
const fromJson = cbor_mod.fromJson;
|
||||
const decodeType = cbor_mod.decodeType;
|
||||
const matchInt = cbor_mod.matchInt;
|
||||
const matchIntValue = cbor_mod.matchIntValue;
|
||||
const matchValue = cbor_mod.matchValue;
|
||||
const match = cbor_mod.match;
|
||||
const isNull = cbor_mod.isNull;
|
||||
const writeArrayHeader = cbor_mod.writeArrayHeader;
|
||||
const writeMapHeader = cbor_mod.writeMapHeader;
|
||||
const writeValue = cbor_mod.writeValue;
|
||||
const extract = cbor_mod.extract;
|
||||
const extract_cbor = cbor_mod.extract_cbor;
|
||||
|
||||
const more = cbor_mod.more;
|
||||
const any = cbor_mod.any;
|
||||
const string = cbor_mod.string;
|
||||
const number = cbor_mod.number;
|
||||
const array = cbor_mod.array;
|
||||
const map = cbor_mod.map;
|
||||
|
||||
test "cbor simple" {
|
||||
var buf: [128]u8 = undefined;
|
||||
try expectEqualDeep(
|
||||
fmt(&buf, .{ "five", 5, "four", 4, .{ "three", 3 } }),
|
||||
&[_]u8{ 0x85, 0x64, 0x66, 0x69, 0x76, 0x65, 0x05, 0x64, 0x66, 0x6f, 0x75, 0x72, 0x04, 0x82, 0x65, 0x74, 0x68, 0x72, 0x65, 0x65, 0x03 },
|
||||
);
|
||||
}
|
||||
|
||||
test "cbor exit message" {
|
||||
var buf: [128]u8 = undefined;
|
||||
try expectEqualDeep(
|
||||
fmt(&buf, .{ "exit", "normal" }),
|
||||
&[_]u8{ 0x82, 0x64, 0x65, 0x78, 0x69, 0x74, 0x66, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C },
|
||||
);
|
||||
}
|
||||
|
||||
test "cbor error.OutOfMemory" {
|
||||
var buf: [128]u8 = undefined;
|
||||
try expectEqualDeep(
|
||||
fmt(&buf, .{ "exit", error.OutOfMemory }),
|
||||
&[_]u8{ 0x82, 0x64, 0x65, 0x78, 0x69, 0x74, 0x71, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x2E, 0x4F, 0x75, 0x74, 0x4F, 0x66, 0x4D, 0x65, 0x6D, 0x6F, 0x72, 0x79 },
|
||||
);
|
||||
}
|
||||
|
||||
test "cbor error.OutOfMemory json" {
|
||||
var buf: [128]u8 = undefined;
|
||||
var json_buf: [128]u8 = undefined;
|
||||
const cbor = fmt(&buf, .{ "exit", error.OutOfMemory });
|
||||
try expectEqualDeep(toJson(cbor, &json_buf),
|
||||
\\["exit","error.OutOfMemory"]
|
||||
);
|
||||
}
|
||||
|
||||
test "cbor.decodeType2" {
|
||||
var buf = [_]u8{ 0x20, 0xDF };
|
||||
var iter: []const u8 = &buf;
|
||||
const t = try decodeType(&iter);
|
||||
try expectEqual(t.major, 1);
|
||||
try expectEqual(t.minor, 0);
|
||||
try expectEqual(iter[0], 0xDF);
|
||||
}
|
||||
|
||||
test "cbor.decodeType3" {
|
||||
var buf = [_]u8{ 0x03, 0xDF };
|
||||
var iter: []const u8 = &buf;
|
||||
const t = try decodeType(&iter);
|
||||
try expectEqual(t.major, 0);
|
||||
try expectEqual(t.minor, 3);
|
||||
try expectEqual(iter[0], 0xDF);
|
||||
}
|
||||
|
||||
test "cbor.matchI64 small" {
|
||||
var buf = [_]u8{ 1, 0xDF };
|
||||
var iter: []const u8 = &buf;
|
||||
var val: i64 = 0;
|
||||
try expect(try matchInt(i64, &iter, &val));
|
||||
try expectEqual(val, 1);
|
||||
try expectEqual(iter[0], 0xDF);
|
||||
}
|
||||
|
||||
test "cbor.matchI64 1byte" {
|
||||
var buf = [_]u8{ 0x18, 0x1A, 0xDF };
|
||||
var iter: []const u8 = &buf;
|
||||
var val: i64 = 0;
|
||||
try expect(try matchInt(i64, &iter, &val));
|
||||
try expectEqual(val, 26);
|
||||
try expectEqual(iter[0], 0xDF);
|
||||
}
|
||||
|
||||
test "cbor.matchI64 2byte" {
|
||||
var buf = [_]u8{ 0x19, 0x01, 0x07, 0xDF };
|
||||
var iter: []const u8 = &buf;
|
||||
var val: i64 = 0;
|
||||
try expect(try matchInt(i64, &iter, &val));
|
||||
try expectEqual(val, 263);
|
||||
try expectEqual(iter[0], 0xDF);
|
||||
}
|
||||
|
||||
test "cbor.matchI64 8byte" {
|
||||
var buf = [_]u8{ 0x1B, 0x00, 0x00, 0xEF, 0x6F, 0xC1, 0x4A, 0x0A, 0x1F, 0xDF };
|
||||
var iter: []const u8 = &buf;
|
||||
var val: i64 = 0;
|
||||
try expect(try matchInt(i64, &iter, &val));
|
||||
try expectEqual(val, 263263263263263);
|
||||
try expectEqual(iter[0], 0xDF);
|
||||
}
|
||||
|
||||
test "cbor.matchI64 error.CborIntegerTooLarge" {
|
||||
var buf = [_]u8{ 0x1B, 0xA9, 0x0A, 0xDE, 0x0D, 0x4E, 0x2B, 0x8A, 0x1F, 0xDF };
|
||||
var iter: []const u8 = &buf;
|
||||
var val: i64 = 0;
|
||||
const result = matchInt(i64, &iter, &val);
|
||||
try expectError(error.CborIntegerTooLarge, result);
|
||||
}
|
||||
|
||||
test "cbor.matchI64 error.CborTooShort" {
|
||||
var buf = [_]u8{ 0x19, 0x01 };
|
||||
var iter: []const u8 = &buf;
|
||||
var val: i64 = 0;
|
||||
const result = matchInt(i64, &iter, &val);
|
||||
try expectError(error.CborTooShort, result);
|
||||
}
|
||||
|
||||
test "cbor.matchI64Value" {
|
||||
var buf = [_]u8{ 7, 0xDF };
|
||||
var iter: []const u8 = &buf;
|
||||
try expect(try matchIntValue(i64, &iter, 7));
|
||||
}
|
||||
|
||||
test "cbor.matchValue(i64)" {
|
||||
var buf: [128]u8 = undefined;
|
||||
var iter = fmt(&buf, 7);
|
||||
try expect(try matchValue(&iter, 7));
|
||||
}
|
||||
|
||||
test "cbor.matchValue(i64) multi" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const iter = fmt(&buf, 7);
|
||||
const iter2 = fmt(buf[iter.len..], 8);
|
||||
var iter3 = buf[0 .. iter.len + iter2.len];
|
||||
try expect(try matchValue(&iter3, 7));
|
||||
try expect(try matchValue(&iter3, 8));
|
||||
}
|
||||
|
||||
test "cbor.match(.{i64...})" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ 5, 4, 3, 123456, 234567890 };
|
||||
const m = fmt(&buf, v);
|
||||
try expect(try match(m, v));
|
||||
}
|
||||
|
||||
test "cbor.match(.{i64... more})" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ 5, 4, 3, 123456, 234567890, 6, 5, 4, 3, 2, 1 };
|
||||
const m = fmt(&buf, v);
|
||||
try expect(try match(m, .{ 5, 4, 3, more }));
|
||||
}
|
||||
|
||||
test "cbor.match(.{any, i64... more})" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ "cbor", 4, 3, 123456, 234567890, 6, 5, 4, 3, 2, 1 };
|
||||
const m = fmt(&buf, v);
|
||||
try expect(try match(m, .{ any, 4, 3, more }));
|
||||
}
|
||||
|
||||
test "cbor.match(.{types...})" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ "five", 5, "four", 4, .{ "three", 3 } };
|
||||
const m = fmt(&buf, v);
|
||||
try expect(try match(m, .{ string, number, string, number, array }));
|
||||
|
||||
try expect(!try match(m, .{ string, number, string, number }));
|
||||
try expect(!try match(m, .{ number, string, number, array, any }));
|
||||
try expect(try match(m, .{ any, number, string, number, any }));
|
||||
|
||||
try expect(try match(m, .{ "five", number, string, 4, any }));
|
||||
try expect(try match(m, .{ "five", 5, "four", 4, array }));
|
||||
try expect(try match(m, .{ "five", 5, more }));
|
||||
try expect(try match(m, .{ "five", 5, "four", 4, array, more }));
|
||||
try expect(!try match(m, .{ "five", 5, "four", 4, array, any }));
|
||||
try expect(!try match(m, .{ "four", 5, "five", 4, array, any }));
|
||||
try expect(try match(m, .{ "five", 5, "four", 4, .{ "three", 3 } }));
|
||||
}
|
||||
|
||||
test "cbor.nulls" {
|
||||
var buf: [128]u8 = undefined;
|
||||
try expect(isNull(fmt(&buf, .{})));
|
||||
try expect(!isNull(fmt(&buf, .{1})));
|
||||
}
|
||||
|
||||
test "cbor.stream_writer" {
|
||||
var buf: [128]u8 = undefined;
|
||||
var stream = std.io.fixedBufferStream(&buf);
|
||||
const writer = stream.writer();
|
||||
try writeArrayHeader(writer, 5);
|
||||
try writeValue(writer, "five");
|
||||
try writeValue(writer, 5);
|
||||
try writeValue(writer, "four");
|
||||
try writeValue(writer, 4);
|
||||
try writeArrayHeader(writer, 2);
|
||||
try writeValue(writer, "three");
|
||||
try writeValue(writer, 3);
|
||||
try expect(try match(stream.getWritten(), .{ "five", 5, "four", 4, .{ "three", 3 } }));
|
||||
}
|
||||
|
||||
test "cbor.stream_object_writer" {
|
||||
var buf: [128]u8 = undefined;
|
||||
var stream = std.io.fixedBufferStream(&buf);
|
||||
const writer = stream.writer();
|
||||
try writeMapHeader(writer, 3);
|
||||
try writeValue(writer, "five");
|
||||
try writeValue(writer, 5);
|
||||
try writeValue(writer, "four");
|
||||
try writeValue(writer, 4);
|
||||
try writeValue(writer, "three");
|
||||
try writeValue(writer, 3);
|
||||
const obj = stream.getWritten();
|
||||
var json_buf: [128]u8 = undefined;
|
||||
const json = try toJson(obj, &json_buf);
|
||||
try expectEqualDeep(json,
|
||||
\\{"five":5,"four":4,"three":3}
|
||||
);
|
||||
}
|
||||
|
||||
test "cbor.match_bool" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const m = fmt(&buf, .{ false, true, 5, "five" });
|
||||
try expect(try match(m, .{ false, true, 5, "five" }));
|
||||
try expect(!try match(m, .{ true, false, 5, "five" }));
|
||||
}
|
||||
|
||||
test "cbor.extract_match" {
|
||||
var buf: [128]u8 = undefined;
|
||||
var t: bool = false;
|
||||
const extractor = extract(&t);
|
||||
var iter = fmt(&buf, true);
|
||||
try expect(try extractor.extract(&iter));
|
||||
try expect(t);
|
||||
iter = fmt(&buf, false);
|
||||
try expect(try extractor.extract(&iter));
|
||||
try expect(!t);
|
||||
|
||||
const m = fmt(&buf, .{ false, true, 5, "five" });
|
||||
try expect(try match(m, .{ extract(&t), true, 5, "five" }));
|
||||
try expect(!t);
|
||||
try expect(try match(m, .{ false, extract(&t), 5, "five" }));
|
||||
try expect(t);
|
||||
|
||||
var i: i64 = undefined;
|
||||
try expect(try match(m, .{ false, true, extract(&i), "five" }));
|
||||
try expect(i == 5);
|
||||
|
||||
var u: u64 = undefined;
|
||||
try expect(try match(m, .{ false, true, extract(&u), "five" }));
|
||||
try expect(u == 5);
|
||||
|
||||
var s: []const u8 = undefined;
|
||||
try expect(try match(m, .{ false, true, 5, extract(&s) }));
|
||||
try expect(std.mem.eql(u8, "five", s));
|
||||
}
|
||||
|
||||
test "cbor.extract_cbor" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ "five", 5, "four", 4, .{ "three", 3 } };
|
||||
const m = fmt(&buf, v);
|
||||
|
||||
try expect(try match(m, .{ "five", 5, "four", 4, .{ "three", 3 } }));
|
||||
|
||||
var sub: []const u8 = undefined;
|
||||
try expect(try match(m, .{ "five", 5, "four", 4, extract_cbor(&sub) }));
|
||||
try expect(try match(sub, .{ "three", 3 }));
|
||||
}
|
||||
|
||||
test "cbor.extract_nested" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ "five", 5, "four", 4, .{ "three", 3 } };
|
||||
const m = fmt(&buf, v);
|
||||
|
||||
var u: u64 = undefined;
|
||||
try expect(try match(m, .{ "five", 5, "four", 4, .{ "three", extract(&u) } }));
|
||||
try expect(u == 3);
|
||||
}
|
||||
|
||||
test "cbor.match_map" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ "five", 5, "four", 4, .{ .three = 3 } };
|
||||
const m = fmt(&buf, v);
|
||||
try expect(try match(m, .{ "five", 5, "four", 4, map }));
|
||||
}
|
||||
|
||||
test "cbor.extract_map_cbor" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ "five", 5, "four", 4, .{ .three = 3 } };
|
||||
const m = fmt(&buf, v);
|
||||
var map_cbor: []const u8 = undefined;
|
||||
try expect(try match(m, .{ "five", 5, "four", 4, extract_cbor(&map_cbor) }));
|
||||
var json_buf: [256]u8 = undefined;
|
||||
const json = try toJson(map_cbor, &json_buf);
|
||||
try expectEqualDeep(json,
|
||||
\\{"three":3}
|
||||
);
|
||||
}
|
||||
|
||||
test "cbor.extract_map" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ "five", 5, "four", 4, .{ .three = 3 } };
|
||||
const m = fmt(&buf, v);
|
||||
var obj = std.json.ObjectMap.init(std.testing.allocator);
|
||||
defer obj.deinit();
|
||||
try expect(try match(m, .{ "five", 5, "four", 4, extract(&obj) }));
|
||||
try expect(obj.contains("three"));
|
||||
try expectEqual(obj.get("three").?, std.json.Value{ .integer = 3 });
|
||||
}
|
||||
|
||||
test "cbor.extract_map_map" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ "five", 5, "four", 4, .{ .three = 3, .child = .{ .two = 2, .one = .{ 1, 2, 3, true, false, null } } } };
|
||||
const m = fmt(&buf, v);
|
||||
var a = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer a.deinit();
|
||||
var obj = std.json.ObjectMap.init(a.allocator());
|
||||
defer obj.deinit();
|
||||
try expect(try match(m, .{ "five", 5, "four", 4, extract(&obj) }));
|
||||
try expect(obj.contains("three"));
|
||||
try expectEqual(obj.get("three").?, std.json.Value{ .integer = 3 });
|
||||
var child = obj.get("child").?.object;
|
||||
try expectEqual(child.get("two"), std.json.Value{ .integer = 2 });
|
||||
|
||||
var json_buf: [256]u8 = undefined;
|
||||
const json = try toJson(m, &json_buf);
|
||||
try expectEqualDeep(json,
|
||||
\\["five",5,"four",4,{"three":3,"child":{"two":2,"one":[1,2,3,true,false,null]}}]
|
||||
);
|
||||
}
|
||||
|
||||
test "cbor.extract_value" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ "five", 5, "four", 4, .{ .three = 3 } };
|
||||
const m = fmt(&buf, v);
|
||||
var value = std.json.Value{ .null = {} };
|
||||
var value_int = std.json.Value{ .null = {} };
|
||||
try expect(try match(m, .{ "five", 5, extract(&value), extract(&value_int), any }));
|
||||
try expectEqualDeep(value.string, "four");
|
||||
try expectEqualDeep(value_int.integer, 4);
|
||||
}
|
||||
|
||||
test "cbor.match_more_nested" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const v = .{ .{124}, .{ "three", 3 } };
|
||||
const m = fmt(&buf, v);
|
||||
try expect(try match(m, .{ .{more}, .{more} }));
|
||||
}
|
||||
|
||||
test "cbor.extract_number_limits" {
|
||||
var buf: [128]u8 = undefined;
|
||||
const bigint: u64 = 18446744073709551615;
|
||||
var u: u64 = undefined;
|
||||
var i: i64 = undefined;
|
||||
const m = fmt(&buf, bigint);
|
||||
try expect(try match(m, extract(&u)));
|
||||
try expectError(error.CborIntegerTooLarge, match(m, extract(&i)));
|
||||
}
|
||||
|
||||
test "cbor.toJson" {
|
||||
var buf: [128]u8 = undefined;
|
||||
var json_buf: [128]u8 = undefined;
|
||||
const a = fmt(&buf, .{ "string\r\n", "\tstring\t", "\r\nstring" });
|
||||
const json = try toJson(a, &json_buf);
|
||||
try expect(try match(a, .{ "string\r\n", "\tstring\t", "\r\nstring" }));
|
||||
try expectEqualDeep(json,
|
||||
\\["string\r\n","\tstring\t","\r\nstring"]
|
||||
);
|
||||
}
|
||||
|
||||
test "cbor.toJson_object" {
|
||||
var buf: [128]u8 = undefined;
|
||||
var json_buf: [128]u8 = undefined;
|
||||
const a = fmt(&buf, .{ .five = 5, .four = 4, .three = 3 });
|
||||
const json = try toJson(a, &json_buf);
|
||||
try expectEqualDeep(json,
|
||||
\\{"five":5,"four":4,"three":3}
|
||||
);
|
||||
}
|
||||
|
||||
test "cbor.toJsonPretty_object" {
|
||||
var buf: [128]u8 = undefined;
|
||||
var json_buf: [128]u8 = undefined;
|
||||
const a = fmt(&buf, .{ .five = 5, .four = 4, .three = 3 });
|
||||
const json = try toJsonPretty(a, &json_buf);
|
||||
try expectEqualDeep(json,
|
||||
\\{
|
||||
\\ "five": 5,
|
||||
\\ "four": 4,
|
||||
\\ "three": 3
|
||||
\\}
|
||||
);
|
||||
}
|
||||
|
||||
test "cbor.fromJson_small" {
|
||||
var cbor_buf: [128]u8 = undefined;
|
||||
const json_buf: []const u8 =
|
||||
\\[12345]
|
||||
;
|
||||
const cbor = try fromJson(json_buf, &cbor_buf);
|
||||
try expect(try match(cbor, .{12345}));
|
||||
}
|
||||
|
||||
test "cbor.fromJson" {
|
||||
var cbor_buf: [128]u8 = undefined;
|
||||
const json_buf: []const u8 =
|
||||
\\["string\r\n","\tstring\t","\r\nstring",12345]
|
||||
;
|
||||
const cbor = try fromJson(json_buf, &cbor_buf);
|
||||
try expect(try match(cbor, .{ "string\r\n", "\tstring\t", "\r\nstring", 12345 }));
|
||||
}
|
||||
|
||||
test "cbor.fromJson_object" {
|
||||
var cbor_buf: [128]u8 = undefined;
|
||||
const json_buf: []const u8 =
|
||||
\\{"five":5,"four":4,"three":3}
|
||||
;
|
||||
const cbor = try fromJson(json_buf, &cbor_buf);
|
||||
try expect(try match(cbor, map));
|
||||
}
|
65
test/tests_cpp.zig
Normal file
65
test/tests_cpp.zig
Normal file
|
@ -0,0 +1,65 @@
|
|||
const std = @import("std");
|
||||
const c = @cImport({
|
||||
@cInclude("tests.h");
|
||||
});
|
||||
|
||||
fn testcase(name: [*c]const u8) !void {
|
||||
const result = c.runtestcase(name);
|
||||
try std.testing.expectEqual(@as(c_int, 0), result);
|
||||
}
|
||||
|
||||
test "cbor_match" {
|
||||
try testcase("cbor_match");
|
||||
}
|
||||
|
||||
test "debug" {
|
||||
try testcase("debug");
|
||||
}
|
||||
|
||||
test "endpoint_unx" {
|
||||
try testcase("endpoint_unx");
|
||||
}
|
||||
|
||||
test "endpoint_tcp" {
|
||||
try testcase("endpoint_tcp");
|
||||
}
|
||||
|
||||
test "hub_filter" {
|
||||
try testcase("hub_filter");
|
||||
}
|
||||
|
||||
test "ip_tcp_client_server" {
|
||||
try testcase("ip_tcp_client_server");
|
||||
}
|
||||
|
||||
test "ip_udp_echo" {
|
||||
try testcase("ip_udp_echo");
|
||||
}
|
||||
|
||||
test "metronome_test" {
|
||||
try testcase("metronome_test");
|
||||
}
|
||||
|
||||
test "perf_cbor" {
|
||||
try testcase("perf_cbor");
|
||||
}
|
||||
|
||||
test "perf_hub" {
|
||||
try testcase("perf_hub");
|
||||
}
|
||||
|
||||
test "perf_ring" {
|
||||
try testcase("perf_ring");
|
||||
}
|
||||
|
||||
test "perf_spawn" {
|
||||
try testcase("perf_spawn");
|
||||
}
|
||||
|
||||
test "spawn_exit" {
|
||||
try testcase("spawn_exit");
|
||||
}
|
||||
|
||||
test "timeout_test" {
|
||||
try testcase("timeout_test");
|
||||
}
|
14
test/tests_thespian.zig
Normal file
14
test/tests_thespian.zig
Normal file
|
@ -0,0 +1,14 @@
|
|||
const std = @import("std");
|
||||
const thespian = @import("thespian");
|
||||
const cbor = @import("cbor");
|
||||
|
||||
pub const unexpected = thespian.unexpected;
|
||||
const message = thespian.message;
|
||||
const error_message = thespian.error_message;
|
||||
|
||||
test "thespian.unexpected" {
|
||||
var buf: [512]u8 = undefined;
|
||||
try std.testing.expectEqual(error.Exit, unexpected(message.fmt(.{"TEST"})));
|
||||
const json = try cbor.toJson(error_message(), &buf);
|
||||
try std.testing.expectEqualStrings("[\"exit\",\"UNEXPECTED_MESSAGE: [\\\"TEST\\\"]\"]", json);
|
||||
}
|
64
test/timeout_test.cpp
Normal file
64
test/timeout_test.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
#include "tests.hpp"
|
||||
|
||||
#include "thespian/env.hpp"
|
||||
#include "thespian/handle.hpp"
|
||||
#include <thespian/instance.hpp>
|
||||
#include <thespian/timeout.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
using cbor::array;
|
||||
using cbor::buffer;
|
||||
using std::move;
|
||||
using std::shared_ptr;
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono::system_clock;
|
||||
using thespian::context;
|
||||
using thespian::create_timeout;
|
||||
using thespian::env_t;
|
||||
using thespian::exit;
|
||||
using thespian::exit_handler;
|
||||
using thespian::ok;
|
||||
using thespian::receive;
|
||||
using thespian::result;
|
||||
using thespian::timeout;
|
||||
using thespian::unexpected;
|
||||
|
||||
using clk = std::chrono::system_clock;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
auto initA() -> result {
|
||||
thespian::link(thespian::env().proc("log"));
|
||||
struct state_t {
|
||||
system_clock::time_point start{clk::now()};
|
||||
timeout t{create_timeout(200ms, array("timeout"))};
|
||||
|
||||
auto receive(const buffer &m) {
|
||||
if (!m("timeout"))
|
||||
return unexpected(m);
|
||||
auto end = clk::now();
|
||||
auto us = duration_cast<microseconds>(end - start);
|
||||
return exit(us > 200ms ? "done" : "failed");
|
||||
}
|
||||
};
|
||||
shared_ptr<state_t> state{new state_t};
|
||||
receive([state](auto, auto m) mutable { return state->receive(m); });
|
||||
return ok();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto timeout_test(context &ctx, bool &result, env_t env) -> ::result {
|
||||
return to_result(ctx.spawn_link(
|
||||
initA,
|
||||
[&](auto s) {
|
||||
if (s == "done")
|
||||
result = true;
|
||||
},
|
||||
"timeout", move(env)));
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue