From 9b40e1ec2b84e0442bc6ef6466d5a8a523c82250 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 18 Jun 2016 22:43:00 +0100 Subject: Initial commit of IceSpider, implements basic route compiler --- icespider/Jamfile.jam | 9 ++ icespider/common/Jamfile.jam | 12 ++ icespider/common/routes.ice | 29 +++++ icespider/compile/Jamfile.jam | 37 ++++++ icespider/compile/main.cpp | 20 +++ icespider/compile/routeCompiler.cpp | 246 ++++++++++++++++++++++++++++++++++++ icespider/compile/routeCompiler.h | 36 ++++++ icespider/core/Jamfile.jam | 9 ++ icespider/core/ihttpRequest.cpp | 10 ++ icespider/core/ihttpRequest.h | 25 ++++ icespider/core/irouteHandler.cpp | 19 +++ icespider/core/irouteHandler.h | 32 +++++ icespider/fcgi/Jamfile.jam | 7 + icespider/fcgi/main.cpp | 22 ++++ icespider/unittests/Jamfile.jam | 44 +++++++ icespider/unittests/test-api.ice | 13 ++ icespider/unittests/testCompile.cpp | 105 +++++++++++++++ icespider/unittests/testRoutes.json | 44 +++++++ 18 files changed, 719 insertions(+) create mode 100644 icespider/Jamfile.jam create mode 100644 icespider/common/Jamfile.jam create mode 100644 icespider/common/routes.ice create mode 100644 icespider/compile/Jamfile.jam create mode 100644 icespider/compile/main.cpp create mode 100644 icespider/compile/routeCompiler.cpp create mode 100644 icespider/compile/routeCompiler.h create mode 100644 icespider/core/Jamfile.jam create mode 100644 icespider/core/ihttpRequest.cpp create mode 100644 icespider/core/ihttpRequest.h create mode 100644 icespider/core/irouteHandler.cpp create mode 100644 icespider/core/irouteHandler.h create mode 100644 icespider/fcgi/Jamfile.jam create mode 100644 icespider/fcgi/main.cpp create mode 100644 icespider/unittests/Jamfile.jam create mode 100644 icespider/unittests/test-api.ice create mode 100644 icespider/unittests/testCompile.cpp create mode 100644 icespider/unittests/testRoutes.json diff --git a/icespider/Jamfile.jam b/icespider/Jamfile.jam new file mode 100644 index 0000000..20ca41b --- /dev/null +++ b/icespider/Jamfile.jam @@ -0,0 +1,9 @@ +build-project core ; +build-project compile ; +build-project unittests ; +build-project fcgi ; + +lib Ice ; +lib IceUtil ; +lib pthread ; + diff --git a/icespider/common/Jamfile.jam b/icespider/common/Jamfile.jam new file mode 100644 index 0000000..081ebd1 --- /dev/null +++ b/icespider/common/Jamfile.jam @@ -0,0 +1,12 @@ +lib icespider-common : + [ glob-tree *.ice *.cpp : bin ] + : + ..//pthread + ..//Ice + ..//IceUtil + : : + ..//pthread + ..//Ice + ..//IceUtil + ; + diff --git a/icespider/common/routes.ice b/icespider/common/routes.ice new file mode 100644 index 0000000..6ec1d50 --- /dev/null +++ b/icespider/common/routes.ice @@ -0,0 +1,29 @@ +module UserIceSpider { + enum HttpMethod { + GET, POST, DELETE + }; + enum ParameterSource { + URL, Body, QueryString, Header + }; + class Parameter { + string name; + ParameterSource source; + optional(0) string key; + }; + sequence Parameters; + class Route { + string name; + string path; + HttpMethod method; + string operation; + Parameters params; + }; + sequence Routes; + sequence Slices; + class RouteConfiguration { + string name; + Routes routes; + Slices slices; + }; +}; + diff --git a/icespider/compile/Jamfile.jam b/icespider/compile/Jamfile.jam new file mode 100644 index 0000000..5d31fb1 --- /dev/null +++ b/icespider/compile/Jamfile.jam @@ -0,0 +1,37 @@ +lib adhocutil : : : : /usr/include/adhocutil ; +lib slicer : : : : /usr/include/slicer /usr/include/adhocutil ; +lib slicer-json ; +lib boost_program_options ; +lib boost_system ; +lib boost_filesystem ; +lib Slice ; + +lib icespider-compile : + [ glob-tree *.cpp : bin main.cpp ] + [ glob ../common/*.ice ] + : + pure + slicer + adhocutil + slicer-json + boost_system + boost_filesystem + Slice + ../common + ../common + ..//Ice + ..//IceUtil + ..//pthread + : : + boost_system + boost_filesystem + ../common + ; + +exe icespider : + main.cpp + : + boost_program_options + icespider-compile + ; + diff --git a/icespider/compile/main.cpp b/icespider/compile/main.cpp new file mode 100644 index 0000000..e96574e --- /dev/null +++ b/icespider/compile/main.cpp @@ -0,0 +1,20 @@ +#include +namespace po = boost::program_options; + +int +main(int c, char ** v) +{ + std::string input, output; + po::options_description opts("IceSpider compile options"); + opts.add_options() + ("input", po::value(&input), "Input .json file") + ("output", po::value(&output), "Output .cpp file") + ; + po::positional_options_description pod; + pod.add("input", 1).add("output", 2); + po::variables_map vm; + po::store(po::command_line_parser(c, v).options(opts).positional(pod).run(), vm); + po::notify(vm); + return 0; +} + diff --git a/icespider/compile/routeCompiler.cpp b/icespider/compile/routeCompiler.cpp new file mode 100644 index 0000000..bce8095 --- /dev/null +++ b/icespider/compile/routeCompiler.cpp @@ -0,0 +1,246 @@ +#include "routeCompiler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace IceSpider { + namespace Compile { + class StringValue : public Slicer::ValueTarget { + public: + StringValue(std::string & t) : target(t) { } + void get(const bool &) const override { } + void get(const Ice::Byte &) const override { } + void get(const Ice::Short &) const override { } + void get(const Ice::Int &) const override { } + void get(const Ice::Long &) const override { } + void get(const Ice::Float &) const override { } + void get(const Ice::Double &) const override { } + void get(const std::string & v) const override { target = v; } + std::string & target; + }; + + RouteCompiler::RouteCompiler() + { + searchPath.insert(boost::filesystem::current_path()); + } + + UserIceSpider::RouteConfigurationPtr + RouteCompiler::loadConfiguration(const boost::filesystem::path & input) const + { + auto deserializer = Slicer::DeserializerPtr(Slicer::FileDeserializerFactory::createNew(input.extension().string(), input)); + return Slicer::DeserializeAnyWith(deserializer); + } + + Ice::StringSeq operator+(Ice::StringSeq ss, const std::string & s) + { + ss.push_back(s); + return ss; + } + + Slice::OperationPtr + RouteCompiler::findOperation(UserIceSpider::RoutePtr r, const Slice::ContainerPtr & c, const Ice::StringSeq & ns) + { + for (const auto & cls : c->classes()) { + auto fqcn = ns + cls->name(); + for (const auto & op : cls->allOperations()) { + auto fqon = boost::algorithm::join(fqcn + op->name(), "."); + if (fqon == r->operation) { + return op; + } + } + } + for (const auto & m : c->modules()) { + auto op = findOperation(r, m, ns + m->name()); + if (op) return op; + } + return NULL; + } + + Slice::OperationPtr + RouteCompiler::findOperation(UserIceSpider::RoutePtr r, const Units & us) + { + for (const auto & u : us) { + auto op = findOperation(r, u.second); + if (op) return op; + } + throw std::runtime_error("Find operator failed for " + r->name); + } + + void + RouteCompiler::applyDefaults(UserIceSpider::RouteConfigurationPtr c, const Units & u) const + { + for (const auto & r : c->routes) { + auto o = findOperation(r, u); + for (const auto & p : o->parameters()) { + auto defined = std::find_if(r->params.begin(), r->params.end(), [p](const auto & rp) { + return p->name() == rp->name; + }); + if (defined != r->params.end()) { + auto d = *defined; + if (!d->key) d->key = d->name; + continue; + } + r->params.push_back(new UserIceSpider::Parameter(p->name(), UserIceSpider::ParameterSource::URL, p->name())); + } + } + } + + void + RouteCompiler::compile(const boost::filesystem::path & input, const boost::filesystem::path & output) const + { + auto configuration = loadConfiguration(input); + auto units = loadUnits(configuration); + applyDefaults(configuration, units); + + AdHoc::ScopeExit uDestroy([&units]() { + for (auto & u : units) { + u.second->destroy(); + } + }); + + FILE * out = fopen(output.c_str(), "w"); + if (!out) { + throw std::runtime_error("Failed to open output file"); + } + AdHoc::ScopeExit outClose( + [out]() { fclose(out); }, + NULL, + [&output]() { boost::filesystem::remove(output); }); + processConfiguration(out, configuration, units); + } + + RouteCompiler::Units + RouteCompiler::loadUnits(UserIceSpider::RouteConfigurationPtr c) const + { + RouteCompiler::Units units; + AdHoc::ScopeExit uDestroy; + for (const auto & slice : c->slices) { + boost::filesystem::path realSlice; + for (const auto & p : searchPath) { + if (boost::filesystem::exists(p / slice)) { + realSlice = p / slice; + break; + } + } + if (realSlice.empty()) { + throw std::runtime_error("Could not find slice file"); + } + std::vector cppArgs; + Slice::PreprocessorPtr icecpp = Slice::Preprocessor::create("IceSpider", realSlice.string(), cppArgs); + FILE * cppHandle = icecpp->preprocess(false); + + if (cppHandle == NULL) { + throw std::runtime_error("Preprocess failed"); + } + + Slice::UnitPtr u = Slice::Unit::createUnit(false, false, false, false); + uDestroy.onFailure.push_back([u]() { u->destroy(); }); + + int parseStatus = u->parse(realSlice.string(), cppHandle, false); + + if (!icecpp->close()) { + throw std::runtime_error("Preprocess close failed"); + } + + if (parseStatus == EXIT_FAILURE) { + throw std::runtime_error("Unit parse failed"); + } + + units[slice] = u; + } + return units; + } + + template + void + fprintbf(unsigned int indent, FILE * output, const X & ... x) + { + for (; indent > 0; --indent) { + fprintf(output, "\t"); + } + fprintbf(output, x...); + } + + template + std::string getEnumString(Enum & e) + { + std::string rtn; + Slicer::ModelPart::CreateFor(e)->GetValue(new StringValue(rtn)); + return rtn; + } + + void + RouteCompiler::processConfiguration(FILE * output, UserIceSpider::RouteConfigurationPtr c, const Units & units) const + { + fprintf(output, "// This source files was generated by IceSpider.\n"); + fprintbf(output, "// Configuration name: %s\n\n", c->name); + + fprintf(output, "// Standard headers.\n"); + fprintf(output, "#include \n"); + + fprintf(output, "\n// Interface headers.\n"); + for (const auto & s : c->slices) { + boost::filesystem::path slicePath(s); + slicePath.replace_extension(".h"); + fprintbf(output, "#include <%s>\n", slicePath.string()); + } + + fprintf(output, "\n"); + fprintbf(output, "namespace %s {\n", c->name); + fprintbf(1, output, "// Implementation classes.\n\n"); + for (const auto & r : c->routes) { + auto proxyName = r->operation.substr(0, r->operation.find_last_of('.')); + auto operation = r->operation.substr(r->operation.find_last_of('.') + 1); + boost::algorithm::replace_all(proxyName, ".", "::"); + std::string methodName = getEnumString(r->method); + + fprintbf(1, output, "// Route name: %s\n", r->name); + fprintbf(1, output, "// path: %s\n", r->path); + fprintbf(1, output, "class %s : public IceSpider::IRouteHandler {\n", r->name); + fprintbf(2, output, "public:\n"); + fprintbf(3, output, "%s() :\n", r->name); + fprintbf(4, output, "IceSpider::IRouteHandler(UserIceSpider::HttpMethod::%s, \"%s\")", methodName, r->path); + for (const auto & p : r->params) { + fprintf(output, ",\n"); + fprintbf(4, output, "_pn_%s(\"%s\")", p->name, *p->key); + } + fprintf(output, "\n"); + fprintbf(3, output, "{\n"); + fprintbf(3, output, "}\n\n"); + fprintbf(3, output, "void execute(IceSpider::IHttpRequest * request) const\n"); + fprintbf(3, output, "{\n"); + auto o = findOperation(r, units); + for (const auto & p : r->params) { + auto ip = *std::find_if(o->parameters().begin(), o->parameters().end(), [p](const auto & ip) { return ip->name() == p->name; }); + fprintbf(4, output, "auto _p_%s(request->get%sParam<%s>(_pn_%s));\n", + p->name, getEnumString(p->source), Slice::typeToString(ip->type()), p->name); + } + fprintbf(4, output, "auto prx = getProxy<%s>();\n", proxyName); + fprintbf(4, output, "prx->%s(", operation); + for (const auto & p : r->params) { + fprintbf(output, "_p_%s, ", p->name); + } + fprintbf(output, "request->getContext());\n"); + fprintbf(3, output, "}\n\n"); + fprintbf(2, output, "private:\n"); + for (const auto & p : r->params) { + fprintbf(3, output, "std::string _pn_%s;\n", p->name); + } + fprintbf(1, output, "};\n\n"); + } + fprintbf(output, "} // namespace %s\n\n", c->name); + fprintf(output, "// Register route handlers.\n"); + for (const auto & r : c->routes) { + fprintbf(output, "PLUGIN(%s::%s, IceSpider::IRouteHandler);\n", c->name, r->name); + } + fprintf(output, "\n// End generated code.\n"); + } + } +} + diff --git a/icespider/compile/routeCompiler.h b/icespider/compile/routeCompiler.h new file mode 100644 index 0000000..2ac1f4b --- /dev/null +++ b/icespider/compile/routeCompiler.h @@ -0,0 +1,36 @@ +#ifndef ICESPIDER_COMPILE_ROURTECOMPILER_H +#define ICESPIDER_COMPILE_ROURTECOMPILER_H + +#include +#include +#include +#include +#include +#include + +namespace IceSpider { + namespace Compile { + class DLL_PUBLIC RouteCompiler { + public: + typedef std::map Units; + + RouteCompiler(); + + UserIceSpider::RouteConfigurationPtr loadConfiguration(const boost::filesystem::path &) const; + Units loadUnits(UserIceSpider::RouteConfigurationPtr) const; + + void applyDefaults(UserIceSpider::RouteConfigurationPtr, const Units & u) const; + void compile(const boost::filesystem::path & input, const boost::filesystem::path & output) const; + + std::set searchPath; + + private: + void processConfiguration(FILE * output, UserIceSpider::RouteConfigurationPtr, const Units &) const; + static Slice::OperationPtr findOperation(UserIceSpider::RoutePtr, const Units &); + static Slice::OperationPtr findOperation(UserIceSpider::RoutePtr, const Slice::ContainerPtr &, const Ice::StringSeq & = Ice::StringSeq()); + }; + } +} + +#endif + diff --git a/icespider/core/Jamfile.jam b/icespider/core/Jamfile.jam new file mode 100644 index 0000000..45bc30a --- /dev/null +++ b/icespider/core/Jamfile.jam @@ -0,0 +1,9 @@ +lib adhocutil : : : : /usr/include/adhocutil ; + +lib icespider-core : + [ glob-tree *.cpp : bin ] + : + ../common + adhocutil + ../common + ; diff --git a/icespider/core/ihttpRequest.cpp b/icespider/core/ihttpRequest.cpp new file mode 100644 index 0000000..423e439 --- /dev/null +++ b/icespider/core/ihttpRequest.cpp @@ -0,0 +1,10 @@ +#include "ihttpRequest.h" + +namespace IceSpider { + Ice::Context + IHttpRequest::getContext() const + { + return Ice::Context(); + } +} + diff --git a/icespider/core/ihttpRequest.h b/icespider/core/ihttpRequest.h new file mode 100644 index 0000000..76361b6 --- /dev/null +++ b/icespider/core/ihttpRequest.h @@ -0,0 +1,25 @@ +#ifndef ICESPIDER_IHTTPREQUEST_H +#define ICESPIDER_IHTTPREQUEST_H + +#include +#include +#include +#include + +namespace IceSpider { + class DLL_PUBLIC IHttpRequest { + public: + Ice::Context getContext() const; + template + T getURLParam(const std::string & key) const { (void)key; return T(); } + template + T getBodyParam(const std::string & key) const { (void)key; return T(); } + template + T getQueryStringParam(const std::string & key) const { (void)key; return T(); } + template + T getHeaderParam(const std::string & key) const { (void)key; return T(); } + }; +} + +#endif + diff --git a/icespider/core/irouteHandler.cpp b/icespider/core/irouteHandler.cpp new file mode 100644 index 0000000..2107ff7 --- /dev/null +++ b/icespider/core/irouteHandler.cpp @@ -0,0 +1,19 @@ +#include "irouteHandler.h" +#include + +INSTANTIATEPLUGINOF(IceSpider::IRouteHandler); + +namespace IceSpider { + IRouteHandler::IRouteHandler(UserIceSpider::HttpMethod m, const std::string & p) : + method(m), + path(p) + { + } + + Ice::ObjectPrx + IRouteHandler::getProxy(const char *) const + { + return NULL; + } +} + diff --git a/icespider/core/irouteHandler.h b/icespider/core/irouteHandler.h new file mode 100644 index 0000000..e02d0a3 --- /dev/null +++ b/icespider/core/irouteHandler.h @@ -0,0 +1,32 @@ +#ifndef ICESPIDER_IROUTEHANDLER_H +#define ICESPIDER_IROUTEHANDLER_H + +#include "ihttpRequest.h" +#include +#include +#include + +namespace IceSpider { + class DLL_PUBLIC IRouteHandler : public AdHoc::AbstractPluginImplementation { + public: + IRouteHandler(UserIceSpider::HttpMethod, const std::string & path); + virtual void execute(IHttpRequest * request) const = 0; + + const UserIceSpider::HttpMethod method; + const std::string path; + + protected: + Ice::ObjectPrx getProxy(const char *) const; + + template + typename Interface::ProxyType getProxy() const + { + return Interface::ProxyType::uncheckedCast(getProxy(typeid(Interface).name())); + } + }; + typedef AdHoc::PluginOf RouteHandlers; +} + +#endif + + diff --git a/icespider/fcgi/Jamfile.jam b/icespider/fcgi/Jamfile.jam new file mode 100644 index 0000000..eb0c3f2 --- /dev/null +++ b/icespider/fcgi/Jamfile.jam @@ -0,0 +1,7 @@ +lib fcgi : : fcgi ; + +lib icespider-fcgi : + [ glob-tree *.cpp : bin ] + : + fcgi + ; diff --git a/icespider/fcgi/main.cpp b/icespider/fcgi/main.cpp new file mode 100644 index 0000000..6132769 --- /dev/null +++ b/icespider/fcgi/main.cpp @@ -0,0 +1,22 @@ +#include + +int +main(void) +{ + if (!FCGX_IsCGI()) { + FCGX_Request request; + + FCGX_Init(); + FCGX_InitRequest(&request, 0, 0); + + while (FCGX_Accept_r(&request) == 0) { + // app.process(IO, &IO, IO); + FCGX_Finish_r(&request); + } + return 0; + } + else { + return 1; + } +} + diff --git a/icespider/unittests/Jamfile.jam b/icespider/unittests/Jamfile.jam new file mode 100644 index 0000000..92a3434 --- /dev/null +++ b/icespider/unittests/Jamfile.jam @@ -0,0 +1,44 @@ +import testing ; + +lib adhocutil : : : : /usr/include/adhocutil ; +lib boost_utf : : boost_unit_test_framework ; +lib dl ; +path-constant me : . ; + +alias testCommon : : + ROOT=\"$(me)\" + ..//Ice + ..//IceUtil + : : + ..//IceUtil + ..//Ice + boost_utf + ROOT=\"$(me)\" + ; + +run + testCompile.cpp + : : + test-api.ice + testRoutes.json + : + BOOST_TEST_DYN_LINK + testCommon + test-api + dl + adhocutil + ../core//icespider-core + ../compile//icespider-compile + ../compile//icespider-compile + ../core + : testCompile : + ; + +lib test-api : + test-api.ice + : + ..//pthread + ..//Ice + ..//IceUtil + ; + diff --git a/icespider/unittests/test-api.ice b/icespider/unittests/test-api.ice new file mode 100644 index 0000000..4fc87bd --- /dev/null +++ b/icespider/unittests/test-api.ice @@ -0,0 +1,13 @@ +module TestIceSpider { + class SomeModel { + string value; + }; + + interface TestApi { + SomeModel index(); + SomeModel withParams(string s, int i); + void returnNothing(string s); + void complexParam(string s, SomeModel m); + }; +}; + diff --git a/icespider/unittests/testCompile.cpp b/icespider/unittests/testCompile.cpp new file mode 100644 index 0000000..c53fe65 --- /dev/null +++ b/icespider/unittests/testCompile.cpp @@ -0,0 +1,105 @@ +#define BOOST_TEST_MODULE TestCompile +#include + +#include +#include +#include +#include "../compile/routeCompiler.h" +#include "../core/irouteHandler.h" +#include + +class CoreFixture { + protected: + CoreFixture() : + modeDir(binDir.lexically_relative(rootDir / "bin" / "testCompile.test")) + { + } + boost::filesystem::path modeDir; +}; + +BOOST_FIXTURE_TEST_SUITE(cf, CoreFixture) + +BOOST_AUTO_TEST_CASE( testLoadConfiguration ) +{ + IceSpider::Compile::RouteCompiler rc; + rc.searchPath.insert(rootDir); + auto cfg = rc.loadConfiguration(rootDir / "testRoutes.json"); + auto u = rc.loadUnits(cfg); + rc.applyDefaults(cfg, u); + + BOOST_REQUIRE_EQUAL("common", cfg->name); + BOOST_REQUIRE_EQUAL(4, cfg->routes.size()); + + BOOST_REQUIRE_EQUAL("index", cfg->routes[0]->name); + BOOST_REQUIRE_EQUAL("/", cfg->routes[0]->path); + BOOST_REQUIRE_EQUAL(UserIceSpider::HttpMethod::GET, cfg->routes[0]->method); + BOOST_REQUIRE_EQUAL("TestIceSpider.TestApi.index", cfg->routes[0]->operation); + BOOST_REQUIRE_EQUAL(0, cfg->routes[0]->params.size()); + + BOOST_REQUIRE_EQUAL("item", cfg->routes[1]->name); + BOOST_REQUIRE_EQUAL("/{s}/{i}", cfg->routes[1]->path); + BOOST_REQUIRE_EQUAL(2, cfg->routes[1]->params.size()); + + BOOST_REQUIRE_EQUAL("del", cfg->routes[2]->name); + BOOST_REQUIRE_EQUAL(UserIceSpider::HttpMethod::DELETE, cfg->routes[2]->method); + BOOST_REQUIRE_EQUAL(1, cfg->routes[2]->params.size()); + + BOOST_REQUIRE_EQUAL("update", cfg->routes[3]->name); + BOOST_REQUIRE_EQUAL(UserIceSpider::HttpMethod::POST, cfg->routes[3]->method); + BOOST_REQUIRE_EQUAL(2, cfg->routes[3]->params.size()); + + BOOST_REQUIRE_EQUAL(1, cfg->slices.size()); + BOOST_REQUIRE_EQUAL("test-api.ice", cfg->slices[0]); +} + +BOOST_AUTO_TEST_CASE( testCompile ) +{ + IceSpider::Compile::RouteCompiler rc; + rc.searchPath.insert(rootDir); + auto input = rootDir / "testRoutes.json"; + auto output = binDir / "testRoutes.cpp"; + auto outputso = boost::filesystem::change_extension(output, ".so"); + auto libGenDir = (rootDir / "bin" / modeDir); + rc.compile(input, output); + + auto compileCommand = boost::algorithm::join>({ + "gcc", "-shared", "-std=c++1y", "-fPIC", " -fvisibility=hidden", "-O3", "-Wl,--warn-once,--gc-sections", + "-I", "/usr/include/adhocutil", + "-I", (rootDir.parent_path() / "core").string(), + "-I", (rootDir.parent_path() / "common").string(), + "-I", (rootDir.parent_path() / "common" / "bin" / modeDir).string(), + "-I", libGenDir.string(), + "-o", outputso.string(), + output.string(), + }, " "); + BOOST_TEST_INFO("Compile command: " << compileCommand); + int compileResult = system(compileCommand.c_str()); + BOOST_REQUIRE_EQUAL(0, compileResult); +} + +class DuffRequest : public IceSpider::IHttpRequest { + +}; + +BOOST_AUTO_TEST_CASE( testLoad ) +{ + auto outputso = binDir / "testRoutes.so"; + auto lib = dlopen(outputso.c_str(), RTLD_NOW); + BOOST_TEST_INFO(dlerror()); + BOOST_REQUIRE(lib); + + BOOST_REQUIRE_EQUAL(4, AdHoc::PluginManager::getDefault()->getAll().size()); + // smoke test (block ensure dlclose dones't cause segfault) + { + auto route = AdHoc::PluginManager::getDefault()->get("common::index"); + BOOST_REQUIRE(route); + BOOST_REQUIRE_THROW({ + route->implementation()->execute(new DuffRequest()); + }, IceUtil::NullHandleException); + } + + BOOST_REQUIRE_EQUAL(0, dlclose(lib)); +} + +BOOST_AUTO_TEST_SUITE_END(); + diff --git a/icespider/unittests/testRoutes.json b/icespider/unittests/testRoutes.json new file mode 100644 index 0000000..b4eb54f --- /dev/null +++ b/icespider/unittests/testRoutes.json @@ -0,0 +1,44 @@ +{ + "name": "common", + "routes": [ + { + "name": "index", + "path": "/", + "method": "GET", + "operation": "TestIceSpider.TestApi.index" + }, + { + "name": "item", + "path": "/{s}/{i}", + "method": "GET", + "operation": "TestIceSpider.TestApi.withParams" + }, + { + "name": "del", + "path": "/{s}", + "method": "DELETE", + "operation": "TestIceSpider.TestApi.returnNothing" + }, + { + "name": "update", + "path": "/{id}", + "method": "POST", + "operation": "TestIceSpider.TestApi.complexParam", + "params": [ + { + "name": "s", + "source": "URL", + "key": "id" + }, + { + "name": "m", + "source": "Body" + } + ] + } + ], + "slices": [ + "test-api.ice" + ] +} + -- cgit v1.2.3