#include "AsyncTest.h"
#include <uv.h> // for getaddrinfo
#include <sstream>
#include <iostream>
#include <unistd.h>
#include <panda/log.h>
namespace panda { namespace unievent { namespace test {
using panda::net::SockAddr;
static int refused_port = 10000;
SockAddr AsyncTest::get_refused_addr () {
++refused_port;
if (refused_port > 40000) refused_port = 10000;
#ifdef _WIN32
return SockAddr::Inet4("0.1.1.1", refused_port);
#elif defined(__APPLE__) || defined(__NetBSD__)
return SockAddr::Inet4("0.0.0.0", refused_port);
#else
static TcpSP rs;
if (!rs) {
rs = new Tcp();
rs->bind("127.0.0.1", 0);
}
return rs->sockaddr().value();
#endif
}
SockAddr AsyncTest::get_blackhole_addr () {
static SockAddr ret;
if (!ret) {
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
addrinfo* res;
int syserr = getaddrinfo("google.com", "81", &hints, &res);
if (syserr) throw std::system_error(std::make_error_code(((std::errc)syserr)));
ret = SockAddr(res->ai_addr, sizeof(*res->ai_addr));
freeaddrinfo(res);
}
return ret;
}
AsyncTest::AsyncTest (uint64_t timeout, const std::vector<string>& expected, const LoopSP& loop)
: loop(loop ? loop : LoopSP(new Loop()))
, expected(expected)
, timer(create_timeout(timeout))
{
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
}
AsyncTest::AsyncTest (uint64_t timeout, unsigned count, const LoopSP& loop) : AsyncTest(timeout, std::vector<string>(), loop) {
set_expected(count);
}
AsyncTest::~AsyncTest() noexcept(false) {
if (std::uncaught_exception()) {
return;
}
loop->run_nowait();
if (!happened_as_expected()) {
throw Error("Test exits in bad state", *this);
}
}
void AsyncTest::set_expected (unsigned count) {
expected.clear();
for (unsigned i = 0; i < count; ++i) expected.push_back("<event>");
}
void AsyncTest::set_expected (const std::vector<string>& v) {
expected = v;
}
bool AsyncTest::run () { return loop->run(); }
bool AsyncTest::run_once () { return loop->run_once(); }
bool AsyncTest::run_nowait () { return loop->run_nowait(); }
void AsyncTest::happens (string event) {
if (event) {
happened.push_back(event);
}
}
bool AsyncTest::wait (uint64_t timeout) {
bool by_timer = false;
auto timer = Timer::create_once(timeout, [&](Timer*) {
by_timer = true;
loop->stop();
}, loop); (void)timer;
run();
return by_timer;
}
std::string AsyncTest::generate_report() {
std::stringstream out;
for (size_t i = 0; i < std::max(expected.size(), happened.size()); ++i) {
if (i >= expected.size()) {
out << "\t\"" << happened[i] << "\" was not expected" << std::endl;
continue;
}
if (i >= happened.size()) {
out << "\t\"" << expected[i] << "\" has not happened" << std::endl;
continue;
}
if (happened[i] != expected[i]) {
out << "\t" << "wrong event " << happened[i] << ", " << expected[i] << " expected at pos " << i << std::endl;
break;
}
}
if (happened_as_expected()) {
out << "OK" << std::endl;
} else {
out << "Expected: ";
for (auto& e : expected) {
out << "\"" << e << "\",";
}
out << std::endl << "Happened: ";
for (auto& h : happened) {
out << "\"" << h << "\",";
}
out << std::endl;
}
return out.str();
}
bool AsyncTest::happened_as_expected() {
if (happened.size() != expected.size()) {
return false;
}
for (size_t i = 0; i < happened.size(); ++i) {
if (happened[i] != expected[i]) {
return false;
}
}
return true;
}
TimerSP AsyncTest::create_timeout(uint64_t timeout) {
if (!timeout) return nullptr;
auto ret = Timer::create_once(timeout, [&](auto&) {
throw Error("AsyncTest timeout", *this);
}, loop);
ret->weak(true);
return ret;
}
AsyncTest::Error::Error(std::string msg, AsyncTest& test)
: std::runtime_error(msg + "\nAsyncTest report:\n" + test.generate_report())
{}
}}}