Project

General

Profile

Bug #5903 » IntegrationTestHelper.hpp

J n, 09/01/2017 10:29 PM

 
#ifndef FUMA_INTEGRATION_TEST_HELPER_HPP
#define FUMA_INTEGRATION_TEST_HELPER_HPP

#include <Wt/WResource>
#include <Wt/WServer>
#include <Wt/Http/Client>
#include <thread>
#include <mutex>
#include <condition_variable>

#include <vector>

namespace Wt
{
namespace Http
{
class Response;
class ResponseContinuation;
class Request;
}
}

namespace Fuma
{
namespace Test
{
struct StringResource
{
std::string m_data;
explicit StringResource(const std::string & val)
: m_data{val}
{}
void write(const Wt::Http::Request & request, Wt::Http::Response & response) const;
};

struct StringVecResource
{
std::vector<std::string> m_data;
explicit StringVecResource(const std::string & val)
: m_data{10,val}
{}
void write(const Wt::Http::Request & request, Wt::Http::Response & response) const;
};

class Resource
: public Wt::WResource
{
int aborted_;
StringResource simple_fixture_;
StringVecResource stream_fixture_;

public:
Resource();
virtual ~Resource();
// accessors
int abortedCount() const;
// interface requirements
protected:
virtual void handleAbort(const Wt::Http::Request & request);
virtual void handleRequest(const Wt::Http::Request & request, Wt::Http::Response & response);
};

class Server
: public Wt::WServer
{
Resource resource_;
public:
Server();
std::string address() const;
Resource & resource();
const Resource & resource() const;
};

class Client
: public Wt::Http::Client
{
bool done_;
bool abortAfterHeaders_;
std::condition_variable doneCondition_;
std::mutex doneMutex_;
std::mutex dataMutex_;

boost::system::error_code err_;
Wt::Http::Message message_;
public:
Client();
bool isDone() const;
const boost::system::error_code & err() const;
const Wt::Http::Message & message() const;
void abortAfterHeaders();
void waitDone();
void onDone(boost::system::error_code err, const Wt::Http::Message & m);
void onHeadersReceived(const Wt::Http::Message & m);
void onDataReceived(const std::string & d);
};
} // Fuma::Test namespace
} // Fuma namespace

#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif

#include <Wt/WConfig.h>
#include <Wt/Http/Response>
#include <Wt/Http/ResponseContinuation>
#include <Wt/Http/Request>
#include <Wt/Utils>
#include <boost/asio/error.hpp>
namespace Fuma
{
namespace Test
{
Client::Client()
: done_(false)
, abortAfterHeaders_(false)
, err_{boost::asio::error::bad_descriptor}
{
setTimeout(15);
setMaximumResponseSize(0);
setFollowRedirect(true);
setMaxRedirects(10);
done().connect(this, &Client::onDone);
headersReceived().connect(this, &Client::onHeadersReceived);
bodyDataReceived().connect(this, &Client::onDataReceived);
}

void Client::abortAfterHeaders()
{
abortAfterHeaders_ = true;
}

void Client::waitDone()
{
std::unique_lock<decltype(doneMutex_)> guard(doneMutex_);
doneCondition_.wait(guard,[this] { return done_;});
}

bool Client::isDone() const
{
return done_;
}

void Client::onDone(boost::system::error_code err, const Wt::Http::Message & m)
{
std::unique_lock<decltype(doneMutex_)> guard(doneMutex_);
err_ = err;
done_ = true;
doneCondition_.notify_one();
}

void Client::onHeadersReceived(const Wt::Http::Message & m)
{
std::unique_lock<decltype(dataMutex_)> guard(dataMutex_);
message_ = m;
if(abortAfterHeaders_)
{
abort();
}
}

void Client::onDataReceived(const std::string & d)
{
std::unique_lock<decltype(dataMutex_)> guard(dataMutex_);
message_.addBodyText(d);
}

const boost::system::error_code & Client::err() const
{
return err_;
}
const Wt::Http::Message & Client::message() const
{
return message_;
}

Resource::Resource()
: aborted_(0)
,simple_fixture_{"Hello"}
,stream_fixture_{"Hello"}
{ }

Resource::~Resource()
{
beingDeleted();
}
int Resource::abortedCount() const
{
return aborted_;
}
void Resource::handleRequest(const Wt::Http::Request & request, Wt::Http::Response & response)
{
if(request.getParameterMap().count("stream"))
{
stream_fixture_.write(request,response);
return;
}
simple_fixture_.write(request,response);
}
void Resource::handleAbort(const Wt::Http::Request & request)
{
++aborted_;
}

Server::Server()
{
int argc = 7;
const char * const argv[]
= { "test",
"--http-address", "127.0.0.1",
"--http-port", "0",
"--docroot", "."
};
setServerConfiguration(argc, const_cast<char **>(argv));
addResource(&resource_, "/test");
}

std::string Server::address() const
{
return "127.0.0.1:" + std::to_string(httpPort());
}
Resource & Server::resource()
{
return resource_;
}
const Resource & Server::resource() const
{
return resource_;
}

void StringResource::write(const Wt::Http::Request & request, Wt::Http::Response & response) const
{
response.out() << m_data;
}

void StringVecResource::write(const Wt::Http::Request & request, Wt::Http::Response & response) const
{
try
{
auto * c = request.continuation();
// mr kipling's exceedingly smug c++
size_t idx = (c) ? boost::any_cast<decltype(idx)>(c->data()) : decltype(idx) {} ;
size_t sz = m_data.size();
if(idx < sz)
{
response.out() << m_data.at(idx);
++idx;
if(idx < sz)
{
c = response.createContinuation();
c->setData(idx);
c->waitForMoreData();
c->haveMoreData();
}
}
}
catch(boost::bad_any_cast & err)
{
}
}

} // Fuma::Test namespace
} // Fuma namespace

#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif

#include <vector>
#include <memory>
#include <boost/test/unit_test.hpp>
//#include "TestHelper.hpp"

struct IntegrationFixture
{
Fuma::Test::Server m_server;
std::vector<std::unique_ptr<Fuma::Test::Client>> m_clients;
bool m_is_started;

enum state_t
{
eDontWait = 0,
eWaitDone = 1<<1,
eAbortAfterHeader = 1<<2,
eCancelledDownload = (eWaitDone|eAbortAfterHeader)
};

IntegrationFixture()
: m_server{}
, m_clients{}
, m_is_started{false}
{
}

auto & nth_client(size_t n)
{
return m_clients.at(n);
}

auto & nth_msg(size_t n)
{
return nth_client(n)->message();
}

auto & nth_err(size_t n)
{
return nth_client(n)->err();
}

int aborted_count() const
{
return m_server.resource().abortedCount();
}

size_t require_http_get_url_scheduled(const std::string & endpoint,state_t setup)
{
size_t sz = m_clients.size();
m_clients.emplace_back(std::make_unique<Fuma::Test::Client>());
if(setup & eAbortAfterHeader)
{
nth_client(sz)->abortAfterHeaders();
}
if(!m_is_started)
{
BOOST_REQUIRE(m_server.start());
m_is_started = true;
}
BOOST_REQUIRE(nth_client(sz)->get("http://" + m_server.address() + endpoint));
if(setup & eWaitDone)
{
nth_client(sz)->waitDone();
}
return sz;
}
};
#endif /* ndef FUMA_INTEGRATION_TEST_HELPER_HPP */
(2-2/2)