|
#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 */
|