[routing] Bugfixing in infrastructure of routing session state tests adding a test on changing states.

This commit is contained in:
Vladimir Byko-Ianko 2020-01-20 16:32:54 +03:00 committed by gmoryes
parent 0f8ab84b7f
commit 02250fd501

View file

@ -20,13 +20,23 @@
#include <string>
#include <vector>
namespace routing
namespace
{
using namespace routing;
using namespace std;
using chrono::seconds;
using chrono::steady_clock;
vector<m2::PointD> kTestRoute = {{0., 1.}, {0., 2.}, {0., 3.}, {0., 4.}};
vector<Segment> const kTestSegments({{0, 0, 0, true}, {0, 0, 1, true}, {0, 0, 2, true}});
Route::TTurns const kTestTurns = {turns::TurnItem(3, turns::CarDirection::ReachedYourDestination)};
Route::TTimes const kTestTimes({Route::TTimeItem(1, 5), Route::TTimeItem(2, 10),
Route::TTimeItem(3, 15)});
auto const kRouteBuildingMaxDuration = seconds(30);
void FillSubroutesInfo(Route & route);
// Simple router. It returns route given to him on creation.
class DummyRouter : public IRouter
{
@ -53,14 +63,34 @@ public:
}
};
static vector<m2::PointD> kTestRoute = {{0., 1.}, {0., 2.}, {0., 3.}, {0., 4.}};
static vector<Segment> const kTestSegments({{0, 0, 0, true}, {0, 0, 1, true}, {0, 0, 2, true}});
static Route::TTurns const kTestTurns = {
turns::TurnItem(3, turns::CarDirection::ReachedYourDestination)};
static Route::TTimes const kTestTimes({Route::TTimeItem(1, 5), Route::TTimeItem(2, 10),
Route::TTimeItem(3, 15)});
// Router which every next call of CalculateRoute() method return different return codes.
class ReturnCodesRouter : public IRouter
{
public:
ReturnCodesRouter(initializer_list<RouterResultCode> const & returnCodes,
vector<m2::PointD> const & route)
: m_returnCodes(returnCodes), m_route(route)
{
}
static auto kRouteBuildingMaxDuration = seconds(30);
// IRouter overrides:
string GetName() const override { return "return codes router"; }
void ClearState() override {}
RouterResultCode CalculateRoute(Checkpoints const & /* checkpoints */,
m2::PointD const & /* startDirection */, bool /* adjust */,
RouterDelegate const & /* delegate */, Route & route) override
{
TEST_LESS(m_returnCodesIdx, m_returnCodes.size(), ());
route = Route(GetName(), m_route.begin(), m_route.end(), 0 /* route id */);
FillSubroutesInfo(route);
return m_returnCodes[m_returnCodesIdx++];
}
private:
vector<RouterResultCode> m_returnCodes;
vector<m2::PointD> m_route;
size_t m_returnCodesIdx = 0;
};
class TimedSignal
{
@ -97,12 +127,34 @@ public:
SessionStateTest(initializer_list<SessionState> expectedStates, RoutingSession & routingSession)
: m_expectedStates(expectedStates), m_session(routingSession)
{
m_session.SetChangeSessionStateCallback([this](SessionState previous, SessionState current) {
TestChangeSessionStateCallbackCall(previous, current);
for (size_t i = 1; i < expectedStates.size(); ++i)
{
// Note. Change session state callback is called only if the state is change.
if (m_expectedStates[i - 1] != m_expectedStates[i])
++m_expectedNumberOfStateChanging;
}
TimedSignal timedSignal;
GetPlatform().RunTask(Platform::Thread::Gui, [this, &timedSignal]() {
m_session.SetChangeSessionStateCallback([this](SessionState previous, SessionState current) {
TestChangeSessionStateCallbackCall(previous, current);
});
timedSignal.Signal();
});
TEST(timedSignal.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration), ("Callback is not set."));
}
~SessionStateTest() { m_session.SetChangeSessionStateCallback(nullptr); }
~SessionStateTest()
{
TEST_EQUAL(m_numberOfTestCalls, m_expectedNumberOfStateChanging,
("Wrong number of calls of SetState() callback.", m_expectedStates));
TimedSignal timedSignal;
GetPlatform().RunTask(Platform::Thread::Gui, [this, &timedSignal]() {
m_session.SetChangeSessionStateCallback(nullptr);
timedSignal.Signal();
});
TEST(timedSignal.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration), ("Callback is not set."));
}
private:
void TestChangeSessionStateCallbackCall(SessionState previous, SessionState current)
@ -117,6 +169,7 @@ private:
size_t m_numberOfTestCalls = 0;
vector<SessionState> const m_expectedStates;
size_t m_expectedNumberOfStateChanging = 0;
RoutingSession & m_session;
};
@ -134,6 +187,36 @@ void FillSubroutesInfo(Route & route)
{Route::SubrouteAttrs(junctions.front(), junctions.back(), 0, kTestSegments.size())}));
}
void TestMovingByUpdatingLat(SessionStateTest const & sessionState, vector<double> const & lats,
location::GpsInfo const & info, RoutingSession & session)
{
location::GpsInfo uptInfo(info);
TimedSignal signal;
GetPlatform().RunTask(Platform::Thread::Gui, [&session, &signal, &lats, &uptInfo]() {
for (auto const lat : lats)
{
uptInfo.m_latitude = lat;
session.OnLocationPositionChanged(uptInfo);
}
signal.Signal();
});
TEST(signal.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration),
("Along route moving timeout."));
}
void TestLeavingRoute(RoutingSession & session, location::GpsInfo const & info)
{
vector<double> const latitudes = {0.0, -0.001, -0.002, -0.003, -0.004, -0.005,
-0.006, -0.007, -0.008, -0.009, -0.01, -0.011};
SessionStateTest sessionStateTest({SessionState::OnRoute, SessionState::RouteNeedRebuild},
session);
TestMovingByUpdatingLat(sessionStateTest, latitudes, info, session);
}
} // namespace
namespace routing
{
UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestRouteBuilding)
{
// Multithreading synchronization note. |counter| and |session| are constructed on the main thread,
@ -170,7 +253,6 @@ UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestRouteBuilding)
UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestRouteRebuildingMovingAway)
{
location::GpsInfo info;
FrozenDataSource dataSource;
size_t counter = 0;
TimedSignal alongTimedSignal;
@ -194,40 +276,40 @@ UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestRouteRebuildingMovingA
TEST(alongTimedSignal.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration), ("Route was not built."));
TEST_EQUAL(counter, 1, ());
TimedSignal oppositeTimedSignal;
GetPlatform().RunTask(Platform::Thread::Gui, [&oppositeTimedSignal, &info, this]() {
info.m_horizontalAccuracy = 0.01;
info.m_verticalAccuracy = 0.01;
info.m_longitude = 0.;
info.m_latitude = 1.;
SessionState code = SessionState::NoValidRoute;
{
SessionStateTest sessionStateTest(
{SessionState::RouteNotStarted, SessionState::OnRoute, SessionState::RouteBuilding,
SessionState::RouteNotStarted},
*m_session);
TimedSignal oppositeTimedSignal;
GetPlatform().RunTask(Platform::Thread::Gui, [&oppositeTimedSignal, &info, this]() {
info.m_horizontalAccuracy = 0.01;
info.m_verticalAccuracy = 0.01;
info.m_longitude = 0.;
info.m_latitude = 1.;
SessionState code = SessionState::NoValidRoute;
{
SessionStateTest sessionStateTest({SessionState::RouteNotStarted, SessionState::OnRoute},
*m_session);
while (info.m_latitude < kTestRoute.back().y)
{
code = m_session->OnLocationPositionChanged(info);
TEST_EQUAL(code, SessionState::OnRoute, ());
info.m_latitude += 0.01;
while (info.m_latitude < kTestRoute.back().y)
{
code = m_session->OnLocationPositionChanged(info);
TEST_EQUAL(code, SessionState::OnRoute, ());
info.m_latitude += 0.01;
}
}
}
// Rebuild route and go in opposite direction. So initiate a route rebuilding flag.
m_session->SetRoutingCallbacks(
[&oppositeTimedSignal](Route const &, RouterResultCode) { oppositeTimedSignal.Signal(); },
nullptr /* rebuildReadyCallback */, nullptr /* needMoreMapsCallback */,
nullptr /* removeRouteCallback */);
{
SessionStateTest sessionStateTest(
{SessionState::OnRoute, SessionState::RouteBuilding},
*m_session);
m_session->BuildRoute(Checkpoints(kTestRoute.front(), kTestRoute.back()),
RouterDelegate::kNoTimeout);
}
});
TEST(oppositeTimedSignal.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration), ("Route was not built."));
// Rebuild route and go in opposite direction. So initiate a route rebuilding flag.
m_session->SetRoutingCallbacks(
[&oppositeTimedSignal](Route const &, RouterResultCode) { oppositeTimedSignal.Signal(); },
nullptr /* rebuildReadyCallback */, nullptr /* needMoreMapsCallback */,
nullptr /* removeRouteCallback */);
{
m_session->BuildRoute(Checkpoints(kTestRoute.front(), kTestRoute.back()),
RouterDelegate::kNoTimeout);
}
});
TEST(oppositeTimedSignal.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration), ("Route was not built."));
}
// Going away from route to set rebuilding flag.
TimedSignal checkTimedSignal;
@ -253,7 +335,6 @@ UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestRouteRebuildingMovingA
UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestRouteRebuildingMovingToRoute)
{
location::GpsInfo info;
FrozenDataSource dataSource;
size_t counter = 0;
TimedSignal alongTimedSignal;
@ -277,33 +358,34 @@ UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestRouteRebuildingMovingT
TEST_EQUAL(counter, 1, ());
// Going starting far from the route and moving to the route but rebuild flag still is set.
TimedSignal checkTimedSignalAway;
GetPlatform().RunTask(Platform::Thread::Gui, [&checkTimedSignalAway, &info, this]() {
info.m_longitude = 0.0;
info.m_latitude = 0.0;
info.m_speedMpS = routing::KMPH2MPS(60);
SessionState code = SessionState::NoValidRoute;
{
SessionStateTest sessionStateTest(
{SessionState::RouteNotStarted, SessionState::RouteNeedRebuild},
*m_session);
for (size_t i = 0; i < 8; ++i)
{
SessionStateTest sessionStateTest(
{SessionState::RouteNotStarted, SessionState::RouteNeedRebuild},
*m_session);
TimedSignal checkTimedSignalAway;
GetPlatform().RunTask(Platform::Thread::Gui, [&checkTimedSignalAway, &info, this]() {
info.m_longitude = 0.0;
info.m_latitude = 0.0;
info.m_speedMpS = routing::KMPH2MPS(60);
SessionState code = SessionState::NoValidRoute;
{
code = m_session->OnLocationPositionChanged(info);
info.m_latitude += 0.1;
for (size_t i = 0; i < 8; ++i)
{
code = m_session->OnLocationPositionChanged(info);
info.m_latitude += 0.1;
}
}
}
TEST_EQUAL(code, SessionState::RouteNeedRebuild, ());
checkTimedSignalAway.Signal();
});
TEST(checkTimedSignalAway.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration),
("Route was not rebuilt."));
TEST_EQUAL(code, SessionState::RouteNeedRebuild, ());
checkTimedSignalAway.Signal();
});
TEST(checkTimedSignalAway.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration),
("Route was not rebuilt."));
}
}
UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestFollowRouteFlagPersistence)
{
location::GpsInfo info;
FrozenDataSource dataSource;
size_t counter = 0;
TimedSignal alongTimedSignal;
@ -390,7 +472,6 @@ UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestFollowRouteFlagPersist
UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestFollowRoutePercentTest)
{
FrozenDataSource dataSource;
TimedSignal alongTimedSignal;
GetPlatform().RunTask(Platform::Thread::Gui, [&alongTimedSignal, this]() {
InitRoutingSession();
@ -452,4 +533,89 @@ UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestFollowRoutePercentTest
TEST(checkTimedSignal.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration),
("Route checking timeout."));
}
UNIT_CLASS_TEST(AsyncGuiThreadTestWithRoutingSession, TestRouteRebuildingError)
{
vector<m2::PointD> const kRoute = {{0.0, 0.001}, {0.0, 0.002}, {0.0, 0.003}, {0.0, 0.004}};
// Creation RoutingSession.
TimedSignal createTimedSignal;
GetPlatform().RunTask(Platform::Thread::Gui, [this, &kRoute, &createTimedSignal]() {
InitRoutingSession();
unique_ptr<ReturnCodesRouter> router = make_unique<ReturnCodesRouter>(initializer_list<
RouterResultCode>{RouterResultCode::NoError, RouterResultCode::InternalError}, kRoute);
m_session->SetRouter(move(router), nullptr);
createTimedSignal.Signal();
});
TEST(createTimedSignal.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration),
("RouteSession was not created."));
// Building a route.
{
SessionStateTest sessionStateTest(
{SessionState::NoValidRoute, SessionState::RouteBuilding, SessionState::RouteNotStarted},
*m_session);
TimedSignal buildTimedSignal;
GetPlatform().RunTask(Platform::Thread::Gui, [this, &kRoute, &buildTimedSignal]() {
m_session->SetRoutingCallbacks(
[&buildTimedSignal](Route const &, RouterResultCode) { buildTimedSignal.Signal(); },
nullptr /* rebuildReadyCallback */, nullptr /* needMoreMapsCallback */,
nullptr /* removeRouteCallback */);
m_session->BuildRoute(Checkpoints(kRoute.front(), kRoute.back()),
RouterDelegate::kNoTimeout);
});
TEST(buildTimedSignal
.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration), ("Route was not built."));
}
location::GpsInfo info;
info.m_horizontalAccuracy = 5.0; // meters
info.m_verticalAccuracy = 5.0; // meters
info.m_longitude = 0.0;
// Moving along route.
{
SessionStateTest sessionStateTest({SessionState::RouteNotStarted, SessionState::OnRoute},
*m_session);
vector<double> const latitudes = {0.001, 0.0015, 0.002};
TestMovingByUpdatingLat(sessionStateTest, latitudes, info, *m_session);
}
// Leaving the route until switching to |SessionState::RouteNeedRebuild| state.
TestLeavingRoute(*m_session, info);
// Continue moving along the route.
{
SessionStateTest sessionStateTest({SessionState::RouteNeedRebuild, SessionState::OnRoute},
*m_session);
vector<double> const latitudes = {0.002, 0.0025, 0.003};
TestMovingByUpdatingLat(sessionStateTest, latitudes, info, *m_session);
}
// Leaving the route until switching to |SessionState::RouteRebuilding| state.
TestLeavingRoute(*m_session, info);
{
SessionStateTest sessionStateTest(
{SessionState::RouteNeedRebuild, SessionState::RouteRebuilding,
SessionState::RouteNotStarted
},
*m_session);
TimedSignal signal;
GetPlatform().RunTask(Platform::Thread::Gui, [this, &signal]() {
m_session->SetState(SessionState::RouteRebuilding);
m_session->SetState(SessionState::RouteNotStarted);
signal.Signal();
});
TEST(signal.WaitUntil(steady_clock::now() + kRouteBuildingMaxDuration),
("State was not set."));
}
// Continue moving along the route again.
{
SessionStateTest sessionStateTest({SessionState::RouteNotStarted, SessionState::OnRoute},
*m_session);
vector<double> const latitudes = {0.003, 0.0035, 0.004};
TestMovingByUpdatingLat(sessionStateTest, latitudes, info, *m_session);
}
}
} // namespace routing