diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2016-06-18 22:43:00 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2016-06-18 22:43:00 +0100 |
commit | 9b40e1ec2b84e0442bc6ef6466d5a8a523c82250 (patch) | |
tree | ff3566336ac19298f5e21895c49d81ceba3c828a | |
parent | Build most things by default (diff) | |
download | icespider-9b40e1ec2b84e0442bc6ef6466d5a8a523c82250.tar.bz2 icespider-9b40e1ec2b84e0442bc6ef6466d5a8a523c82250.tar.xz icespider-9b40e1ec2b84e0442bc6ef6466d5a8a523c82250.zip |
Initial commit of IceSpider, implements basic route compiler
-rw-r--r-- | icespider/Jamfile.jam | 9 | ||||
-rw-r--r-- | icespider/common/Jamfile.jam | 12 | ||||
-rw-r--r-- | icespider/common/routes.ice | 29 | ||||
-rw-r--r-- | icespider/compile/Jamfile.jam | 37 | ||||
-rw-r--r-- | icespider/compile/main.cpp | 20 | ||||
-rw-r--r-- | icespider/compile/routeCompiler.cpp | 246 | ||||
-rw-r--r-- | icespider/compile/routeCompiler.h | 36 | ||||
-rw-r--r-- | icespider/core/Jamfile.jam | 9 | ||||
-rw-r--r-- | icespider/core/ihttpRequest.cpp | 10 | ||||
-rw-r--r-- | icespider/core/ihttpRequest.h | 25 | ||||
-rw-r--r-- | icespider/core/irouteHandler.cpp | 19 | ||||
-rw-r--r-- | icespider/core/irouteHandler.h | 32 | ||||
-rw-r--r-- | icespider/fcgi/Jamfile.jam | 7 | ||||
-rw-r--r-- | icespider/fcgi/main.cpp | 22 | ||||
-rw-r--r-- | icespider/unittests/Jamfile.jam | 44 | ||||
-rw-r--r-- | icespider/unittests/test-api.ice | 13 | ||||
-rw-r--r-- | icespider/unittests/testCompile.cpp | 105 | ||||
-rw-r--r-- | icespider/unittests/testRoutes.json | 44 |
18 files changed, 719 insertions, 0 deletions
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 ] + : + <library>..//pthread + <library>..//Ice + <library>..//IceUtil + : : + <library>..//pthread + <library>..//Ice + <library>..//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<Parameter> Parameters; + class Route { + string name; + string path; + HttpMethod method; + string operation; + Parameters params; + }; + sequence<Route> Routes; + sequence<string> 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 : : : : <include>/usr/include/adhocutil ; +lib slicer : : : : <include>/usr/include/slicer <include>/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 ] + : + <slicer>pure + <library>slicer + <library>adhocutil + <library>slicer-json + <library>boost_system + <library>boost_filesystem + <library>Slice + <library>../common + <implicit-dependency>../common + <library>..//Ice + <library>..//IceUtil + <library>..//pthread + : : + <library>boost_system + <library>boost_filesystem + <library>../common + ; + +exe icespider : + main.cpp + : + <library>boost_program_options + <library>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 <boost/program_options.hpp> +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 <slicer/slicer.h> +#include <slicer/modelPartsTypes.h> +#include <Slice/Preprocessor.h> +#include <scopeExit.h> +#include <fprintbf.h> +#include <boost/filesystem/convenience.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <boost/algorithm/string/join.hpp> +#include <Slice/CPlusPlusUtil.h> + +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<UserIceSpider::RouteConfigurationPtr>(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<std::string> 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<typename ... X> + void + fprintbf(unsigned int indent, FILE * output, const X & ... x) + { + for (; indent > 0; --indent) { + fprintf(output, "\t"); + } + fprintbf(output, x...); + } + + template<typename Enum> + std::string getEnumString(Enum & e) + { + std::string rtn; + Slicer::ModelPart::CreateFor<Enum>(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 <irouteHandler.h>\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 <boost/filesystem/path.hpp> +#include <visibility.h> +#include <set> +#include <routes.h> +#include <Slice/Parser.h> +#include <Ice/BuiltinSequences.h> + +namespace IceSpider { + namespace Compile { + class DLL_PUBLIC RouteCompiler { + public: + typedef std::map<std::string, Slice::UnitPtr> 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<boost::filesystem::path> 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 : : : : <include>/usr/include/adhocutil ; + +lib icespider-core : + [ glob-tree *.cpp : bin ] + : + <library>../common + <library>adhocutil + <implicit-dependency>../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 <IceUtil/Exception.h> +#include <IceUtil/Optional.h> +#include <Ice/Current.h> +#include <visibility.h> + +namespace IceSpider { + class DLL_PUBLIC IHttpRequest { + public: + Ice::Context getContext() const; + template<typename T> + T getURLParam(const std::string & key) const { (void)key; return T(); } + template<typename T> + T getBodyParam(const std::string & key) const { (void)key; return T(); } + template<typename T> + T getQueryStringParam(const std::string & key) const { (void)key; return T(); } + template<typename T> + 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 <plugins.impl.h> + +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 <routes.h> +#include <plugins.h> +#include <visibility.h> + +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> + typename Interface::ProxyType getProxy() const + { + return Interface::ProxyType::uncheckedCast(getProxy(typeid(Interface).name())); + } + }; + typedef AdHoc::PluginOf<IRouteHandler> 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 : : <name>fcgi ; + +lib icespider-fcgi : + [ glob-tree *.cpp : bin ] + : + <library>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 <fcgio.h> + +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 : : : : <include>/usr/include/adhocutil ; +lib boost_utf : : <name>boost_unit_test_framework ; +lib dl ; +path-constant me : . ; + +alias testCommon : : + <define>ROOT=\"$(me)\" + <library>..//Ice + <library>..//IceUtil + : : + <library>..//IceUtil + <library>..//Ice + <library>boost_utf + <define>ROOT=\"$(me)\" + ; + +run + testCompile.cpp + : : + test-api.ice + testRoutes.json + : + <define>BOOST_TEST_DYN_LINK + <library>testCommon + <library>test-api + <library>dl + <library>adhocutil + <library>../core//icespider-core + <library>../compile//icespider-compile + <implicit-dependency>../compile//icespider-compile + <dependency>../core + : testCompile : + ; + +lib test-api : + test-api.ice + : + <library>..//pthread + <library>..//Ice + <library>..//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 <boost/test/unit_test.hpp> + +#include <definedDirs.h> +#include <plugins.h> +#include <dlfcn.h> +#include "../compile/routeCompiler.h" +#include "../core/irouteHandler.h" +#include <boost/algorithm/string/join.hpp> + +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<std::vector<std::string>>({ + "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<IceSpider::IRouteHandler>().size()); + // smoke test (block ensure dlclose dones't cause segfault) + { + auto route = AdHoc::PluginManager::getDefault()->get<IceSpider::IRouteHandler>("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" + ] +} + |