#include "testing/testing.hpp" #include "routing/cross_mwm_connector_serialization.hpp" #include "routing/cross_mwm_ids.hpp" #include "coding/writer.hpp" #include "base/geo_object_id.hpp" using namespace routing; using namespace routing::connector; using namespace std; namespace { NumMwmId constexpr mwmId = 777; template CrossMwmConnector CreateConnector() { return CrossMwmConnector(mwmId, 0 /* featuresNumerationOffset */); } template void TestConnectorConsistency(CrossMwmConnector const & connector) { for (Segment const & enter : connector.GetEnters()) { TEST(connector.IsTransition(enter, false /* isOutgoing */), ("enter:", enter)); TEST(!connector.IsTransition(enter, true /* isOutgoing */), ("enter:", enter)); } for (Segment const & exit : connector.GetExits()) { TEST(!connector.IsTransition(exit, false /* isOutgoing */), ("exit:", exit)); TEST(connector.IsTransition(exit, true /* isOutgoing */), ("exit:", exit)); } } template void TestOutgoingEdges(CrossMwmConnector const & connector, Segment const & from, vector const & expectedEdges) { vector edges; connector.GetOutgoingEdgeList(from, edges); TEST_EQUAL(edges, expectedEdges, ()); } template void TestOneWayEnter(CrossMwmId const & crossMwmId) { uint32_t constexpr featureId = 1; uint32_t constexpr segmentIdx = 1; auto connector = CreateConnector(); connector.AddTransition(crossMwmId, featureId, segmentIdx, true /* oneWay */, true /* forwardIsEnter */); TestConnectorConsistency(connector); TEST_EQUAL(connector.GetEnters().size(), 1, ()); TEST_EQUAL(connector.GetExits().size(), 0, ()); TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), true /* isOutgoing */), ()); TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), false /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), true /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), false /* isOutgoing */), ()); } template void TestOneWayExit(CrossMwmId const & crossMwmId) { uint32_t constexpr featureId = 1; uint32_t constexpr segmentIdx = 1; auto connector = CreateConnector(); connector.AddTransition(crossMwmId, featureId, segmentIdx, true /* oneWay */, false /* forwardIsEnter */); TestConnectorConsistency(connector); TEST_EQUAL(connector.GetEnters().size(), 0, ()); TEST_EQUAL(connector.GetExits().size(), 1, ()); TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), true /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), false /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), true /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), false /* isOutgoing */), ()); } template void TestTwoWayEnter(CrossMwmId const & crossMwmId) { uint32_t constexpr featureId = 1; uint32_t constexpr segmentIdx = 1; auto connector = CreateConnector(); connector.AddTransition(crossMwmId, featureId, segmentIdx, false /* oneWay */, true /* forwardIsEnter */); TestConnectorConsistency(connector); TEST_EQUAL(connector.GetEnters().size(), 1, ()); TEST_EQUAL(connector.GetExits().size(), 1, ()); TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), true /* isOutgoing */), ()); TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), false /* isOutgoing */), ()); TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), true /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), false /* isOutgoing */), ()); } template void TestTwoWayExit(CrossMwmId const & crossMwmId) { uint32_t constexpr featureId = 1; uint32_t constexpr segmentIdx = 1; auto connector = CreateConnector(); connector.AddTransition(crossMwmId, featureId, segmentIdx, false /* oneWay */, false /* forwardIsEnter */); TestConnectorConsistency(connector); TEST_EQUAL(connector.GetEnters().size(), 1, ()); TEST_EQUAL(connector.GetExits().size(), 1, ()); TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), true /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, true /* forward */), false /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), true /* isOutgoing */), ()); TEST(connector.IsTransition(Segment(mwmId, featureId, segmentIdx, false /* forward */), false /* isOutgoing */), ()); } template void TestSerialization(vector> const & transitions) { double constexpr kEdgesWeight = 4444.0; vector buffer; { CrossMwmConnectorPerVehicleType connectors; CrossMwmConnector & carConnector = connectors[static_cast(VehicleType::Car)]; for (auto const & transition : transitions) CrossMwmConnectorSerializer::AddTransition(transition, kCarMask, carConnector); carConnector.FillWeights( [&](Segment const & enter, Segment const & exit) { return kEdgesWeight; }); serial::GeometryCodingParams const codingParams; MemWriter> writer(buffer); CrossMwmConnectorSerializer::Serialize(transitions, connectors, writer); } auto connector = CreateConnector(); { MemReader reader(buffer.data(), buffer.size()); ReaderSource source(reader); CrossMwmConnectorSerializer::DeserializeTransitions(VehicleType::Car, connector, source); } TestConnectorConsistency(connector); TEST_EQUAL(connector.GetEnters().size(), 2, ()); TEST_EQUAL(connector.GetExits().size(), 1, ()); TEST(!connector.IsTransition(Segment(mwmId, 0, 0, true), true /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, 10, 1, true /* forward */), true /* isOutgoing */), ()); TEST(connector.IsTransition(Segment(mwmId, 10, 1, true /* forward */), false /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, 10, 1, false /* forward */), true /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, 10, 1, false /* forward */), false /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, 20, 2, true /* forward */), true /* isOutgoing */), ()); TEST(connector.IsTransition(Segment(mwmId, 20, 2, true /* forward */), false /* isOutgoing */), ()); TEST(connector.IsTransition(Segment(mwmId, 20, 2, false /* forward */), true /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, 20, 2, false /* forward */), false /* isOutgoing */), ()); TEST(!connector.IsTransition(Segment(mwmId, 30, 3, true /* forward */), true /* isOutgoing */), ()); TEST(!connector.WeightsWereLoaded(), ()); TEST(!connector.HasWeights(), ()); { MemReader reader(buffer.data(), buffer.size()); ReaderSource source(reader); CrossMwmConnectorSerializer::DeserializeWeights(VehicleType::Car, connector, source); } TEST(connector.WeightsWereLoaded(), ()); TEST(connector.HasWeights(), ()); TestOutgoingEdges(connector, Segment(mwmId, 10, 1, true /* forward */), {{Segment(mwmId, 20, 2, false /* forward */), RouteWeight::FromCrossMwmWeight(kEdgesWeight)}}); TestOutgoingEdges(connector, Segment(mwmId, 20, 2, true /* forward */), {{Segment(mwmId, 20, 2, false /* forward */), RouteWeight::FromCrossMwmWeight(kEdgesWeight)}}); } void GetCrossMwmId(uint32_t i, base::GeoObjectId & id) { id = base::GeoObjectId(base::MakeOsmWay(10 * i)); } void GetCrossMwmId(uint32_t i, TransitId & id) { id = TransitId(1 /* stop 1 id */, 10 * i /* stop 1 id */, 1 /* line id */); } template void TestWeightsSerialization() { size_t constexpr kNumTransitions = 3; vector const weights = { 4.0, 20.0, connector::kNoRoute, 12.0, connector::kNoRoute, 40.0, 48.0, 24.0, 12.0}; TEST_EQUAL(weights.size(), kNumTransitions * kNumTransitions, ()); vector buffer; { vector> transitions; for (uint32_t featureId = 0; featureId < kNumTransitions; ++featureId) { CrossMwmId id; GetCrossMwmId(featureId, id); transitions.emplace_back(id, featureId, 1 /* segmentIdx */, kCarMask, 0 /* oneWayMask */, true /* forwardIsEnter */); } CrossMwmConnectorPerVehicleType connectors; CrossMwmConnector & carConnector = connectors[static_cast(VehicleType::Car)]; for (auto const & transition : transitions) CrossMwmConnectorSerializer::AddTransition(transition, kCarMask, carConnector); int weightIdx = 0; carConnector.FillWeights( [&](Segment const & enter, Segment const & exit) { return weights[weightIdx++]; }); serial::GeometryCodingParams const codingParams; MemWriter> writer(buffer); CrossMwmConnectorSerializer::Serialize(transitions, connectors, writer); } auto connector = CreateConnector(); { MemReader reader(buffer.data(), buffer.size()); ReaderSource source(reader); CrossMwmConnectorSerializer::DeserializeTransitions(VehicleType::Car, connector, source); } TestConnectorConsistency(connector); TEST_EQUAL(connector.GetEnters().size(), kNumTransitions, ()); TEST_EQUAL(connector.GetExits().size(), kNumTransitions, ()); TEST(!connector.WeightsWereLoaded(), ()); TEST(!connector.HasWeights(), ()); { MemReader reader(buffer.data(), buffer.size()); ReaderSource source(reader); CrossMwmConnectorSerializer::DeserializeWeights(VehicleType::Car, connector, source); } TEST(connector.WeightsWereLoaded(), ()); TEST(connector.HasWeights(), ()); int weightIdx = 0; for (uint32_t enterId = 0; enterId < kNumTransitions; ++enterId) { Segment const enter(mwmId, enterId, 1, true /* forward */); vector expectedEdges; for (uint32_t exitId = 0; exitId < kNumTransitions; ++exitId) { auto const weight = weights[weightIdx]; if (weight != connector::kNoRoute) { expectedEdges.emplace_back(Segment(mwmId, exitId, 1 /* segmentIdx */, false /* forward */), RouteWeight::FromCrossMwmWeight(weight)); } ++weightIdx; } TestOutgoingEdges(connector, enter, expectedEdges); } } } // namespace namespace routing_test { UNIT_TEST(OneWayEnter) { TestOneWayEnter(base::MakeOsmWay(1ULL)); TestOneWayEnter(TransitId(1 /* stop 1 id */, 2 /* stop 2 id */, 1 /* line id */)); } UNIT_TEST(OneWayExit) { TestOneWayExit(base::MakeOsmWay(1ULL)); TestOneWayExit(TransitId(1 /* stop 1 id */, 2 /* stop 2 id */, 1 /* line id */)); } UNIT_TEST(TwoWayEnter) { TestTwoWayEnter(base::MakeOsmWay(1ULL)); TestTwoWayEnter(TransitId(1 /* stop 1 id */, 2 /* stop 2 id */, 1 /* line id */)); } UNIT_TEST(TwoWayExit) { TestTwoWayExit(base::MakeOsmWay(1ULL)); TestTwoWayExit(TransitId(1 /* stop 1 id */, 2 /* stop 2 id */, 1 /* line id */)); } UNIT_TEST(Serialization) { { vector> const transitions = { /* osmId featureId, segmentIdx, roadMask, oneWayMask, forwardIsEnter, backPoint, frontPoint */ {base::MakeOsmWay(100ULL), 10, 1, kCarMask, kCarMask, true}, {base::MakeOsmWay(200ULL), 20, 2, kCarMask, 0, true}, {base::MakeOsmWay(300ULL), 30, 3, kPedestrianMask, kCarMask, true}}; TestSerialization(transitions); } { vector> const transitions = { /* osmId featureId, segmentIdx, roadMask, oneWayMask, forwardIsEnter, backPoint, frontPoint */ {TransitId(1ULL /* stop 1 id */, 2ULL /* stop 2 id */, 1ULL /* line id */), 10, 1, kCarMask, kCarMask, true}, {TransitId(1ULL, 3ULL, 1ULL), 20, 2, kCarMask, 0, true}, {TransitId(1ULL, 3ULL, 2ULL), 30, 3, kPedestrianMask, kCarMask, true}}; TestSerialization(transitions); } } UNIT_TEST(WeightsSerialization) { TestWeightsSerialization(); TestWeightsSerialization(); } } // namespace routing_test