diff options
| -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" +	] +} +  | 
