#include "integration_tests/routing_test_tools.hpp" #include "testing/testing.hpp" #include "map/feature_vec_model.hpp" #include "geometry/distance_on_sphere.hpp" #include "geometry/latlon.hpp" #include "routing/online_absent_fetcher.hpp" #include "routing/online_cross_fetcher.hpp" #include "routing/road_graph_router.hpp" #include "routing/route.hpp" #include "routing/router_delegate.hpp" #include "search/search_engine.hpp" #include "indexer/index.hpp" #include "platform/local_country_file.hpp" #include "platform/local_country_file_utils.hpp" #include "platform/platform.hpp" #include "platform/preferred_languages.hpp" #include "geometry/distance_on_sphere.hpp" #include "search/search_engine.hpp" #include "search/search_query_factory.hpp" #include using namespace routing; using platform::LocalCountryFile; namespace { void ChangeMaxNumberOfOpenFiles(size_t n) { struct rlimit rlp; getrlimit(RLIMIT_NOFILE, &rlp); rlp.rlim_cur = n; setrlimit(RLIMIT_NOFILE, &rlp); } } namespace integration { shared_ptr CreateFeaturesFetcher(vector const & localFiles) { size_t const maxOpenFileNumber = 1024; ChangeMaxNumberOfOpenFiles(maxOpenFileNumber); shared_ptr featuresFetcher(new model::FeaturesFetcher); featuresFetcher->InitClassificator(); for (LocalCountryFile const & localFile : localFiles) { auto p = featuresFetcher->RegisterMap(localFile); if (p.second != MwmSet::RegResult::Success) { ASSERT(false, ("Can't register", localFile)); return nullptr; } } return featuresFetcher; } shared_ptr CreateSearchEngine(shared_ptr featuresFetcher) { ASSERT(featuresFetcher, ()); search::Engine::IndexType const & index = featuresFetcher->GetIndex(); Platform const & pl = GetPlatform(); try { shared_ptr searchEngine(new search::Engine( &index, pl.GetReader(SEARCH_CATEGORIES_FILE_NAME), pl.GetReader(PACKED_POLYGONS_FILE), pl.GetReader(COUNTRIES_FILE), languages::GetCurrentOrig(), make_unique())); return searchEngine; } catch (RootException const &e) { LOG(LCRITICAL, ("Error:", e.what(), " while creating searchEngine.")); return nullptr; } } shared_ptr CreateOsrmRouter(Index & index, search::Engine & searchEngine) { shared_ptr osrmRouter(new OsrmRouter( &index, [&searchEngine](m2::PointD const & pt) { return searchEngine.GetCountryFile(pt); } )); return osrmRouter; } shared_ptr CreatePedestrianRouter(Index & index, search::Engine & searchEngine) { auto countryFileGetter = [&searchEngine](m2::PointD const & pt) { return searchEngine.GetCountryFile(pt); }; unique_ptr router = CreatePedestrianAStarBidirectionalRouter(index, countryFileGetter); return shared_ptr(move(router)); } class IRouterComponents { public: virtual IRouter * GetRouter() const = 0; virtual search::Engine * GetSearchEngine() const = 0; virtual ~IRouterComponents() = default; }; class OsrmRouterComponents : public IRouterComponents { public: OsrmRouterComponents(vector const & localFiles) : m_featuresFetcher(CreateFeaturesFetcher(localFiles)), m_searchEngine(CreateSearchEngine(m_featuresFetcher)), m_osrmRouter(CreateOsrmRouter(m_featuresFetcher->GetIndex(), *m_searchEngine.get())) { } IRouter * GetRouter() const override { return m_osrmRouter.get(); } search::Engine * GetSearchEngine() const override { return m_searchEngine.get(); } private: shared_ptr m_featuresFetcher; shared_ptr m_searchEngine; shared_ptr m_osrmRouter; }; class PedestrianRouterComponents : public IRouterComponents { public: PedestrianRouterComponents(vector const & localFiles) : m_featuresFetcher(CreateFeaturesFetcher(localFiles)), m_searchEngine(CreateSearchEngine(m_featuresFetcher)), m_router(CreatePedestrianRouter(m_featuresFetcher->GetIndex(), *m_searchEngine)) { } IRouter * GetRouter() const override { return m_router.get(); } search::Engine * GetSearchEngine() const override { return m_searchEngine.get(); } private: shared_ptr m_featuresFetcher; shared_ptr m_searchEngine; shared_ptr m_router; }; template shared_ptr CreateAllMapsComponents() { // Setting stored paths from testingmain.cpp Platform & pl = GetPlatform(); CommandLineOptions const & options = GetTestingOptions(); if (options.m_dataPath) pl.SetWritableDirForTests(options.m_dataPath); if (options.m_resourcePath) pl.SetResourceDir(options.m_resourcePath); vector localFiles; platform::FindAllLocalMaps(localFiles); for (auto & file : localFiles) file.SyncWithDisk(); ASSERT(!localFiles.empty(), ()); return shared_ptr(new TRouterComponents(localFiles)); } shared_ptr GetOsrmComponents(vector const & localFiles) { return shared_ptr(new OsrmRouterComponents(localFiles)); } IRouterComponents & GetOsrmComponents() { static shared_ptr const inst = CreateAllMapsComponents(); ASSERT(inst, ()); return *inst; } shared_ptr GetPedestrianComponents(vector const & localFiles) { return shared_ptr(new PedestrianRouterComponents(localFiles)); } IRouterComponents & GetPedestrianComponents() { static shared_ptr const inst = CreateAllMapsComponents(); ASSERT(inst, ()); return *inst; } TRouteResult CalculateRoute(IRouterComponents const & routerComponents, m2::PointD const & startPoint, m2::PointD const & startDirection, m2::PointD const & finalPoint) { RouterDelegate delegate; IRouter * router = routerComponents.GetRouter(); ASSERT(router, ()); shared_ptr route(new Route("mapsme")); IRouter::ResultCode result = router->CalculateRoute(startPoint, startDirection, finalPoint, delegate, *route.get()); ASSERT(route, ()); return TRouteResult(route, result); } void TestTurnCount(routing::Route const & route, uint32_t expectedTurnCount) { TEST_EQUAL(route.GetTurnsGeometry().size(), expectedTurnCount, ()); } void TestRouteLength(Route const & route, double expectedRouteMeters, double relativeError) { double const delta = expectedRouteMeters * relativeError; double const routeMeters = route.GetTotalDistanceMeters(); TEST(my::AlmostEqualAbs(routeMeters, expectedRouteMeters, delta), ("Route time test failed. Expected:", expectedRouteMeters, "have:", routeMeters, "delta:", delta)); } void TestRouteTime(Route const & route, double expectedRouteSeconds, double relativeError) { double const delta = expectedRouteSeconds * relativeError; double const routeSeconds = route.GetTotalTimeSec(); TEST(my::AlmostEqualAbs(routeSeconds, expectedRouteSeconds, delta), ("Route time test failed. Expected:", expectedRouteSeconds, "have:", routeSeconds, "delta:", delta)); } void CalculateRouteAndTestRouteLength(IRouterComponents const & routerComponents, m2::PointD const & startPoint, m2::PointD const & startDirection, m2::PointD const & finalPoint, double expectedRouteMeters, double relativeError) { TRouteResult routeResult = CalculateRoute(routerComponents, startPoint, startDirection, finalPoint); IRouter::ResultCode const result = routeResult.second; TEST_EQUAL(result, IRouter::NoError, ()); TestRouteLength(*routeResult.first, expectedRouteMeters, relativeError); } const TestTurn & TestTurn::TestValid() const { TEST(m_isValid, ()); return *this; } const TestTurn & TestTurn::TestNotValid() const { TEST(!m_isValid, ()); return *this; } const TestTurn & TestTurn::TestPoint(m2::PointD const & expectedPoint, double inaccuracyMeters) const { double const distanceMeters = ms::DistanceOnEarth(expectedPoint.y, expectedPoint.x, m_point.y, m_point.x); TEST_LESS(distanceMeters, inaccuracyMeters, ()); return *this; } const TestTurn & TestTurn::TestDirection(routing::turns::TurnDirection expectedDirection) const { TEST_EQUAL(m_direction, expectedDirection, ()); return *this; } const TestTurn & TestTurn::TestOneOfDirections( set const & expectedDirections) const { TEST(expectedDirections.find(m_direction) != expectedDirections.cend(), ()); return *this; } const TestTurn & TestTurn::TestRoundAboutExitNum(uint32_t expectedRoundAboutExitNum) const { TEST_EQUAL(m_roundAboutExitNum, expectedRoundAboutExitNum, ()); return *this; } TestTurn GetNthTurn(routing::Route const & route, uint32_t turnNumber) { turns::TTurnsGeom const & turnsGeom = route.GetTurnsGeometry(); if (turnNumber >= turnsGeom.size()) return TestTurn(); Route::TTurns const & turns = route.GetTurns(); if (turnNumber >= turns.size()) return TestTurn(); turns::TurnGeom const & turnGeom = turnsGeom[turnNumber]; ASSERT_LESS(turnGeom.m_turnIndex, turnGeom.m_points.size(), ()); TurnItem const & turn = turns[turnNumber]; return TestTurn(turnGeom.m_points[turnGeom.m_turnIndex], turn.m_turn, turn.m_exitNum); } TestTurn GetTurnByPoint(routing::Route const & route, m2::PointD const & approximateTurnPoint, double inaccuracyMeters) { turns::TTurnsGeom const & turnsGeom = route.GetTurnsGeometry(); Route::TTurns const & turns = route.GetTurns(); ASSERT_EQUAL(turnsGeom.size() + 1, turns.size(), ()); for (int i = 0; i != turnsGeom.size(); ++i) { turns::TurnGeom const & turnGeom = turnsGeom[i]; ASSERT_LESS(turnGeom.m_turnIndex, turnGeom.m_points.size(), ()); m2::PointD const turnPoint = turnGeom.m_points[turnGeom.m_turnIndex]; if (ms::DistanceOnEarth(turnPoint.y, turnPoint.x, approximateTurnPoint.y, approximateTurnPoint.x) <= inaccuracyMeters) { TurnItem const & turn = turns[i]; return TestTurn(turnPoint, turn.m_turn, turn.m_exitNum); } } return TestTurn(); } void TestOnlineFetcher(ms::LatLon const & startPoint, ms::LatLon const & finalPoint, vector const & expected, IRouterComponents & routerComponents) { auto countryFileGetter = [&routerComponents](m2::PointD const & p) -> string { return routerComponents.GetSearchEngine()->GetCountryFile(p); }; auto localFileGetter = [&routerComponents](string const & countryFile) -> shared_ptr { //Always returns empty LocalFile return make_shared(); }; routing::OnlineAbsentCountriesFetcher fetcher(countryFileGetter, localFileGetter); fetcher.GenerateRequest(MercatorBounds::FromLatLon(startPoint), MercatorBounds::FromLatLon(finalPoint)); vector absent; fetcher.GetAbsentCountries(absent); if (expected.size() < 2) { // Single MWM case. Do not use online routing. TEST(absent.empty(), ()); return; } TEST_EQUAL(absent.size(), expected.size(), ()); for (string const & name : expected) { TEST(find(absent.begin(), absent.end(), name) != absent.end(), ("Can't find ", name)); } } void TestOnlineCrosses(ms::LatLon const & startPoint, ms::LatLon const & finalPoint, vector const & expected, IRouterComponents & routerComponents) { routing::OnlineCrossFetcher fetcher(OSRM_ONLINE_SERVER_URL, startPoint, finalPoint); fetcher.Do(); vector const & points = fetcher.GetMwmPoints(); TEST_EQUAL(points.size(), expected.size(), ()); for (m2::PointD const & point : points) { string const mwmName = routerComponents.GetSearchEngine()->GetCountryFile(point); TEST(find(expected.begin(), expected.end(), mwmName) != expected.end(), ("Can't find ", mwmName)); } TestOnlineFetcher(startPoint, finalPoint, expected, routerComponents); } }