diff --git a/CHANGELOG.md b/CHANGELOG.md index 98a871bf4..83e1fab4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased - Changes from 5.27.1 + - Features + - ADDED: Add support for a default_radius flag. [#6575](https://github.com/Project-OSRM/osrm-backend/pull/6575) - Build: - ADDED: Add CI job which builds OSRM with gcc 12. [#6455](https://github.com/Project-OSRM/osrm-backend/pull/6455) - CHANGED: Upgrade to clang-tidy 15. [#6439](https://github.com/Project-OSRM/osrm-backend/pull/6439) @@ -8,6 +10,7 @@ - NodeJS: - CHANGED: Use node-api instead of NAN. [#6452](https://github.com/Project-OSRM/osrm-backend/pull/6452) - Misc: + - CHANGED: keep libosrm* in the docker image for downstream linking [#6602](https://github.com/Project-OSRM/osrm-backend/pull/6602) - CHANGED: Move vector in CSVFilesParser instead copying it. [#6470](https://github.com/Project-OSRM/osrm-backend/pull/6470) - REMOVED: Get rid of unused functions in util/json_util.hpp. [#6446](https://github.com/Project-OSRM/osrm-backend/pull/6446) - FIXED: Apply workaround for Conan installation issue on CI. [#6442](https://github.com/Project-OSRM/osrm-backend/pull/6442) @@ -18,6 +21,8 @@ - CHANGED: Make edge metrics strongly typed [#6420](https://github.com/Project-OSRM/osrm-backend/pull/6420) - FIXED: Typo in file name src/util/timed_historgram.cpp -> src/util/timed_histogram.cpp [#6428](https://github.com/Project-OSRM/osrm-backend/issues/6428) - CHANGED: Replace boost::string_ref with std::string_view [#6433](https://github.com/Project-OSRM/osrm-backend/pull/6433) + - ADDED: Print tracebacks for Lua runtime errors [#6564](https://github.com/Project-OSRM/osrm-backend/pull/6564) + - FIXED: Added a variable to preprocessor guard in file osrm-backend/include/util/range_table.hpp to solve build error. [#6596](https://github.com/Project-OSRM/osrm-backend/pull/6596) - Routing: - FIXED: Fix adding traffic signal penalties during compression [#6419](https://github.com/Project-OSRM/osrm-backend/pull/6419) # 5.27.1 diff --git a/docker/Dockerfile b/docker/Dockerfile index a44d3319f..7bb158132 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -37,7 +37,7 @@ RUN NPROC=${BUILD_CONCURRENCY:-$(nproc)} && \ cd ../profiles && \ cp -r * /opt && \ strip /usr/local/bin/* && \ - rm -rf /src /usr/local/lib/libosrm* + rm -rf /src # Multistage build to reduce image size - https://docs.docker.com/engine/userguide/eng-image/multistage-build/#use-multi-stage-builds diff --git a/docs/nodejs/api.md b/docs/nodejs/api.md index c4d389023..b24800210 100644 --- a/docs/nodejs/api.md +++ b/docs/nodejs/api.md @@ -38,6 +38,7 @@ var osrm = new OSRM('network.osrm'); - `options.max_radius_map_matching` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Max. radius size supported in map matching query (default: 5). - `options.max_results_nearest` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Max. results supported in nearest query (default: unlimited). - `options.max_alternatives` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Max. number of alternatives supported in alternative routes query (default: 3). + - `options.default_radius` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** Default radius for queries (default: unlimited). ### route diff --git a/docs/testing.md b/docs/testing.md index 5f608fb6e..ed258b805 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -300,7 +300,7 @@ And the relations The setting looks perfectly fine at first glance. However, it is not well defined. The forbidden right turn could be either a superfluous addition, forbidding the turn `cb` to `be`, or actually refer to the turn `ab` to `bd` to say that a turn is forbidden here. -To model turn-restrictions correctly and unique, we need to split segments that contribute to the restriction into the smallest possible parts. +To model turn-restrictions correctly and uniquely, we need to split segments that contribute to the restriction into the smallest possible parts. E.g. the above scenario could correctly be expressed as: ``` @@ -360,7 +360,7 @@ When I route I should get And the test reports `turn right` for the route `a->e`, where before it said `slight right`. -If you changed the turn angles, obviously you can expect changes in the distinction between `slight right` and `right`. +If you change the turn angles, obviously you can expect changes in the distinction between `slight right` and `right`. In such a case it is, of course, reasonable to change the expected route to report `right` instead of `slight right`. You should consider inspecting the actual turn angles at `b` to see if you feel that change is justified. However, you should never adjust the test itself. @@ -390,9 +390,9 @@ In this case we would see a very slight turn angle. If your change now reports d ### Consider Post-Processing Impacts -Some changes you might see could look completely unrelated. To understand the impact of your changes, you can make use of the debugging utilities you can finde in `util/debug.hpp` (and potentially other related headers). +Some changes you might see could look completely unrelated. To understand the impact of your changes, you can make use of the debugging utilities you can find in `util/debug.hpp` (and potentially other related headers). -If your test is inspecting a series of turns (remember, a turn not necessarily equals an instruction), you could see interaction with post-processing. +If your test is inspecting a series of turns (remember, a turn does not necessarily equals an instruction), you could see interaction with post-processing. To see the unprocessed turns, you should print the steps at the end of step assembly (`assembleSteps` in `engine/guidance/assemble_steps.hpp`). If you see unexpected changes, you can consider adding the `locations` field to your test to study what location a turn is reported at. diff --git a/features/options/routed/help.feature b/features/options/routed/help.feature index a581be3fe..401a5ba4f 100644 --- a/features/options/routed/help.feature +++ b/features/options/routed/help.feature @@ -22,6 +22,7 @@ Feature: osrm-routed command line options: help And stdout should contain "--max-trip-size" And stdout should contain "--max-table-size" And stdout should contain "--max-matching-size" + And stdout should contain "--default-radius" And it should exit successfully Scenario: osrm-routed - Help, short @@ -42,6 +43,7 @@ Feature: osrm-routed command line options: help And stdout should contain "--max-trip-size" And stdout should contain "--max-table-size" And stdout should contain "--max-matching-size" + And stdout should contain "--default-radius" And it should exit successfully Scenario: osrm-routed - Help, long @@ -62,4 +64,5 @@ Feature: osrm-routed command line options: help And stdout should contain "--max-table-size" And stdout should contain "--max-table-size" And stdout should contain "--max-matching-size" + And stdout should contain "--default-radius" And it should exit successfully diff --git a/include/engine/engine.hpp b/include/engine/engine.hpp index fcb4f2dc5..3e479e5cd 100644 --- a/include/engine/engine.hpp +++ b/include/engine/engine.hpp @@ -43,12 +43,16 @@ template class Engine final : public EngineInterface { public: explicit Engine(const EngineConfig &config) - : route_plugin(config.max_locations_viaroute, config.max_alternatives), // - table_plugin(config.max_locations_distance_table), // - nearest_plugin(config.max_results_nearest), // - trip_plugin(config.max_locations_trip), // - match_plugin(config.max_locations_map_matching, config.max_radius_map_matching), // - tile_plugin() // + : route_plugin(config.max_locations_viaroute, + config.max_alternatives, + config.default_radius), // + table_plugin(config.max_locations_distance_table, config.default_radius), // + nearest_plugin(config.max_results_nearest, config.default_radius), // + trip_plugin(config.max_locations_trip, config.default_radius), // + match_plugin(config.max_locations_map_matching, + config.max_radius_map_matching, + config.default_radius), // + tile_plugin() // { if (config.use_shared_memory) diff --git a/include/engine/engine_config.hpp b/include/engine/engine_config.hpp index 6f4594b15..5ca54ba1c 100644 --- a/include/engine/engine_config.hpp +++ b/include/engine/engine_config.hpp @@ -83,6 +83,7 @@ struct EngineConfig final int max_locations_map_matching = -1; double max_radius_map_matching = -1.0; int max_results_nearest = -1; + boost::optional default_radius; int max_alternatives = 3; // set an arbitrary upper bound; can be adjusted by user bool use_shared_memory = true; boost::filesystem::path memory_file; diff --git a/include/engine/plugins/match.hpp b/include/engine/plugins/match.hpp index 0bb4682ba..d56be60fe 100644 --- a/include/engine/plugins/match.hpp +++ b/include/engine/plugins/match.hpp @@ -20,8 +20,10 @@ class MatchPlugin : public BasePlugin using CandidateLists = routing_algorithms::CandidateLists; static const constexpr double RADIUS_MULTIPLIER = 3; - MatchPlugin(const int max_locations_map_matching, const double max_radius_map_matching) - : max_locations_map_matching(max_locations_map_matching), + MatchPlugin(const int max_locations_map_matching, + const double max_radius_map_matching, + const boost::optional default_radius) + : BasePlugin(default_radius), max_locations_map_matching(max_locations_map_matching), max_radius_map_matching(max_radius_map_matching) { } diff --git a/include/engine/plugins/nearest.hpp b/include/engine/plugins/nearest.hpp index e4c9212b2..415639b2c 100644 --- a/include/engine/plugins/nearest.hpp +++ b/include/engine/plugins/nearest.hpp @@ -13,7 +13,7 @@ namespace osrm::engine::plugins class NearestPlugin final : public BasePlugin { public: - explicit NearestPlugin(const int max_results); + explicit NearestPlugin(const int max_results, const boost::optional default_radius); Status HandleRequest(const RoutingAlgorithmsInterface &algorithms, const api::NearestParameters ¶ms, diff --git a/include/engine/plugins/plugin_base.hpp b/include/engine/plugins/plugin_base.hpp index d2084ae36..375456897 100644 --- a/include/engine/plugins/plugin_base.hpp +++ b/include/engine/plugins/plugin_base.hpp @@ -27,6 +27,10 @@ namespace osrm::engine::plugins class BasePlugin { protected: + BasePlugin() = default; + + BasePlugin(const boost::optional default_radius_) : default_radius(default_radius_) {} + bool CheckAllCoordinates(const std::vector &coordinates) const { return !std::any_of( @@ -237,7 +241,7 @@ class BasePlugin phantom_nodes[i] = facade.NearestPhantomNodes( parameters.coordinates[i], number_of_results, - use_radiuses ? parameters.radiuses[i] : boost::none, + use_radiuses ? parameters.radiuses[i] : default_radius, use_bearings ? parameters.bearings[i] : boost::none, use_approaches && parameters.approaches[i] ? parameters.approaches[i].get() : engine::Approach::UNRESTRICTED); @@ -279,7 +283,7 @@ class BasePlugin alternatives[i] = facade.NearestCandidatesWithAlternativeFromBigComponent( parameters.coordinates[i], - use_radiuses ? parameters.radiuses[i] : boost::none, + use_radiuses ? parameters.radiuses[i] : default_radius, use_bearings ? parameters.bearings[i] : boost::none, use_approaches && parameters.approaches[i] ? parameters.approaches[i].get() : engine::Approach::UNRESTRICTED, @@ -320,6 +324,8 @@ class BasePlugin return std::string("Could not find a matching segment for coordinate ") + std::to_string(missing_index); } + + const boost::optional default_radius; }; } // namespace osrm::engine::plugins diff --git a/include/engine/plugins/table.hpp b/include/engine/plugins/table.hpp index fc8fd18eb..e4cf9a33c 100644 --- a/include/engine/plugins/table.hpp +++ b/include/engine/plugins/table.hpp @@ -14,7 +14,8 @@ namespace osrm::engine::plugins class TablePlugin final : public BasePlugin { public: - explicit TablePlugin(const int max_locations_distance_table); + explicit TablePlugin(const int max_locations_distance_table, + const boost::optional default_radius); Status HandleRequest(const RoutingAlgorithmsInterface &algorithms, const api::TableParameters ¶ms, diff --git a/include/engine/plugins/trip.hpp b/include/engine/plugins/trip.hpp index 01b72a38c..9cfdab094 100644 --- a/include/engine/plugins/trip.hpp +++ b/include/engine/plugins/trip.hpp @@ -32,7 +32,10 @@ class TripPlugin final : public BasePlugin const bool roundtrip) const; public: - explicit TripPlugin(const int max_locations_trip_) : max_locations_trip(max_locations_trip_) {} + explicit TripPlugin(const int max_locations_trip_, boost::optional default_radius) + : BasePlugin(default_radius), max_locations_trip(max_locations_trip_) + { + } Status HandleRequest(const RoutingAlgorithmsInterface &algorithms, const api::TripParameters ¶meters, diff --git a/include/engine/plugins/viaroute.hpp b/include/engine/plugins/viaroute.hpp index d45dd44aa..e036ac90e 100644 --- a/include/engine/plugins/viaroute.hpp +++ b/include/engine/plugins/viaroute.hpp @@ -25,7 +25,9 @@ class ViaRoutePlugin final : public BasePlugin const int max_alternatives; public: - explicit ViaRoutePlugin(int max_locations_viaroute, int max_alternatives); + explicit ViaRoutePlugin(int max_locations_viaroute, + int max_alternatives, + boost::optional default_radius); Status HandleRequest(const RoutingAlgorithmsInterface &algorithms, const api::RouteParameters &route_parameters, diff --git a/include/extractor/scripting_environment_lua.hpp b/include/extractor/scripting_environment_lua.hpp index b81e9024c..ef5243454 100644 --- a/include/extractor/scripting_environment_lua.hpp +++ b/include/extractor/scripting_environment_lua.hpp @@ -41,10 +41,10 @@ struct LuaScriptingContext final bool has_way_function = false; bool has_segment_function = false; - sol::function turn_function; - sol::function way_function; - sol::function node_function; - sol::function segment_function; + sol::protected_function turn_function; + sol::protected_function way_function; + sol::protected_function node_function; + sol::protected_function segment_function; int api_version = 4; sol::table profile_table; diff --git a/include/nodejs/node_osrm_support.hpp b/include/nodejs/node_osrm_support.hpp index a1d7b4473..5296dc4ed 100644 --- a/include/nodejs/node_osrm_support.hpp +++ b/include/nodejs/node_osrm_support.hpp @@ -285,6 +285,7 @@ inline engine_config_ptr argumentsToEngineConfig(const Napi::CallbackInfo &args) auto max_results_nearest = params.Get("max_results_nearest"); auto max_alternatives = params.Get("max_alternatives"); auto max_radius_map_matching = params.Get("max_radius_map_matching"); + auto default_radius = params.Get("default_radius"); if (!max_locations_trip.IsUndefined() && !max_locations_trip.IsNumber()) { @@ -316,6 +317,11 @@ inline engine_config_ptr argumentsToEngineConfig(const Napi::CallbackInfo &args) ThrowError(args.Env(), "max_alternatives must be an integral number"); return engine_config_ptr(); } + if (!default_radius.IsUndefined() && !default_radius.IsNumber()) + { + ThrowError(args.Env(), "default_radius must be an integral number"); + return engine_config_ptr(); + } if (max_locations_trip.IsNumber()) engine_config->max_locations_trip = max_locations_trip.ToNumber().Int32Value(); @@ -333,6 +339,8 @@ inline engine_config_ptr argumentsToEngineConfig(const Napi::CallbackInfo &args) engine_config->max_alternatives = max_alternatives.ToNumber().Int32Value(); if (max_radius_map_matching.IsNumber()) engine_config->max_radius_map_matching = max_radius_map_matching.ToNumber().DoubleValue(); + if (default_radius.IsNumber()) + engine_config->default_radius = default_radius.ToNumber().DoubleValue(); return engine_config; } diff --git a/include/util/range_table.hpp b/include/util/range_table.hpp index 7bb6497c8..757462570 100644 --- a/include/util/range_table.hpp +++ b/include/util/range_table.hpp @@ -79,10 +79,10 @@ template class RangeTable unsigned last_length = 0; unsigned lengths_prefix_sum = 0; unsigned block_idx = 0; - unsigned block_counter = 0; BlockT block; #ifndef BOOST_ASSERT_IS_VOID unsigned block_sum = 0; + unsigned block_counter = 0; #endif for (const unsigned l : lengths) { @@ -109,7 +109,9 @@ template class RangeTable if (BLOCK_SIZE == block_idx) { diff_blocks.push_back(block); +#ifndef BOOST_ASSERT_IS_VOID block_counter++; +#endif } // we can only store strings with length 255 diff --git a/scripts/ci/leaksanitizer.conf b/scripts/ci/leaksanitizer.conf index 9f0e08c19..27c16d04a 100644 --- a/scripts/ci/leaksanitizer.conf +++ b/scripts/ci/leaksanitizer.conf @@ -6,3 +6,12 @@ # #1 0x7f7ae595d13e (/usr/lib/x86_64-linux-gnu/libtbb.so.2+0x2213e) leak:libtbb.so + +# The extract-tests leak some memory in the tests to confirm that +# lua errors print tracebacks. +# This appears to be because when these tests throw exceptions, the +# osmium objects being processed are not freed. In production this doesn't +# matter, as the exceptions bring down the entire osrm-extract process. In the +# tests, we catch the error to make sure it occurs, which is why the +# leaksanitizer flags it. +leak:extract-tests \ No newline at end of file diff --git a/src/engine/plugins/match.cpp b/src/engine/plugins/match.cpp index 8fe0d78e0..90517df18 100644 --- a/src/engine/plugins/match.cpp +++ b/src/engine/plugins/match.cpp @@ -181,24 +181,29 @@ Status MatchPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, if (tidied.parameters.radiuses.empty()) { search_radiuses.resize(tidied.parameters.coordinates.size(), - routing_algorithms::DEFAULT_GPS_PRECISION * RADIUS_MULTIPLIER); + default_radius.has_value() + ? *default_radius + : routing_algorithms::DEFAULT_GPS_PRECISION * RADIUS_MULTIPLIER); } else { search_radiuses.resize(tidied.parameters.coordinates.size()); - std::transform(tidied.parameters.radiuses.begin(), - tidied.parameters.radiuses.end(), - search_radiuses.begin(), - [](const boost::optional &maybe_radius) { - if (maybe_radius) - { - return *maybe_radius * RADIUS_MULTIPLIER; - } - else - { - return routing_algorithms::DEFAULT_GPS_PRECISION * RADIUS_MULTIPLIER; - } - }); + std::transform( + tidied.parameters.radiuses.begin(), + tidied.parameters.radiuses.end(), + search_radiuses.begin(), + [default_radius = this->default_radius](const boost::optional &maybe_radius) { + if (maybe_radius) + { + return *maybe_radius * RADIUS_MULTIPLIER; + } + else + { + return default_radius.has_value() + ? *default_radius + : routing_algorithms::DEFAULT_GPS_PRECISION * RADIUS_MULTIPLIER; + } + }); } auto candidates_lists = diff --git a/src/engine/plugins/nearest.cpp b/src/engine/plugins/nearest.cpp index bd6dd8e54..671dbe3f2 100644 --- a/src/engine/plugins/nearest.cpp +++ b/src/engine/plugins/nearest.cpp @@ -10,7 +10,10 @@ namespace osrm::engine::plugins { -NearestPlugin::NearestPlugin(const int max_results_) : max_results{max_results_} {} +NearestPlugin::NearestPlugin(const int max_results_, const boost::optional default_radius_) + : BasePlugin(default_radius_), max_results{max_results_} +{ +} Status NearestPlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, const api::NearestParameters ¶ms, diff --git a/src/engine/plugins/table.cpp b/src/engine/plugins/table.cpp index 150cefb34..451811305 100644 --- a/src/engine/plugins/table.cpp +++ b/src/engine/plugins/table.cpp @@ -14,8 +14,9 @@ namespace osrm::engine::plugins { -TablePlugin::TablePlugin(const int max_locations_distance_table) - : max_locations_distance_table(max_locations_distance_table) +TablePlugin::TablePlugin(const int max_locations_distance_table, + const boost::optional default_radius) + : BasePlugin(default_radius), max_locations_distance_table(max_locations_distance_table) { } diff --git a/src/engine/plugins/viaroute.cpp b/src/engine/plugins/viaroute.cpp index aa533d96a..fe0727936 100644 --- a/src/engine/plugins/viaroute.cpp +++ b/src/engine/plugins/viaroute.cpp @@ -15,8 +15,11 @@ namespace osrm::engine::plugins { -ViaRoutePlugin::ViaRoutePlugin(int max_locations_viaroute, int max_alternatives) - : max_locations_viaroute(max_locations_viaroute), max_alternatives(max_alternatives) +ViaRoutePlugin::ViaRoutePlugin(int max_locations_viaroute, + int max_alternatives, + boost::optional default_radius) + : BasePlugin(default_radius), max_locations_viaroute(max_locations_viaroute), + max_alternatives(max_alternatives) { } diff --git a/src/extractor/scripting_environment_lua.cpp b/src/extractor/scripting_environment_lua.cpp index 0ab3df52d..9576cc18b 100644 --- a/src/extractor/scripting_environment_lua.cpp +++ b/src/extractor/scripting_environment_lua.cpp @@ -91,6 +91,19 @@ struct to_lua_object : public boost::static_visitor }; } // namespace +// Handle a lua error thrown in a protected function by printing the traceback and bubbling +// exception up to caller. Lua errors are generally unrecoverable, so this exception should not be +// caught but instead should terminate the process. The point of having this error handler rather +// than just using unprotected Lua functions which terminate the process automatically is that this +// function provides more useful error messages including Lua tracebacks and line numbers. +void handle_lua_error(sol::protected_function_result &luares) +{ + sol::error luaerr = luares; + std::string msg = luaerr.what(); + std::cerr << msg << std::endl; + throw util::exception("Lua error (see stderr for traceback)"); +} + Sol2ScriptingEnvironment::Sol2ScriptingEnvironment( const std::string &file_name, const std::vector &location_dependent_data_paths) @@ -550,10 +563,16 @@ void Sol2ScriptingEnvironment::InitContext(LuaScriptingContext &context) std::numeric_limits::max()); // call initialize function - sol::function setup_function = function_table.value()["setup"]; + sol::protected_function setup_function = function_table.value()["setup"]; if (!setup_function.valid()) throw util::exception("Profile must have an setup() function."); - sol::optional profile_table = setup_function(); + + auto setup_result = setup_function(); + + if (!setup_result.valid()) + handle_lua_error(setup_result); + + sol::optional profile_table = setup_result; if (profile_table == sol::nullopt) throw util::exception("Profile setup() must return a table."); else @@ -1123,7 +1142,9 @@ void Sol2ScriptingEnvironment::ProcessTurn(ExtractionTurn &turn) case 2: if (context.has_turn_penalty_function) { - context.turn_function(context.profile_table, std::ref(turn)); + auto luares = context.turn_function(context.profile_table, std::ref(turn)); + if (!luares.valid()) + handle_lua_error(luares); // Turn weight falls back to the duration value in deciseconds // or uses the extracted unit-less weight value @@ -1138,7 +1159,9 @@ void Sol2ScriptingEnvironment::ProcessTurn(ExtractionTurn &turn) case 1: if (context.has_turn_penalty_function) { - context.turn_function(std::ref(turn)); + auto luares = context.turn_function(std::ref(turn)); + if (!luares.valid()) + handle_lua_error(luares); // Turn weight falls back to the duration value in deciseconds // or uses the extracted unit-less weight value @@ -1184,24 +1207,28 @@ void Sol2ScriptingEnvironment::ProcessSegment(ExtractionSegment &segment) if (context.has_segment_function) { + sol::protected_function_result luares; switch (context.api_version) { case 4: case 3: case 2: - context.segment_function(context.profile_table, std::ref(segment)); + luares = context.segment_function(context.profile_table, std::ref(segment)); break; case 1: - context.segment_function(std::ref(segment)); + luares = context.segment_function(std::ref(segment)); break; case 0: - context.segment_function(std::ref(segment.source), - std::ref(segment.target), - segment.distance, - segment.duration); + luares = context.segment_function(std::ref(segment.source), + std::ref(segment.target), + segment.distance, + segment.duration); segment.weight = segment.duration; // back-compatibility fallback to duration break; } + + if (!luares.valid()) + handle_lua_error(luares); } } @@ -1211,20 +1238,27 @@ void LuaScriptingContext::ProcessNode(const osmium::Node &node, { BOOST_ASSERT(state.lua_state() != nullptr); + sol::protected_function_result luares; + + // TODO check for api version, make sure luares is always set switch (api_version) { case 4: case 3: - node_function(profile_table, std::cref(node), std::ref(result), std::cref(relations)); + luares = + node_function(profile_table, std::cref(node), std::ref(result), std::cref(relations)); break; case 2: - node_function(profile_table, std::cref(node), std::ref(result)); + luares = node_function(profile_table, std::cref(node), std::ref(result)); break; case 1: case 0: - node_function(std::cref(node), std::ref(result)); + luares = node_function(std::cref(node), std::ref(result)); break; } + + if (!luares.valid()) + handle_lua_error(luares); } void LuaScriptingContext::ProcessWay(const osmium::Way &way, @@ -1233,20 +1267,27 @@ void LuaScriptingContext::ProcessWay(const osmium::Way &way, { BOOST_ASSERT(state.lua_state() != nullptr); + sol::protected_function_result luares; + + // TODO check for api version, make sure luares is always set switch (api_version) { case 4: case 3: - way_function(profile_table, std::cref(way), std::ref(result), std::cref(relations)); + luares = + way_function(profile_table, std::cref(way), std::ref(result), std::cref(relations)); break; case 2: - way_function(profile_table, std::cref(way), std::ref(result)); + luares = way_function(profile_table, std::cref(way), std::ref(result)); break; case 1: case 0: - way_function(std::cref(way), std::ref(result)); + luares = way_function(std::cref(way), std::ref(result)); break; } + + if (!luares.valid()) + handle_lua_error(luares); } } // namespace osrm::extractor diff --git a/src/nodejs/node_osrm.cpp b/src/nodejs/node_osrm.cpp index 1537ab4d5..5d5bb916f 100644 --- a/src/nodejs/node_osrm.cpp +++ b/src/nodejs/node_osrm.cpp @@ -81,6 +81,7 @@ Napi::Object Engine::Init(Napi::Env env, Napi::Object exports) * @param {Number} [options.max_radius_map_matching] Max. radius size supported in map matching query (default: 5). * @param {Number} [options.max_results_nearest] Max. results supported in nearest query (default: unlimited). * @param {Number} [options.max_alternatives] Max. number of alternatives supported in alternative routes query (default: 3). + * @param {Number} [options.default_radius] Default radius for queries (default: unlimited). * * @class OSRM * diff --git a/src/tools/routed.cpp b/src/tools/routed.cpp index 943475052..e8fb36695 100644 --- a/src/tools/routed.cpp +++ b/src/tools/routed.cpp @@ -147,7 +147,10 @@ inline unsigned generateServerProgramOptions(const int argc, "Max. number of alternatives supported in the MLD route query") // ("max-matching-radius", value(&config.max_radius_map_matching)->default_value(-1.0), - "Max. radius size supported in map matching query. Default: unlimited."); + "Max. radius size supported in map matching query. Default: unlimited.") // + ("default-radius", + value>(&config.default_radius), + "Default radius size for queries. Default: unlimited."); // hidden options, will be allowed on command line, but will not be shown to the user boost::program_options::options_description hidden_options("Hidden options"); diff --git a/test/data/profiles/bad_node.lua b/test/data/profiles/bad_node.lua new file mode 100644 index 000000000..74c730587 --- /dev/null +++ b/test/data/profiles/bad_node.lua @@ -0,0 +1,140 @@ +-- copy of testbot with process_node throwing a runtime error + +api_version = 4 + +function setup() + return { + properties = { + continue_straight_at_waypoint = true, + max_speed_for_map_matching = 30/3.6, --km -> m/s + weight_name = 'duration', + process_call_tagless_node = false, + u_turn_penalty = 20, + traffic_light_penalty = 7, -- seconds + use_turn_restrictions = true + }, + + classes = {"motorway", "toll", "TooWords2"}, + + excludable = { + {["motorway"] = true}, + {["toll"] = true}, + {["motorway"] = true, ["toll"] = true} + }, + + default_speed = 24, + speeds = { + primary = 36, + secondary = 18, + tertiary = 12, + steps = 6 + } + } +end + +function process_node (profile, node, result) + if (2 < nil) then + print("2 is less than nil") + end + + -- check if node is a traffic light + -- TODO: a way to set the penalty value +end + +function process_way (profile, way, result) + local highway = way:get_value_by_key("highway") + local toll = way:get_value_by_key("toll") + local name = way:get_value_by_key("name") + local oneway = way:get_value_by_key("oneway") + local route = way:get_value_by_key("route") + local duration = way:get_value_by_key("duration") + local maxspeed = tonumber(way:get_value_by_key ( "maxspeed")) + local maxspeed_forward = tonumber(way:get_value_by_key( "maxspeed:forward")) + local maxspeed_backward = tonumber(way:get_value_by_key( "maxspeed:backward")) + local junction = way:get_value_by_key("junction") + + if name then + result.name = name + end + + result.forward_mode = mode.driving + result.backward_mode = mode.driving + + if duration and durationIsValid(duration) then + result.duration = math.max( 1, parseDuration(duration) ) + result.forward_mode = mode.route + result.backward_mode = mode.route + else + local speed_forw = profile.speeds[highway] or profile.default_speed + local speed_back = speed_forw + + if highway == "river" then + local temp_speed = speed_forw + result.forward_mode = mode.river_down + result.backward_mode = mode.river_up + speed_forw = temp_speed*1.5 + speed_back = temp_speed/1.5 + elseif highway == "steps" then + result.forward_mode = mode.steps_down + result.backward_mode = mode.steps_up + end + + if maxspeed_forward ~= nil and maxspeed_forward > 0 then + speed_forw = maxspeed_forward + else + if maxspeed ~= nil and maxspeed > 0 and speed_forw > maxspeed then + speed_forw = maxspeed + end + end + + if maxspeed_backward ~= nil and maxspeed_backward > 0 then + speed_back = maxspeed_backward + else + if maxspeed ~=nil and maxspeed > 0 and speed_back > maxspeed then + speed_back = maxspeed + end + end + + result.forward_speed = speed_forw + result.backward_speed = speed_back + end + + if oneway == "no" or oneway == "0" or oneway == "false" then + -- nothing to do + elseif oneway == "-1" then + result.forward_mode = mode.inaccessible + elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" then + result.backward_mode = mode.inaccessible + end + + if highway == 'motorway' then + result.forward_classes["motorway"] = true + result.backward_classes["motorway"] = true + end + + if toll == "yes" then + result.forward_classes["toll"] = true + result.backward_classes["toll"] = true + end + + if junction == 'roundabout' then + result.roundabout = true + end +end + +function process_turn (profile, turn) + if turn.is_u_turn then + turn.duration = turn.duration + profile.properties.u_turn_penalty + turn.weight = turn.weight + profile.properties.u_turn_penalty + end + if turn.has_traffic_light then + turn.duration = turn.duration + profile.properties.traffic_light_penalty + end +end + +return { + setup = setup, + process_way = process_way, + process_node = process_node, + process_turn = process_turn +} diff --git a/test/data/profiles/bad_segment.lua b/test/data/profiles/bad_segment.lua new file mode 100644 index 000000000..cbb5bac19 --- /dev/null +++ b/test/data/profiles/bad_segment.lua @@ -0,0 +1,143 @@ +-- A copy of testbot with process_segment throwing a runtime error + +api_version = 4 + +function setup() + return { + properties = { + continue_straight_at_waypoint = true, + max_speed_for_map_matching = 30/3.6, --km -> m/s + weight_name = 'duration', + process_call_tagless_node = false, + u_turn_penalty = 20, + traffic_light_penalty = 7, -- seconds + use_turn_restrictions = true + }, + + classes = {"motorway", "toll", "TooWords2"}, + + excludable = { + {["motorway"] = true}, + {["toll"] = true}, + {["motorway"] = true, ["toll"] = true} + }, + + default_speed = 24, + speeds = { + primary = 36, + secondary = 18, + tertiary = 12, + steps = 6 + } + } +end + +function process_node (profile, node, result) + -- check if node is a traffic light + -- TODO: a way to set the penalty value +end + +function process_way (profile, way, result) + local highway = way:get_value_by_key("highway") + local toll = way:get_value_by_key("toll") + local name = way:get_value_by_key("name") + local oneway = way:get_value_by_key("oneway") + local route = way:get_value_by_key("route") + local duration = way:get_value_by_key("duration") + local maxspeed = tonumber(way:get_value_by_key ( "maxspeed")) + local maxspeed_forward = tonumber(way:get_value_by_key( "maxspeed:forward")) + local maxspeed_backward = tonumber(way:get_value_by_key( "maxspeed:backward")) + local junction = way:get_value_by_key("junction") + + if name then + result.name = name + end + + result.forward_mode = mode.driving + result.backward_mode = mode.driving + + if duration and durationIsValid(duration) then + result.duration = math.max( 1, parseDuration(duration) ) + result.forward_mode = mode.route + result.backward_mode = mode.route + else + local speed_forw = profile.speeds[highway] or profile.default_speed + local speed_back = speed_forw + + if highway == "river" then + local temp_speed = speed_forw + result.forward_mode = mode.river_down + result.backward_mode = mode.river_up + speed_forw = temp_speed*1.5 + speed_back = temp_speed/1.5 + elseif highway == "steps" then + result.forward_mode = mode.steps_down + result.backward_mode = mode.steps_up + end + + if maxspeed_forward ~= nil and maxspeed_forward > 0 then + speed_forw = maxspeed_forward + else + if maxspeed ~= nil and maxspeed > 0 and speed_forw > maxspeed then + speed_forw = maxspeed + end + end + + if maxspeed_backward ~= nil and maxspeed_backward > 0 then + speed_back = maxspeed_backward + else + if maxspeed ~=nil and maxspeed > 0 and speed_back > maxspeed then + speed_back = maxspeed + end + end + + result.forward_speed = speed_forw + result.backward_speed = speed_back + end + + if oneway == "no" or oneway == "0" or oneway == "false" then + -- nothing to do + elseif oneway == "-1" then + result.forward_mode = mode.inaccessible + elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" then + result.backward_mode = mode.inaccessible + end + + if highway == 'motorway' then + result.forward_classes["motorway"] = true + result.backward_classes["motorway"] = true + end + + if toll == "yes" then + result.forward_classes["toll"] = true + result.backward_classes["toll"] = true + end + + if junction == 'roundabout' then + result.roundabout = true + end +end + +function process_turn (profile, turn) + if turn.is_u_turn then + turn.duration = turn.duration + profile.properties.u_turn_penalty + turn.weight = turn.weight + profile.properties.u_turn_penalty + end + if turn.has_traffic_light then + turn.duration = turn.duration + profile.properties.traffic_light_penalty + end +end + +function process_segment (profile, segment) + if (2 < nil) then + print("2 is less than nil") + end +end + +return { + setup = setup, + process_way = process_way, + process_node = process_node, + process_turn = process_turn, + process_segment = process_segment +} diff --git a/test/data/profiles/bad_setup.lua b/test/data/profiles/bad_setup.lua new file mode 100644 index 000000000..72e32cc40 --- /dev/null +++ b/test/data/profiles/bad_setup.lua @@ -0,0 +1,140 @@ +-- Copy of testbot profile, with setup throwing a runtime error + +api_version = 4 + +function setup() + if (2 < nil) then -- arithmetic with nil should error + return {} + end + + return { + properties = { + continue_straight_at_waypoint = true, + max_speed_for_map_matching = 30/3.6, --km -> m/s + weight_name = 'duration', + process_call_tagless_node = false, + u_turn_penalty = 20, + traffic_light_penalty = 7, -- seconds + use_turn_restrictions = true + }, + + classes = {"motorway", "toll", "TooWords2"}, + + excludable = { + {["motorway"] = true}, + {["toll"] = true}, + {["motorway"] = true, ["toll"] = true} + }, + + default_speed = 24, + speeds = { + primary = 36, + secondary = 18, + tertiary = 12, + steps = 6 + } + } +end + +function process_node (profile, node, result) + -- check if node is a traffic light + -- TODO: a way to set the penalty value +end + +function process_way (profile, way, result) + local highway = way:get_value_by_key("highway") + local toll = way:get_value_by_key("toll") + local name = way:get_value_by_key("name") + local oneway = way:get_value_by_key("oneway") + local route = way:get_value_by_key("route") + local duration = way:get_value_by_key("duration") + local maxspeed = tonumber(way:get_value_by_key ( "maxspeed")) + local maxspeed_forward = tonumber(way:get_value_by_key( "maxspeed:forward")) + local maxspeed_backward = tonumber(way:get_value_by_key( "maxspeed:backward")) + local junction = way:get_value_by_key("junction") + + if name then + result.name = name + end + + result.forward_mode = mode.driving + result.backward_mode = mode.driving + + if duration and durationIsValid(duration) then + result.duration = math.max( 1, parseDuration(duration) ) + result.forward_mode = mode.route + result.backward_mode = mode.route + else + local speed_forw = profile.speeds[highway] or profile.default_speed + local speed_back = speed_forw + + if highway == "river" then + local temp_speed = speed_forw + result.forward_mode = mode.river_down + result.backward_mode = mode.river_up + speed_forw = temp_speed*1.5 + speed_back = temp_speed/1.5 + elseif highway == "steps" then + result.forward_mode = mode.steps_down + result.backward_mode = mode.steps_up + end + + if maxspeed_forward ~= nil and maxspeed_forward > 0 then + speed_forw = maxspeed_forward + else + if maxspeed ~= nil and maxspeed > 0 and speed_forw > maxspeed then + speed_forw = maxspeed + end + end + + if maxspeed_backward ~= nil and maxspeed_backward > 0 then + speed_back = maxspeed_backward + else + if maxspeed ~=nil and maxspeed > 0 and speed_back > maxspeed then + speed_back = maxspeed + end + end + + result.forward_speed = speed_forw + result.backward_speed = speed_back + end + + if oneway == "no" or oneway == "0" or oneway == "false" then + -- nothing to do + elseif oneway == "-1" then + result.forward_mode = mode.inaccessible + elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" then + result.backward_mode = mode.inaccessible + end + + if highway == 'motorway' then + result.forward_classes["motorway"] = true + result.backward_classes["motorway"] = true + end + + if toll == "yes" then + result.forward_classes["toll"] = true + result.backward_classes["toll"] = true + end + + if junction == 'roundabout' then + result.roundabout = true + end +end + +function process_turn (profile, turn) + if turn.is_u_turn then + turn.duration = turn.duration + profile.properties.u_turn_penalty + turn.weight = turn.weight + profile.properties.u_turn_penalty + end + if turn.has_traffic_light then + turn.duration = turn.duration + profile.properties.traffic_light_penalty + end +end + +return { + setup = setup, + process_way = process_way, + process_node = process_node, + process_turn = process_turn +} diff --git a/test/data/profiles/bad_turn.lua b/test/data/profiles/bad_turn.lua new file mode 100644 index 000000000..0fecb7d2d --- /dev/null +++ b/test/data/profiles/bad_turn.lua @@ -0,0 +1,140 @@ +-- a copy of testbot with process_turn throwing an error + +api_version = 4 + +function setup() + return { + properties = { + continue_straight_at_waypoint = true, + max_speed_for_map_matching = 30/3.6, --km -> m/s + weight_name = 'duration', + process_call_tagless_node = false, + u_turn_penalty = 20, + traffic_light_penalty = 7, -- seconds + use_turn_restrictions = true + }, + + classes = {"motorway", "toll", "TooWords2"}, + + excludable = { + {["motorway"] = true}, + {["toll"] = true}, + {["motorway"] = true, ["toll"] = true} + }, + + default_speed = 24, + speeds = { + primary = 36, + secondary = 18, + tertiary = 12, + steps = 6 + } + } +end + +function process_node (profile, node, result) + -- check if node is a traffic light + -- TODO: a way to set the penalty value +end + +function process_way (profile, way, result) + local highway = way:get_value_by_key("highway") + local toll = way:get_value_by_key("toll") + local name = way:get_value_by_key("name") + local oneway = way:get_value_by_key("oneway") + local route = way:get_value_by_key("route") + local duration = way:get_value_by_key("duration") + local maxspeed = tonumber(way:get_value_by_key ( "maxspeed")) + local maxspeed_forward = tonumber(way:get_value_by_key( "maxspeed:forward")) + local maxspeed_backward = tonumber(way:get_value_by_key( "maxspeed:backward")) + local junction = way:get_value_by_key("junction") + + if name then + result.name = name + end + + result.forward_mode = mode.driving + result.backward_mode = mode.driving + + if duration and durationIsValid(duration) then + result.duration = math.max( 1, parseDuration(duration) ) + result.forward_mode = mode.route + result.backward_mode = mode.route + else + local speed_forw = profile.speeds[highway] or profile.default_speed + local speed_back = speed_forw + + if highway == "river" then + local temp_speed = speed_forw + result.forward_mode = mode.river_down + result.backward_mode = mode.river_up + speed_forw = temp_speed*1.5 + speed_back = temp_speed/1.5 + elseif highway == "steps" then + result.forward_mode = mode.steps_down + result.backward_mode = mode.steps_up + end + + if maxspeed_forward ~= nil and maxspeed_forward > 0 then + speed_forw = maxspeed_forward + else + if maxspeed ~= nil and maxspeed > 0 and speed_forw > maxspeed then + speed_forw = maxspeed + end + end + + if maxspeed_backward ~= nil and maxspeed_backward > 0 then + speed_back = maxspeed_backward + else + if maxspeed ~=nil and maxspeed > 0 and speed_back > maxspeed then + speed_back = maxspeed + end + end + + result.forward_speed = speed_forw + result.backward_speed = speed_back + end + + if oneway == "no" or oneway == "0" or oneway == "false" then + -- nothing to do + elseif oneway == "-1" then + result.forward_mode = mode.inaccessible + elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" then + result.backward_mode = mode.inaccessible + end + + if highway == 'motorway' then + result.forward_classes["motorway"] = true + result.backward_classes["motorway"] = true + end + + if toll == "yes" then + result.forward_classes["toll"] = true + result.backward_classes["toll"] = true + end + + if junction == 'roundabout' then + result.roundabout = true + end +end + +function process_turn (profile, turn) + if (2 < nil) then + print("2 is less than nil") + end + + if turn.is_u_turn then + turn.duration = turn.duration + profile.properties.u_turn_penalty + turn.weight = turn.weight + profile.properties.u_turn_penalty + end + if turn.has_traffic_light then + turn.duration = turn.duration + profile.properties.traffic_light_penalty + end +end + +return { + setup = setup, + process_way = process_way, + process_node = process_node, + process_turn = process_turn +} diff --git a/test/data/profiles/bad_way.lua b/test/data/profiles/bad_way.lua new file mode 100644 index 000000000..cb0a3eb72 --- /dev/null +++ b/test/data/profiles/bad_way.lua @@ -0,0 +1,140 @@ +-- copy of testbot with process_way throwing a runtime error + +api_version = 4 + +function setup() + return { + properties = { + continue_straight_at_waypoint = true, + max_speed_for_map_matching = 30/3.6, --km -> m/s + weight_name = 'duration', + process_call_tagless_node = false, + u_turn_penalty = 20, + traffic_light_penalty = 7, -- seconds + use_turn_restrictions = true + }, + + classes = {"motorway", "toll", "TooWords2"}, + + excludable = { + {["motorway"] = true}, + {["toll"] = true}, + {["motorway"] = true, ["toll"] = true} + }, + + default_speed = 24, + speeds = { + primary = 36, + secondary = 18, + tertiary = 12, + steps = 6 + } + } +end + +function process_node (profile, node, result) + -- check if node is a traffic light + -- TODO: a way to set the penalty value +end + +function process_way (profile, way, result) + if (2 < nil) then + print("2 less than nil") + end + + local highway = way:get_value_by_key("highway") + local toll = way:get_value_by_key("toll") + local name = way:get_value_by_key("name") + local oneway = way:get_value_by_key("oneway") + local route = way:get_value_by_key("route") + local duration = way:get_value_by_key("duration") + local maxspeed = tonumber(way:get_value_by_key ( "maxspeed")) + local maxspeed_forward = tonumber(way:get_value_by_key( "maxspeed:forward")) + local maxspeed_backward = tonumber(way:get_value_by_key( "maxspeed:backward")) + local junction = way:get_value_by_key("junction") + + if name then + result.name = name + end + + result.forward_mode = mode.driving + result.backward_mode = mode.driving + + if duration and durationIsValid(duration) then + result.duration = math.max( 1, parseDuration(duration) ) + result.forward_mode = mode.route + result.backward_mode = mode.route + else + local speed_forw = profile.speeds[highway] or profile.default_speed + local speed_back = speed_forw + + if highway == "river" then + local temp_speed = speed_forw + result.forward_mode = mode.river_down + result.backward_mode = mode.river_up + speed_forw = temp_speed*1.5 + speed_back = temp_speed/1.5 + elseif highway == "steps" then + result.forward_mode = mode.steps_down + result.backward_mode = mode.steps_up + end + + if maxspeed_forward ~= nil and maxspeed_forward > 0 then + speed_forw = maxspeed_forward + else + if maxspeed ~= nil and maxspeed > 0 and speed_forw > maxspeed then + speed_forw = maxspeed + end + end + + if maxspeed_backward ~= nil and maxspeed_backward > 0 then + speed_back = maxspeed_backward + else + if maxspeed ~=nil and maxspeed > 0 and speed_back > maxspeed then + speed_back = maxspeed + end + end + + result.forward_speed = speed_forw + result.backward_speed = speed_back + end + + if oneway == "no" or oneway == "0" or oneway == "false" then + -- nothing to do + elseif oneway == "-1" then + result.forward_mode = mode.inaccessible + elseif oneway == "yes" or oneway == "1" or oneway == "true" or junction == "roundabout" then + result.backward_mode = mode.inaccessible + end + + if highway == 'motorway' then + result.forward_classes["motorway"] = true + result.backward_classes["motorway"] = true + end + + if toll == "yes" then + result.forward_classes["toll"] = true + result.backward_classes["toll"] = true + end + + if junction == 'roundabout' then + result.roundabout = true + end +end + +function process_turn (profile, turn) + if turn.is_u_turn then + turn.duration = turn.duration + profile.properties.u_turn_penalty + turn.weight = turn.weight + profile.properties.u_turn_penalty + end + if turn.has_traffic_light then + turn.duration = turn.duration + profile.properties.traffic_light_penalty + end +end + +return { + setup = setup, + process_way = process_way, + process_node = process_node, + process_turn = process_turn +} diff --git a/test/nodejs/index.js b/test/nodejs/index.js index f08356706..dd6f44ea4 100644 --- a/test/nodejs/index.js +++ b/test/nodejs/index.js @@ -112,6 +112,18 @@ test('constructor: throws if dataset_name is not a string', function(assert) { assert.throws(function() { new OSRM({dataset_name: "unsued_name___", shared_memory: true}); }, /Could not find shared memory region/, 'Does not accept wrong name'); }); +test('constructor: takes a default_radius argument', function(assert) { + assert.plan(1); + var osrm = new OSRM({algorithm: 'MLD', path: monaco_mld_path, default_radius: 1}); + assert.ok(osrm); +}); + +test('constructor: throws if default_radius is not a number', function(assert) { + assert.plan(2); + assert.throws(function() { new OSRM({algorithm: 'MLD', path: monaco_mld_path, default_radius: 'abc'}); }, /default_radius must be an integral number/, 'Does not accept string'); + assert.ok(new OSRM({algorithm: 'MLD', path: monaco_mld_path, default_radius: 1}), 'Does accept number'); +}); + test('constructor: parses custom limits', function(assert) { assert.plan(1); var osrm = new OSRM({ diff --git a/unit_tests/library/extract.cpp b/unit_tests/library/extract.cpp index aa18c7552..342d672f7 100644 --- a/unit_tests/library/extract.cpp +++ b/unit_tests/library/extract.cpp @@ -1,10 +1,32 @@ #include +#include "osrm/exception.hpp" #include "osrm/extractor.hpp" #include "osrm/extractor_config.hpp" +#include #include +// utility class to redirect stderr so we can test it +// inspired by https://stackoverflow.com/questions/5405016 +class redirect_stderr +{ + // constructor: accept a pointer to a buffer where stderr will be redirected + public: + redirect_stderr(std::streambuf *buf) + // store the original buffer for later (original buffer returned by rdbuf) + : old(std::cerr.rdbuf(buf)) + { + } + + // destructor: restore the original cerr, regardless of how this class gets destroyed + ~redirect_stderr() { std::cerr.rdbuf(old); } + + // place to store the buffer + private: + std::streambuf *old; +}; + BOOST_AUTO_TEST_SUITE(library_extract) BOOST_AUTO_TEST_CASE(test_extract_with_invalid_config) @@ -26,4 +48,114 @@ BOOST_AUTO_TEST_CASE(test_extract_with_valid_config) BOOST_CHECK_NO_THROW(osrm::extract(config)); } +BOOST_AUTO_TEST_CASE(test_setup_runtime_error) +{ + osrm::ExtractorConfig config; + config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf"; + config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf"); + config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_setup.lua"; + config.small_component_size = 1000; + config.requested_num_threads = std::thread::hardware_concurrency(); + + std::stringstream output; + + { + redirect_stderr redir(output.rdbuf()); + BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception); + } + + // We just look for the line number, file name, and error message. This avoids portability + // issues since the output contains the full path to the file, which may change between systems + BOOST_CHECK(boost::algorithm::contains(output.str(), + "bad_setup.lua:6: attempt to compare number with nil")); +} + +BOOST_AUTO_TEST_CASE(test_way_runtime_error) +{ + osrm::ExtractorConfig config; + config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf"; + config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf"); + config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_way.lua"; + config.small_component_size = 1000; + config.requested_num_threads = std::thread::hardware_concurrency(); + + std::stringstream output; + + { + redirect_stderr redir(output.rdbuf()); + BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception); + } + + // We just look for the line number, file name, and error message. This avoids portability + // issues since the output contains the full path to the file, which may change between systems + BOOST_CHECK(boost::algorithm::contains(output.str(), + "bad_way.lua:41: attempt to compare number with nil")); +} + +BOOST_AUTO_TEST_CASE(test_node_runtime_error) +{ + osrm::ExtractorConfig config; + config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf"; + config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf"); + config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_node.lua"; + config.small_component_size = 1000; + config.requested_num_threads = std::thread::hardware_concurrency(); + + std::stringstream output; + + { + redirect_stderr redir(output.rdbuf()); + BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception); + } + + // We just look for the line number, file name, and error message. This avoids portability + // issues since the output contains the full path to the file, which may change between systems + BOOST_CHECK(boost::algorithm::contains(output.str(), + "bad_node.lua:36: attempt to compare number with nil")); +} + +BOOST_AUTO_TEST_CASE(test_segment_runtime_error) +{ + osrm::ExtractorConfig config; + config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf"; + config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf"); + config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_segment.lua"; + config.small_component_size = 1000; + config.requested_num_threads = std::thread::hardware_concurrency(); + + std::stringstream output; + + { + redirect_stderr redir(output.rdbuf()); + BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception); + } + + // We just look for the line number, file name, and error message. This avoids portability + // issues since the output contains the full path to the file, which may change between systems + BOOST_CHECK(boost::algorithm::contains( + output.str(), "bad_segment.lua:132: attempt to compare number with nil")); +} + +BOOST_AUTO_TEST_CASE(test_turn_runtime_error) +{ + osrm::ExtractorConfig config; + config.input_path = OSRM_TEST_DATA_DIR "/monaco.osm.pbf"; + config.UseDefaultOutputNames(OSRM_TEST_DATA_DIR "/monaco.osm.pbf"); + config.profile_path = OSRM_TEST_DATA_DIR "/profiles/bad_turn.lua"; + config.small_component_size = 1000; + config.requested_num_threads = std::thread::hardware_concurrency(); + + std::stringstream output; + + { + redirect_stderr redir(output.rdbuf()); + BOOST_CHECK_THROW(osrm::extract(config), osrm::util::exception); + } + + // We just look for the line number, file name, and error message. This avoids portability + // issues since the output contains the full path to the file, which may change between systems + BOOST_CHECK(boost::algorithm::contains(output.str(), + "bad_turn.lua:122: attempt to compare number with nil")); +} + BOOST_AUTO_TEST_SUITE_END()