Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,13 @@
project(transit_tests)
set(SRC
transit_graph_test.cpp
transit_json_parsing_test.cpp
transit_schedule_tests.cpp
transit_test.cpp
transit_tools.hpp
)
omim_add_test(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME} transit)

View file

@ -0,0 +1,775 @@
#include "testing/testing.hpp"
#include "transit/transit_tests/transit_tools.hpp"
#include "transit/transit_graph_data.hpp"
#include "transit/transit_types.hpp"
#include <memory>
#include <string>
#include <utility>
#include <vector>
using namespace routing;
using namespace routing::transit;
using namespace std;
namespace
{
struct Graph
{
vector<Stop> m_stops;
vector<Gate> m_gates;
vector<Edge> m_edges;
vector<Transfer> m_transfers;
vector<Line> m_lines;
vector<Shape> m_shapes;
vector<Network> m_networks;
};
unique_ptr<GraphData> CreateGraphFromJson()
{
string const jsonBuffer = R"(
{
"stops": [{
"id": 0,
"line_ids": [1],
"osm_id": "100",
"point": { "x": -2.0, "y": 1.0 },
"title_anchors": []
},
{
"id": 1,
"line_ids": [1],
"osm_id": "101",
"point": { "x": 0.0, "y": 1.0 },
"title_anchors": []
},
{
"id": 2,
"line_ids": [1],
"osm_id": "102",
"point": { "x": 2.0, "y": 1.0 },
"title_anchors": []
},
{
"id": 3,
"line_ids": [1],
"osm_id": "103",
"point": { "x": 4.0, "y": 1.0 },
"title_anchors": []
},
{
"id": 4,
"line_ids": [1],
"osm_id": "104",
"point": { "x": 5.0, "y": 1.0 },
"title_anchors": []
},
{
"id": 5,
"line_ids": [2],
"osm_id": "105",
"point": { "x": -1.0, "y": -1.0 },
"title_anchors": []
},
{
"id": 6,
"line_ids": [2],
"osm_id": "106",
"point": { "x": 1.0, "y": -1.0 },
"title_anchors": []
}
],
"lines": [
{
"id": 1,
"interval": 150,
"network_id": 2,
"number": "1",
"stop_ids": [ 0, 1, 2, 3, 4 ],
"title": "Московская линия",
"type": "subway",
"color": "green"
},
{
"id": 2,
"interval": 150,
"network_id": 2,
"number": "2",
"stop_ids": [ 5, 6 ],
"title": "Варшавская линия",
"type": "subway",
"color": "red"
}
],
"transfers": [ ],
"networks": [
{
"id": 2,
"title": "Минский метрополитен"
}
],
"edges": [
{
"stop1_id": 0,
"stop2_id": 1,
"line_id": 1,
"shape_ids": [ { "stop1_id": 0, "stop2_id": 1 }],
"transfer": false,
"weight" : 20
},
{
"stop1_id": 1,
"stop2_id": 2,
"line_id": 1,
"shape_ids": [ { "stop1_id": 1, "stop2_id": 2 }],
"transfer": false,
"weight" : 20
},
{
"stop1_id": 2,
"stop2_id": 3,
"line_id": 1,
"shape_ids": [ { "stop1_id": 2, "stop2_id": 3 }],
"transfer": false,
"weight" : 20
},
{
"stop1_id": 3,
"stop2_id": 4,
"line_id": 1,
"shape_ids": [ { "stop1_id": 3, "stop2_id": 4 }],
"transfer": false,
"weight" : 10
},
{
"stop1_id": 3,
"stop2_id": 4,
"line_id": 1,
"shape_ids": [ { "stop1_id": 3, "stop2_id": 4 }],
"transfer": false,
"weight" : 20
},
{
"stop1_id": 3,
"stop2_id": 4,
"line_id": 1,
"shape_ids": [ { "stop1_id": 3, "stop2_id": 4 }],
"transfer": false,
"weight" : 20
},
{
"stop1_id": 5,
"stop2_id": 6,
"line_id": 2,
"shape_ids": [ { "stop1_id": 5, "stop2_id": 6 }],
"transfer": false,
"weight" : 20
}
],
"shapes": [
{
"id": { "stop1_id": 0, "stop2_id": 1 },
"polyline": [
{ "x": -2.0, "y": 1.0 },
{ "x": 0.0, "y": 1.0 }
]
},
{
"id": { "stop1_id": 1, "stop2_id": 2 },
"polyline": [
{ "x": 0.0, "y": 1.0 },
{ "x": 2.0, "y": 1.0 }
]
},
{
"id": { "stop1_id": 2, "stop2_id": 3 },
"polyline": [
{ "x": 2.0, "y": 1.0 },
{ "x": 4.0, "y": 1.0 }
]
},
{
"id": { "stop1_id": 3, "stop2_id": 4 },
"polyline": [
{ "x": 4.0, "y": 1.0 },
{ "x": 5.0, "y": 1.0 }
]
},
{
"id": { "stop1_id": 5, "stop2_id": 6 },
"polyline": [
{ "x": -1.0, "y": -1.0 },
{ "x": 1.0, "y": -1.0 }
]
}
],
"gates": [
{
"entrance": true,
"exit": true,
"osm_id": "100",
"point": { "x": -2.0, "y": 1.0 },
"stop_ids": [ 0 ],
"weight": 0
},
{
"entrance": true,
"exit": true,
"osm_id": "101",
"point": { "x": 0.0, "y": 1.0 },
"stop_ids": [ 1 ],
"weight": 0
},
{
"entrance": true,
"exit": true,
"osm_id": "102",
"point": { "x": 2.0, "y": 1.0 },
"stop_ids": [ 2 ],
"weight": 0
},
{
"entrance": true,
"exit": true,
"osm_id": "103",
"point": { "x": 4.0, "y": 1.0 },
"stop_ids": [ 3 ],
"weight": 0
},
{
"entrance": true,
"exit": true,
"osm_id": "104",
"point": { "x": 5.0, "y": 1.0 },
"stop_ids": [ 4 ],
"weight": 0
},
{
"entrance": true,
"exit": true,
"osm_id": "105",
"point": { "x": -1.0, "y": -1.0 },
"stop_ids": [ 5 ],
"weight": 0
},
{
"entrance": true,
"exit": true,
"osm_id": "106",
"point": { "x": 1.0, "y": -1.0 },
"stop_ids": [ 6 ],
"weight": 0
}
]})";
auto graph = make_unique<GraphData>();
OsmIdToFeatureIdsMap mapping;
mapping[base::GeoObjectId(100)] = vector<FeatureId>({10});
mapping[base::GeoObjectId(101)] = vector<FeatureId>({11});
mapping[base::GeoObjectId(102)] = vector<FeatureId>({12});
mapping[base::GeoObjectId(103)] = vector<FeatureId>({13});
mapping[base::GeoObjectId(104)] = vector<FeatureId>({14});
mapping[base::GeoObjectId(105)] = vector<FeatureId>({15});
mapping[base::GeoObjectId(106)] = vector<FeatureId>({16});
base::Json root(jsonBuffer.c_str());
CHECK(root.get() != nullptr, ("Cannot parse the json."));
graph->DeserializeFromJson(root, mapping);
return graph;
}
unique_ptr<Graph> MakeFullGraph()
{
auto graph = make_unique<Graph>();
graph->m_stops = {{0 /* stop id */,
100 /* osm id */,
10 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(-2.0, 1.0),
{}},
{1 /* stop id */,
101 /* osm id */,
11 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(0.0, 1.0),
{}},
{2 /* stop id */,
102 /* osm id */,
12 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(2.0, 1.0),
{}},
{3 /* stop id */,
103 /* osm id */,
13 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(4.0, 1.0),
{}},
{4 /* stop id */,
104 /* osm id */,
14 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(5.0, 1.0),
{}},
{5 /* stop id */,
105 /* osm id */,
15 /* feature id */,
kInvalidTransferId,
{2} /* line ids */,
m2::PointD(-1.0, -1.0),
{}},
{6 /* stop id */,
106 /* osm id */,
16 /* feature id */,
kInvalidTransferId,
{2} /* line ids */,
m2::PointD(1.0, -1.0),
{}}};
graph->m_gates = {
{100 /* osm id */,
10 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{0} /* stop ids */,
m2::PointD(-2.0, 1.0)},
{101 /* osm id */,
11 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{1} /* stop ids */,
m2::PointD(0.0, 1.0)},
{102 /* osm id */,
12 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{2} /* stop ids */,
m2::PointD(2.0, 1.0)},
{103 /* osm id */,
13 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{3} /* stop ids */,
m2::PointD(4.0, 1.0)},
{104 /* osm id */,
14 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{4} /* stop ids */,
m2::PointD(5.0, 1.0)},
{105 /* osm id */,
15 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{5} /* stop ids */,
m2::PointD(-1.0, -1.0)},
{106 /* osm id */,
16 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{6} /* stop ids */,
m2::PointD(1.0, -1.0)},
};
graph->m_edges = {{0 /* stop 1 id */,
1 /* stop 2 id */,
20 /* weight */,
1 /* line id */,
false /* transfer */,
{{0, 1}} /* shape ids */},
{1 /* stop 1 id */,
2 /* stop 2 id */,
20 /* weight */,
1 /* line id */,
false /* transfer */,
{{1, 2}} /* shape ids */},
{2 /* stop 1 id */,
3 /* stop 2 id */,
20 /* weight */,
1 /* line id */,
false /* transfer */,
{{2, 3}} /* shape ids */},
{3 /* stop 1 id */,
4 /* stop 2 id */,
10 /* weight */,
1 /* line id */,
false /* transfer */,
{{3, 4}} /* shape ids */},
{5 /* stop 1 id */,
6 /* stop 2 id */,
20 /* weight */,
2 /* line id */,
false /* transfer */,
{{5, 6}} /* shape ids */}};
// |graph.m_transfers| should be empty.
graph->m_lines = {{1 /* line id */,
"1" /* number */,
"Московская линия" /* title */,
"subway" /* type */,
"green",
2 /* network id */,
{{0, 1, 2, 3, 4}} /* stop id */,
150 /* interval */},
{2 /* line id */,
"2" /* number */,
"Варшавская линия" /* title */,
"subway" /* type */,
"red",
2 /* network id */,
{{5, 6}} /* stop id */,
150 /* interval */}};
graph->m_shapes = {{{0, 1} /* shape id */, {{-2.0, 1.0}, {0.0, 1.0}} /* polyline */},
{{1, 2} /* shape id */, {{0.0, 1.0}, {2.0, 1.0}} /* polyline */},
{{2, 3} /* shape id */, {{2.0, 1.0}, {4.0, 1.0}} /* polyline */},
{{3, 4} /* shape id */, {{4.0, 1.0}, {5.0, 1.0}} /* polyline */},
{{5, 6} /* shape id */, {{-1.0, -1.0}, {1.0, -1.0}} /* polyline */}};
graph->m_networks = {{2 /* network id */, "Минский метрополитен" /* title */}};
return graph;
}
unique_ptr<Graph> MakeOneLineGraph()
{
auto graph = make_unique<Graph>();
graph->m_stops = {{0 /* stop id */,
100 /* osm id */,
10 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(-2.0, 1.0),
{}},
{1 /* stop id */,
101 /* osm id */,
11 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(0.0, 1.0),
{}},
{2 /* stop id */,
102 /* osm id */,
12 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(2.0, 1.0),
{}},
{3 /* stop id */,
103 /* osm id */,
13 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(4.0, 1.0),
{}}};
graph->m_gates = {{100 /* osm id */,
10 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{0} /* stop ids */,
m2::PointD(-2.0, 1.0)},
{101 /* osm id */,
11 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{1} /* stop ids */,
m2::PointD(0.0, 1.0)},
{102 /* osm id */,
12 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{2} /* stop ids */,
m2::PointD(2.0, 1.0)},
{103 /* osm id */,
13 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{3} /* stop ids */,
m2::PointD(4.0, 1.0)}};
graph->m_edges = {{0 /* stop 1 id */,
1 /* stop 2 id */,
20 /* weight */,
1 /* line id */,
false /* transfer */,
{{0, 1}} /* shape ids */},
{1 /* stop 1 id */,
2 /* stop 2 id */,
20 /* weight */,
1 /* line id */,
false /* transfer */,
{{1, 2}} /* shape ids */},
{2 /* stop 1 id */,
3 /* stop 2 id */,
20 /* weight */,
1 /* line id */,
false /* transfer */,
{{2, 3}} /* shape ids */}};
// |graph.m_transfers| should be empty.
graph->m_lines = {{1 /* line id */,
"1" /* number */,
"Московская линия" /* title */,
"subway" /* type */,
"green",
2 /* network id */,
{{0, 1, 2, 3}} /* stop id */,
150 /* interval */}};
graph->m_shapes = {{{0, 1} /* shape id */, {{-2.0, 1.0}, {0.0, 1.0}} /* polyline */},
{{1, 2} /* shape id */, {{0.0, 1.0}, {2.0, 1.0}} /* polyline */},
{{2, 3} /* shape id */, {{2.0, 1.0}, {4.0, 1.0}} /* polyline */}};
graph->m_networks = {{2 /* network id */, "Минский метрополитен" /* title */}};
return graph;
}
unique_ptr<Graph> MakeTwoLinesGraph()
{
auto graph = make_unique<Graph>();
graph->m_stops = {{1 /* stop id */,
101 /* osm id */,
11 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(0.0, 1.0),
{}},
{2 /* stop id */,
102 /* osm id */,
12 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(2.0, 1.0),
{}},
{3 /* stop id */,
103 /* osm id */,
13 /* feature id */,
kInvalidTransferId,
{1} /* line ids */,
m2::PointD(4.0, 1.0),
{}},
{5 /* stop id */,
105 /* osm id */,
15 /* feature id */,
kInvalidTransferId,
{2} /* line ids */,
m2::PointD(-1.0, -1.0),
{}},
{6 /* stop id */,
106 /* osm id */,
16 /* feature id */,
kInvalidTransferId,
{2} /* line ids */,
m2::PointD(1.0, -1.0),
{}}};
graph->m_gates = {
{101 /* osm id */,
11 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{1} /* stop ids */,
m2::PointD(0.0, 1.0)},
{102 /* osm id */,
12 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{2} /* stop ids */,
m2::PointD(2.0, 1.0)},
{103 /* osm id */,
13 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{3} /* stop ids */,
m2::PointD(4.0, 1.0)},
{105 /* osm id */,
15 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{5} /* stop ids */,
m2::PointD(-1.0, -1.0)},
{106 /* osm id */,
16 /* feature id */,
true /* entrance */,
true /* exit */,
0 /* weight */,
{6} /* stop ids */,
m2::PointD(1.0, -1.0)},
};
graph->m_edges = {{1 /* stop 1 id */,
2 /* stop 2 id */,
20 /* weight */,
1 /* line id */,
false /* transfer */,
{{1, 2}} /* shape ids */},
{2 /* stop 1 id */,
3 /* stop 2 id */,
20 /* weight */,
1 /* line id */,
false /* transfer */,
{{2, 3}} /* shape ids */},
{5 /* stop 1 id */,
6 /* stop 2 id */,
20 /* weight */,
2 /* line id */,
false /* transfer */,
{{5, 6}} /* shape ids */}};
// |graph.m_transfers| should be empty.
graph->m_lines = {{1 /* line id */,
"1" /* number */,
"Московская линия" /* title */,
"subway" /* type */,
"green",
2 /* network id */,
{{1, 2, 3}} /* stop id */,
150 /* interval */},
{2 /* line id */,
"2" /* number */,
"Варшавская линия" /* title */,
"subway" /* type */,
"red",
2 /* network id */,
{{5, 6}} /* stop id */,
150 /* interval */}};
graph->m_shapes = {{{1, 2} /* shape id */, {{0.0, 1.0}, {2.0, 1.0}} /* polyline */},
{{2, 3} /* shape id */, {{2.0, 1.0}, {4.0, 1.0}} /* polyline */},
{{5, 6} /* shape id */, {{-1.0, -1.0}, {1.0, -1.0}} /* polyline */}};
graph->m_networks = {{2 /* network id */, "Минский метрополитен" /* title */}};
return graph;
}
void TestGraph(GraphData const & actualGraph, Graph const & expectedGraph)
{
TestForEquivalence(actualGraph.GetStops(), expectedGraph.m_stops);
TestForEquivalence(actualGraph.GetGates(), expectedGraph.m_gates);
TestForEquivalence(actualGraph.GetEdges(), expectedGraph.m_edges);
TestForEquivalence(actualGraph.GetTransfers(), expectedGraph.m_transfers);
TestForEquivalence(actualGraph.GetLines(), expectedGraph.m_lines);
TestForEquivalence(actualGraph.GetShapes(), expectedGraph.m_shapes);
TestForEquivalence(actualGraph.GetNetworks(), expectedGraph.m_networks);
}
void SerializeAndDeserializeGraph(GraphData & src, GraphData & dst)
{
vector<uint8_t> buffer;
MemWriter<decltype(buffer)> writer(buffer);
src.Serialize(writer);
MemReader reader(buffer.data(), buffer.size());
dst.DeserializeAll(reader);
dst.CheckValidSortedUnique();
}
// ^
// |
// * 2 _______
// | |
// s0----------s1----------s2----------s3----s4 Line 1
// |_____|
// * * * * * * * * * -->
// -3 -2 -1 0 1 2 3 4 5
// s5----------s6 Line 2
//
UNIT_TEST(ClipGraph_SmokeTest)
{
auto graph = CreateGraphFromJson();
graph->Sort();
auto expectedGraph = MakeFullGraph();
TestGraph(*graph, *expectedGraph);
GraphData readedGraph;
SerializeAndDeserializeGraph(*graph, readedGraph);
TestGraph(*graph, *expectedGraph);
}
// ^
// |
// * 4
//
// ------------------* 3-----------Border
// | |
// | * 2 | _______
// | | | |
// | s0----------s1----------s2----|-----s3----s4 Line 1
// | | |_____|
// *-----*-----*-----*-----*-----*-----* * * -->
// -3 -2 -1 0 1 2 3 4 5
// s5----------s6 Line 2
//
UNIT_TEST(ClipGraph_OneLineTest)
{
auto graph = CreateGraphFromJson();
vector<m2::PointD> points = {{3.0, 3.0}, {3.0, 0.0}, {-3.0, 0.0}, {-3.0, 3.0}, {3.0, 3.0}};
graph->ClipGraph({m2::RegionD(points)});
auto expectedGraph = MakeOneLineGraph();
TestGraph(*graph, *expectedGraph);
GraphData readedGraph;
SerializeAndDeserializeGraph(*graph, readedGraph);
TestGraph(*graph, *expectedGraph);
}
// ^
// |
// * 3
//
// * 2----------Border _______
// | | | |
// s0----------s1-|--------s2-|--------s3----s4 Line 1
// | | |_____|
// * * * * | * * | * * * -->
// -3 -2 -1 0 | 1 2 | 3 4 5
// Line 2 s5-------|--s6 |
// | |
// -2 * -------------
//
UNIT_TEST(ClipGraph_TwoLinesTest)
{
auto graph = CreateGraphFromJson();
vector<m2::PointD> points = {{2.5, 2.0}, {2.5, -2.0}, {0.5, -2.0}, {0.5, 2.0}, {2.5, 2.0}};
graph->ClipGraph({m2::RegionD(points)});
auto expectedGraph = MakeTwoLinesGraph();
TestGraph(*graph, *expectedGraph);
GraphData readedGraph;
SerializeAndDeserializeGraph(*graph, readedGraph);
TestGraph(*graph, *expectedGraph);
}
} // namespace

View file

@ -0,0 +1,337 @@
#include "testing/testing.hpp"
#include "transit/transit_tests/transit_tools.hpp"
#include "transit/transit_graph_data.hpp"
#include "transit/transit_types.hpp"
#include "base/assert.hpp"
#include <memory>
#include <string>
#include <utility>
#include <vector>
using namespace routing;
using namespace routing::transit;
using namespace std;
namespace
{
template <typename Obj>
void TestDeserializerFromJson(string const & jsonBuffer, OsmIdToFeatureIdsMap const & osmIdToFeatureIds,
string const & name, vector<Obj> const & expected)
{
base::Json root(jsonBuffer.c_str());
CHECK(root.get() != nullptr, ("Cannot parse the json."));
DeserializerFromJson deserializer(root.get(), osmIdToFeatureIds);
vector<Obj> objects;
deserializer(objects, name.c_str());
TEST_EQUAL(objects.size(), expected.size(), ());
TestForEquivalence(objects, expected);
}
template <typename Obj>
void TestDeserializerFromJson(string const & jsonBuffer, string const & name, vector<Obj> const & expected)
{
return TestDeserializerFromJson(jsonBuffer, OsmIdToFeatureIdsMap(), name, expected);
}
UNIT_TEST(DeserializerFromJson_TitleAnchors)
{
string const jsonBuffer = R"(
{
"title_anchors": [
{ "min_zoom": 11, "anchor": 4 },
{ "min_zoom": 14, "anchor": 6 }
]})";
vector<TitleAnchor> expected = {TitleAnchor(11 /* min zoom */, 4 /* anchor */),
TitleAnchor(14 /* min zoom */, 6 /* anchor */)};
TestDeserializerFromJson(jsonBuffer, "title_anchors", expected);
}
UNIT_TEST(DeserializerFromJson_Stops)
{
string const jsonBuffer = R"(
{
"stops": [{
"id": 343259523,
"line_ids": [
19207936,
19207937
],
"osm_id": "1234",
"point": {
"x": 27.4970954,
"y": 64.20146835878187
},
"title_anchors": []
},
{
"id": 266680843,
"transfer_id" : 5,
"line_ids": [
19213568,
19213569
],
"osm_id": "2345",
"point": {
"x": 27.5227942,
"y": 64.25206634443111
},
"title_anchors": [
{ "min_zoom": 12, "anchor": 0 },
{ "min_zoom": 15, "anchor": 9 }]
}
]})";
vector<Stop> const expected = {
Stop(343259523 /* id */, 1234 /* osm id */, 1 /* feature id */, kInvalidTransferId /* transfer id */,
{19207936, 19207937} /* lineIds */, {27.4970954, 64.20146835878187} /* point */, {} /* anchors */),
Stop(266680843 /* id */, 2345 /* osm id */, 2 /* feature id */, 5 /* transfer id */,
{19213568, 19213569} /* line ids */, {27.5227942, 64.25206634443111} /* point */,
{TitleAnchor(12 /* min zoom */, 0 /* anchor */), TitleAnchor(15, 9)})};
OsmIdToFeatureIdsMap mapping;
mapping[base::GeoObjectId(1234)] = vector<FeatureId>({1});
mapping[base::GeoObjectId(2345)] = vector<FeatureId>({2});
TestDeserializerFromJson(jsonBuffer, mapping, "stops", expected);
}
UNIT_TEST(DeserializerFromJson_Gates)
{
string const jsonBuffer = R"(
{
"gates": [
{
"entrance": true,
"exit": true,
"osm_id": "46116860",
"point": {
"x": 43.8594864,
"y": 68.33320554776377
},
"stop_ids": [ 442018474 ],
"weight": 60
},
{
"entrance": true,
"exit": true,
"osm_id": "18446744073709551615",
"point": {
"x": 43.9290544,
"y": 68.41120791512581
},
"stop_ids": [ 442018465 ],
"weight": 60
}
]})";
vector<Gate> const expected = {
Gate(46116860 /* osm id */, 0 /* feature id */, true /* entrance */, true /* exit */, 60 /* weight */,
{442018474} /* stop ids */, {43.8594864, 68.33320554776377} /* point */),
Gate(18446744073709551615ULL /* osm id */, 2 /* feature id */, true /* entrance */, true /* exit */,
60 /* weight */, {442018465} /* stop ids */, {43.9290544, 68.41120791512581} /* point */)};
OsmIdToFeatureIdsMap mapping;
mapping[base::GeoObjectId(46116860)] = vector<FeatureId>({0});
// Note. std::numeric_limits<uint64_t>::max() == 18446744073709551615
mapping[base::GeoObjectId(18446744073709551615ULL)] = vector<FeatureId>({2});
TestDeserializerFromJson(jsonBuffer, mapping, "gates", expected);
}
UNIT_TEST(DeserializerFromJson_Edges)
{
string const jsonBuffer = R"(
{
"edges": [
{
"stop2_id": 442018445,
"line_id": 72551680,
"shape_ids": [
{
"stop1_id": 209186407,
"stop2_id": 209186410
},
{
"stop1_id": 209186408,
"stop2_id": 209186411
}
],
"stop1_id": 442018444,
"transfer": false
},
{
"stop2_id": 442018446,
"line_id": 72551680,
"shape_ids": [],
"weight" : 345,
"stop1_id": 442018445,
"transfer": false
}
]})";
vector<Edge> const expected = {
Edge(442018444 /* stop 1 id */, 442018445 /* stop 2 id */, kInvalidWeight /* weight */, 72551680 /* line id */,
false /* transfer */, {ShapeId(209186407, 209186410), ShapeId(209186408, 209186411)}),
Edge(442018445 /* stop 1 id */, 442018446 /* stop 2 id */, 345 /* weight */, 72551680 /* line id */,
false /* transfer */, {} /* shape ids */)};
TestDeserializerFromJson(jsonBuffer, "edges", expected);
}
UNIT_TEST(DeserializerFromJson_Transfers)
{
string const jsonBuffer = R"(
{
"transfers": [
{
"id": 922337203,
"point": {
"x": 27.5619844,
"y": 64.24325959173672
},
"stop_ids": [
209186416,
277039518
]
}
]})";
vector<Transfer> const expected = {Transfer(922337203 /* stop id */, {27.5619844, 64.24325959173672} /* point */,
{209186416, 277039518} /* stopIds */, {} /* anchors */)};
TestDeserializerFromJson(jsonBuffer, "transfers", expected);
}
UNIT_TEST(DeserializerFromJson_Lines)
{
string const jsonBuffer = R"(
{
"lines": [
{
"id": 19207936,
"interval": 150,
"network_id": 2,
"number": "1",
"stop_ids": [
343262691,
343259523,
343252898,
209191847,
2947858576
],
"title": "Московская линия",
"type": "subway",
"color": "green"
},
{
"id": 19207937,
"interval": 150,
"network_id": 2,
"number": "2",
"stop_ids": [
246659391,
246659390,
209191855,
209191854,
209191853,
209191852,
209191851
],
"title": "Московская линия",
"type": "subway",
"color": "red"
}
]})";
vector<Line> const expected = {
Line(19207936 /* line id */, "1" /* number */, "Московская линия" /* title */, "subway" /* type */,
"green" /* color */, 2 /* network id */,
{{343262691, 343259523, 343252898, 209191847, 2947858576}} /* stop ids */, 150 /* interval */),
Line(19207937 /* line id */, "2" /* number */, "Московская линия" /* title */, "subway" /* type */,
"red" /* color */, 2 /* network id */,
{{246659391, 246659390, 209191855, 209191854, 209191853, 209191852, 209191851}} /* stop ids */,
150 /* interval */)};
TestDeserializerFromJson(jsonBuffer, "lines", expected);
}
UNIT_TEST(DeserializerFromJson_Shapes)
{
string const jsonBuffer = R"(
{
"shapes": [
{
"id": {
"stop1_id": 209186424,
"stop2_id": 248520179
},
"polyline": [
{
"x": 27.5762295,
"y": 64.256768574044699
},
{
"x": 27.576325736220355,
"y": 64.256879325696005
},
{
"x": 27.576420780761875,
"y": 64.256990221238539
},
{
"x": 27.576514659541523,
"y": 64.257101255242176
}
]
},
{
"id": {
"stop1_id": 209191850,
"stop2_id": 209191851
},
"polyline": [
{
"x": 27.554025800000002,
"y": 64.250591911669844
},
{
"x": 27.553906184631536,
"y": 64.250633404586054
}
]
}
]})";
vector<Shape> const expected = {
Shape(ShapeId(209186424 /* stop 1 id */, 248520179 /* stop 2 id */),
{m2::PointD(27.5762295, 64.256768574044699), m2::PointD(27.576325736220355, 64.256879325696005),
m2::PointD(27.576420780761875, 64.256990221238539),
m2::PointD(27.576514659541523, 64.257101255242176)} /* polyline */),
Shape(ShapeId(209191850 /* stop 1 id */, 209191851 /* stop 2 id */),
{m2::PointD(27.554025800000002, 64.250591911669844),
m2::PointD(27.553906184631536, 64.250633404586054)} /* polyline */)};
TestDeserializerFromJson(jsonBuffer, "shapes", expected);
}
UNIT_TEST(DeserializerFromJson_Networks)
{
string const jsonBuffer = R"(
{
"networks": [
{
"id": 2,
"title": "Минский метрополитен"
}
]})";
vector<Network> const expected = {Network(2 /* network id */, "Минский метрополитен" /* title */)};
TestDeserializerFromJson(jsonBuffer, "networks", expected);
}
} // namespace

View file

@ -0,0 +1,255 @@
#include "testing/testing.hpp"
#include "transit/transit_schedule.hpp"
#include "base/assert.hpp"
#include <array>
#include <cstdint>
#include <string>
#include "3party/just_gtfs/just_gtfs.h"
namespace transit_schedule_tests
{
using namespace ::transit;
// String dates are provided in GTFS date format YYYYMMDD.
// String times are provided in GTFS time format HH:MM:SS.
uint32_t GetYear(std::string const & date)
{
return std::stoi(date.substr(0, 4));
}
uint32_t GetMonth(std::string const & date)
{
return std::stoi(date.substr(4, 2));
}
uint32_t GetDay(std::string const & date)
{
return std::stoi(date.substr(6));
}
uint32_t GetHour(std::string const & time)
{
return std::stoi(time.substr(0, 2));
}
uint32_t GetMinute(std::string const & time)
{
return std::stoi(time.substr(3, 2));
}
uint32_t GetSecond(std::string const & time)
{
return std::stoi(time.substr(6));
}
gtfs::Frequency GetFrequency(std::string const & startTime, std::string const & endTime, Frequency headwayS)
{
gtfs::Frequency freq;
freq.start_time = gtfs::Time(startTime);
freq.end_time = gtfs::Time(endTime);
freq.headway_secs = headwayS;
return freq;
}
gtfs::CalendarAvailability GetAvailability(bool available)
{
return available ? gtfs::CalendarAvailability::Available : gtfs::CalendarAvailability::NotAvailable;
}
void TestDatesInterval(std::string const & date1, std::string const & date2, WeekSchedule weekDays)
{
CHECK_EQUAL(date1.size(), 8, ());
CHECK_EQUAL(date2.size(), 8, ());
gtfs::CalendarItem ci;
ci.start_date = gtfs::Date(date1);
ci.end_date = gtfs::Date(date2);
ci.sunday = GetAvailability(weekDays[0]);
ci.monday = GetAvailability(weekDays[1]);
ci.tuesday = GetAvailability(weekDays[2]);
ci.wednesday = GetAvailability(weekDays[3]);
ci.thursday = GetAvailability(weekDays[4]);
ci.friday = GetAvailability(weekDays[5]);
ci.saturday = GetAvailability(weekDays[6]);
DatesInterval const interval(ci);
auto const & [intervalDate1, intervalDate2, wd] = interval.Extract();
TEST_EQUAL(intervalDate1.m_year, GetYear(date1), ());
TEST_EQUAL(intervalDate1.m_month, GetMonth(date1), ());
TEST_EQUAL(intervalDate1.m_day, GetDay(date1), ());
TEST_EQUAL(intervalDate2.m_year, GetYear(date2), ());
TEST_EQUAL(intervalDate2.m_month, GetMonth(date2), ());
TEST_EQUAL(intervalDate2.m_day, GetDay(date2), ());
for (size_t i = 0; i < wd.size(); ++i)
TEST_EQUAL(wd[i], weekDays[i], ());
}
void TestDateException(std::string const & date, bool isOpen)
{
gtfs::CalendarDateException const ex =
isOpen ? gtfs::CalendarDateException::Added : gtfs::CalendarDateException::Removed;
DateException const dateException(gtfs::Date(date), ex);
auto const & [exDate, exStatus] = dateException.Extract();
TEST_EQUAL(exDate.m_year, GetYear(date), ());
TEST_EQUAL(exDate.m_month, GetMonth(date), ());
TEST_EQUAL(exDate.m_day, GetDay(date), ());
TEST_EQUAL(exStatus, isOpen, ());
}
void TestTime(std::string const & timePlan, Time const & timeFact)
{
TEST_EQUAL(timeFact.m_hour, GetHour(timePlan), ());
TEST_EQUAL(timeFact.m_minute, GetMinute(timePlan), ());
TEST_EQUAL(timeFact.m_second, GetSecond(timePlan), ());
}
void TestTimeInterval(std::string const & time1, std::string const & time2)
{
CHECK_EQUAL(time1.size(), 8, ());
CHECK_EQUAL(time2.size(), 8, ());
auto const & [startTime, endTime] = TimeInterval(gtfs::Time(time1), gtfs::Time(time2)).Extract();
TestTime(time1, startTime);
TestTime(time2, endTime);
}
UNIT_TEST(TransitSchedule_DatesInterval)
{
TestDatesInterval("20200902", "20210531",
{
false /* sunday */, true /* monday */, false /* tuesday */, true /* wednesday */,
false /* thursday */, false /* friday */, false /* saturday */
});
TestDatesInterval("20201101", "20201130",
{
true /* sunday */, false /* monday */, false /* tuesday */, false /* wednesday */,
false /* thursday */, false /* friday */, true /* saturday */
});
TestDatesInterval("20210101", "20210228",
{
false /* sunday */, true /* monday */, true /* tuesday */, true /* wednesday */,
true /* thursday */, true /* friday */, false /* saturday */
});
TestDatesInterval("20220101", "20240101",
{
false /* sunday */, false /* monday */, false /* tuesday */, false /* wednesday */,
true /* thursday */, true /* friday */, true /* saturday */
});
}
UNIT_TEST(TransitSchedule_DateException)
{
TestDateException("20210316", true);
TestDateException("20210101", false);
TestDateException("20221231", true);
TestDateException("20221231", false);
}
UNIT_TEST(TransitSchedule_TimeInterval)
{
TestTimeInterval("14:30:00", "14:30:30");
TestTimeInterval("00:00:00", "21:45:40");
TestTimeInterval("07:10:30", "11:25:00");
TestTimeInterval("13:00:00", "13:50:00");
TestTimeInterval("23:40:50", "23:59:59");
}
UNIT_TEST(TransitSchedule_FrequencyIntervals)
{
gtfs::Frequencies const frequencies{GetFrequency("14:40:00", "15:55:30", 600),
GetFrequency("15:55:40", "17:00:00", 1200),
GetFrequency("21:10:20", "22:45:40", 300)};
FrequencyIntervals const intervals(frequencies);
TEST_EQUAL(intervals.GetFrequency(Time(13, 15, 30)), kDefaultFrequency, ());
TEST_EQUAL(intervals.GetFrequency(Time(14, 55, 0)), 600, ());
TEST_EQUAL(intervals.GetFrequency(Time(15, 0, 55)), 600, ());
TEST_EQUAL(intervals.GetFrequency(Time(15, 55, 39)), kDefaultFrequency, ());
TEST_EQUAL(intervals.GetFrequency(Time(15, 55, 40)), 1200, ());
TEST_EQUAL(intervals.GetFrequency(Time(16, 0, 0)), 1200, ());
TEST_EQUAL(intervals.GetFrequency(Time(21, 50, 0)), 300, ());
TEST_EQUAL(intervals.GetFrequency(Time(22, 14, 20)), 300, ());
TEST_EQUAL(intervals.GetFrequency(Time(22, 50, 10)), kDefaultFrequency, ());
}
UNIT_TEST(TransitSchedule_Schedule_DatesInterval_Status)
{
gtfs::CalendarItem calendarItem1;
calendarItem1.start_date = gtfs::Date("20200401");
calendarItem1.end_date = gtfs::Date("20201120");
calendarItem1.friday = gtfs::CalendarAvailability::Available;
calendarItem1.saturday = gtfs::CalendarAvailability::Available;
gtfs::CalendarItem calendarItem2;
calendarItem2.start_date = gtfs::Date("20201121");
calendarItem2.end_date = gtfs::Date("20201231");
calendarItem2.wednesday = gtfs::CalendarAvailability::Available;
gtfs::Frequencies const frequencies1{GetFrequency("12:10:30", "16:00:40", 4500),
GetFrequency("18:00:00", "19:00:00", 1800)};
gtfs::Frequencies const frequencies2{GetFrequency("07:25:00", "12:10:00", 2700)};
Schedule busSchedule;
busSchedule.AddDatesInterval(calendarItem1, frequencies1);
busSchedule.AddDatesInterval(calendarItem2, frequencies2);
// 11.04.2020, saturday.
TEST_EQUAL(busSchedule.GetStatus(time_t(1586600488)), Status::Open, ());
// 12.04.2020, sunday.
TEST_EQUAL(busSchedule.GetStatus(time_t(1586686888)), Status::Closed, ());
// 07.08.2020, friday.
TEST_EQUAL(busSchedule.GetStatus(time_t(1596795688)), Status::Open, ());
// 08.08.2020, saturday.
TEST_EQUAL(busSchedule.GetStatus(time_t(1596882088)), Status::Open, ());
// 09.08.2020, sunday.
TEST_EQUAL(busSchedule.GetStatus(time_t(1596968488)), Status::Closed, ());
}
UNIT_TEST(TransitSchedule_Schedule_DateException_Status)
{
gtfs::Frequencies const emptyFrequencies;
Schedule busSchedule;
busSchedule.AddDateException(gtfs::Date("20200606"), gtfs::CalendarDateException::Added, emptyFrequencies);
busSchedule.AddDateException(gtfs::Date("20200607"), gtfs::CalendarDateException::Removed, emptyFrequencies);
busSchedule.AddDateException(gtfs::Date("20211029"), gtfs::CalendarDateException::Added, emptyFrequencies);
busSchedule.AddDateException(gtfs::Date("20211128"), gtfs::CalendarDateException::Removed, emptyFrequencies);
// 06.06.2020.
TEST(busSchedule.GetStatus(time_t(1591438888)) == Status::Open, ());
// 07.06.2020.
TEST(busSchedule.GetStatus(time_t(1591525288)) == Status::Closed, ());
// 29.10.2021.
TEST(busSchedule.GetStatus(time_t(1635502888)) == Status::Open, ());
// 28.11.2021.
TEST(busSchedule.GetStatus(time_t(1638094888)) == Status::Closed, ());
// 01.03.2021.
TEST(busSchedule.GetStatus(time_t(1614594088)) == Status::Unknown, ());
// 01.01.2020.
TEST(busSchedule.GetStatus(time_t(1577874088)) == Status::Unknown, ());
}
} // namespace transit_schedule_tests

View file

@ -0,0 +1,308 @@
#include "testing/testing.hpp"
#include "transit/transit_serdes.hpp"
#include "transit/transit_types.hpp"
#include "transit/transit_version.hpp"
#include "coding/reader.hpp"
#include "coding/writer.hpp"
#include <algorithm>
#include <cstdint>
#include <vector>
using namespace routing;
using namespace routing::transit;
using namespace std;
namespace routing
{
namespace transit
{
auto constexpr kTransitHeaderVersion = static_cast<uint16_t>(::transit::TransitVersion::OnlySubway);
template <class S, class D, class Obj>
void TestCommonSerialization(Obj const & obj)
{
vector<uint8_t> buffer;
MemWriter<vector<uint8_t>> writer(buffer);
S serializer(writer);
serializer(obj);
MemReader reader(buffer.data(), buffer.size());
ReaderSource<MemReader> src(reader);
Obj deserializedObj;
D deserializer(src);
deserializer(deserializedObj);
TEST(obj.IsEqualForTesting(deserializedObj), (obj, deserializedObj));
}
void TestSerialization(TransitHeader const & header)
{
TestCommonSerialization<FixedSizeSerializer<MemWriter<vector<uint8_t>>>,
FixedSizeDeserializer<ReaderSource<MemReader>>>(header);
}
template <class Obj>
void TestSerialization(Obj const & obj)
{
TestCommonSerialization<Serializer<MemWriter<vector<uint8_t>>>, Deserializer<ReaderSource<MemReader>>>(obj);
}
UNIT_TEST(Transit_HeaderRewriting)
{
TransitHeader const bigHeader(kTransitHeaderVersion /* version */, 500 /* stopsOffset */, 1000 /* gatesOffset */,
200000 /* edgesOffset */, 300000 /* transfersOffset */, 400000 /* linesOffset */,
5000000 /* shapesOffset */, 6000000 /* networksOffset */, 700000000 /* endOffset */);
TransitHeader header;
vector<uint8_t> buffer;
MemWriter<vector<uint8_t>> writer(buffer);
// Writing.
FixedSizeSerializer<MemWriter<vector<uint8_t>>> serializer(writer);
serializer(header);
auto const endOffset = writer.Pos();
// Rewriting.
header = bigHeader;
writer.Seek(0 /* start offset */);
serializer(header);
TEST_EQUAL(writer.Pos(), endOffset, ());
// Reading.
MemReader reader(buffer.data(), buffer.size());
ReaderSource<MemReader> src(reader);
TransitHeader deserializedHeader;
FixedSizeDeserializer<ReaderSource<MemReader>> deserializer(src);
deserializer(deserializedHeader);
TEST(deserializedHeader.IsEqualForTesting(bigHeader), (deserializedHeader, bigHeader));
}
} // namespace transit
} // namespace routing
namespace
{
UNIT_TEST(Transit_CheckValidSortedUnique)
{
vector<Network> const networks = {Network(1 /* id */, "Title 1"), Network(2 /* id */, "Title 2")};
CheckValidSortedUnique(networks, "networks");
vector<ShapeId> const shapeIds = {ShapeId(1, 2), ShapeId(1, 3)};
CheckValidSortedUnique(shapeIds, "shapeIds");
}
UNIT_TEST(Transit_HeaderSerialization)
{
{
TransitHeader header;
TestSerialization(header);
TEST(header.IsValid(), (header));
}
{
TransitHeader header(kTransitHeaderVersion /* version */, 500 /* stopsOffset */, 1000 /* gatesOffset */,
2000 /* edgesOffset */, 3000 /* transfersOffset */, 4000 /* linesOffset */,
5000 /* shapesOffset */, 6000 /* networksOffset */, 7000 /* endOffset */);
TestSerialization(header);
TEST(header.IsValid(), (header));
}
}
UNIT_TEST(Transit_TransitHeaderValidity)
{
{
TransitHeader header;
TEST(header.IsValid(), (header));
}
{
TransitHeader const header(kTransitHeaderVersion /* version */, 40 /* stopsOffset */, 44 /* gatesOffset */,
48 /* edgesOffset */, 52 /* transfersOffset */, 56 /* linesOffset */,
60 /* shapesOffset */, 64 /* networksOffset */, 68 /* endOffset */);
TEST(header.IsValid(), (header));
}
{
TransitHeader const header(kTransitHeaderVersion /* version */, 44 /* stopsOffset */, 40 /* gatesOffset */,
48 /* edgesOffset */, 52 /* transfersOffset */, 56 /* linesOffset */,
60 /* shapesOffset */, 64 /* networksOffset */, 68 /* endOffset */);
TEST(!header.IsValid(), (header));
}
}
UNIT_TEST(Transit_TitleAnchorSerialization)
{
{
TitleAnchor anchor(17 /* min zoom */, 0 /* anchor */);
TestSerialization(anchor);
TEST(anchor.IsValid(), (anchor));
}
{
TitleAnchor anchor(10 /* min zoom */, 2 /* anchor */);
TestSerialization(anchor);
TEST(anchor.IsValid(), (anchor));
}
{
TitleAnchor anchor(18 /* min zoom */, 7 /* anchor */);
TestSerialization(anchor);
TEST(anchor.IsValid(), (anchor));
}
}
UNIT_TEST(Transit_StopSerialization)
{
{
Stop stop;
TestSerialization(stop);
TEST(!stop.IsValid(), (stop));
}
{
Stop stop(1234 /* id */, 1234567 /* osm id */, 5678 /* feature id */, 7 /* transfer id */,
{7, 8, 9, 10} /* line id */, {55.0, 37.0} /* point */, {} /* anchors */);
TestSerialization(stop);
TEST(stop.IsValid(), (stop));
}
}
UNIT_TEST(Transit_SingleMwmSegmentSerialization)
{
{
SingleMwmSegment s(12344 /* feature id */, 0 /* segmentIdx */, true /* forward */);
TestSerialization(s);
TEST(s.IsValid(), (s));
}
{
SingleMwmSegment s(12544 /* feature id */, 5 /* segmentIdx */, false /* forward */);
TestSerialization(s);
TEST(s.IsValid(), (s));
}
}
UNIT_TEST(Transit_GateSerialization)
{
Gate gate(12345678 /* osm id */, 12345 /* feature id */, true /* entrance */, false /* exit */, 117 /* weight */,
{1, 2, 3} /* stop ids */, {30.0, 50.0} /* point */);
TestSerialization(gate);
TEST(gate.IsValid(), (gate));
}
UNIT_TEST(Transit_GatesRelational)
{
vector<Gate> const gates = {{1234567 /* osm id */,
123 /* feature id */,
true /* entrance */,
false /* exit */,
1 /* weight */,
{1, 2, 3} /* stops ids */,
m2::PointD(0.0, 0.0)},
{12345678 /* osm id */,
1234 /* feature id */,
true /* entrance */,
false /* exit */,
1 /* weight */,
{1, 2, 3} /* stops ids */,
m2::PointD(0.0, 0.0)},
{12345678 /* osm id */,
1234 /* feature id */,
true /* entrance */,
false /* exit */,
1 /* weight */,
{1, 2, 3, 4} /* stops ids */,
m2::PointD(0.0, 0.0)}};
TEST(is_sorted(gates.cbegin(), gates.cend()), ());
}
UNIT_TEST(Transit_EdgeSerialization)
{
{
Edge edge(1 /* start stop id */, 2 /* finish stop id */, 123 /* weight */, 11 /* line id */, false /* transfer */,
{ShapeId(1, 2), ShapeId(3, 4), ShapeId(5, 6)} /* shape ids */);
TestSerialization(edge);
TEST(edge.IsValid(), (edge));
}
{
Edge edge(1 /* start stop id */, 2 /* finish stop id */, 123 /* weight */, 11 /* line id */, false /* transfer */,
{ShapeId(1, 2)} /* shape ids */);
TestSerialization(edge);
TEST(edge.IsValid(), (edge));
}
{
Edge edge(1 /* start stop id */, 2 /* finish stop id */, 123 /* weight */, 11 /* line id */, false /* transfer */,
{ShapeId(2, 1)} /* shape ids */);
TestSerialization(edge);
TEST(edge.IsValid(), (edge));
}
{
Edge edge(1 /* start stop id */, 2 /* finish stop id */, 123 /* weight */, kInvalidLineId, true /* transfer */,
{} /* shape ids */);
TestSerialization(edge);
TEST(edge.IsValid(), (edge));
}
{
Edge edge(1 /* start stop id */, 2 /* finish stop id */, 123 /* weight */, 11 /* line id */, true /* transfer */,
{} /* shape ids */);
TestSerialization(edge);
// Note. A transfer edge (transfer == true) with a valid line id is not allowable.
TEST(!edge.IsValid(), (edge));
}
}
UNIT_TEST(Transit_TransferSerialization)
{
Transfer transfer(1 /* id */, {40.0, 35.0} /* point */, {1, 2, 3} /* stop ids */, {TitleAnchor(16, 0 /* anchor */)});
TestSerialization(transfer);
TEST(transfer.IsValid(), (transfer));
}
UNIT_TEST(Transit_LineSerialization)
{
{
Line line(1 /* line id */, "2" /* number */, "Линия" /* title */, "subway" /* type */, "red" /* color */,
3 /* network id */, {{1}} /* stop ids */, 10 /* interval */);
TestSerialization(line);
TEST(line.IsValid(), (line));
}
{
Line line(10 /* line id */, "11" /* number */, "Линия" /* title */, "subway" /* type */, "green" /* color */,
12 /* network id */, {{13, 14, 15}} /* stop ids */, 15 /* interval */);
TestSerialization(line);
TEST(line.IsValid(), (line));
}
{
Line line(100 /* line id */, "101" /* number */, "Линия" /* title */, "subway" /* type */, "blue" /* color */,
103 /* network id */, {{1, 2, 3}, {7, 8, 9}} /* stop ids */, 15 /* interval */);
TestSerialization(line);
TEST(line.IsValid(), (line));
}
}
UNIT_TEST(Transit_ShapeSerialization)
{
{
Shape shape(ShapeId(10, 20), {m2::PointD(0.0, 20.0), m2::PointD(0.0, 0.0)} /* polyline */);
TestSerialization(shape);
TEST(shape.IsValid(), (shape));
}
{
Shape shape(ShapeId(11, 21),
{m2::PointD(20.0, 20.0), m2::PointD(21.0, 21.0), m2::PointD(22.0, 22.0)} /* polyline */);
TestSerialization(shape);
TEST(shape.IsValid(), (shape));
}
}
UNIT_TEST(Transit_ShapeIdRelational)
{
vector<ShapeId> const ids = {{0, 10}, {0, 11}, {1, 10}, {1, 11}};
TEST(is_sorted(ids.cbegin(), ids.cend()), ());
}
UNIT_TEST(Transit_NetworkSerialization)
{
Network network(0 /* network id */, "Title" /* title */);
TestSerialization(network);
TEST(network.IsValid(), (network));
}
} // namespace

View file

@ -0,0 +1,107 @@
#pragma once
#include "testing/testing.hpp"
#include "transit/experimental/transit_types_experimental.hpp"
#include "geometry/point2d.hpp"
#include <cstddef>
#include <tuple>
#include <vector>
namespace routing
{
namespace transit
{
template <typename Obj>
void TestForEquivalence(std::vector<Obj> const & actual, std::vector<Obj> const & expected)
{
TEST_EQUAL(actual.size(), expected.size(), ());
for (size_t i = 0; i < actual.size(); ++i)
TEST(actual[i].IsEqualForTesting(expected[i]), (i, actual[i], expected[i]));
}
} // namespace transit
} // namespace routing
namespace transit
{
namespace experimental
{
double constexpr kPointsEqualEpsilon = 1e-5;
inline bool Equal(SingleMwmSegment const & s1, SingleMwmSegment const & s2)
{
return std::make_tuple(s1.GetFeatureId(), s1.GetSegmentIdx(), s1.IsForward()) ==
std::make_tuple(s2.GetFeatureId(), s2.GetSegmentIdx(), s2.IsForward());
}
inline bool Equal(Network const & n1, Network const & n2)
{
return std::make_tuple(n1.GetId(), n1.GetTitle()) == std::make_tuple(n2.GetId(), n2.GetTitle());
}
inline bool Equal(Route const & r1, Route const & r2)
{
return std::make_tuple(r1.GetId(), r1.GetNetworkId(), r1.GetType(), r1.GetTitle(), r1.GetColor()) ==
std::make_tuple(r2.GetId(), r2.GetNetworkId(), r2.GetType(), r2.GetTitle(), r2.GetColor());
}
inline bool Equal(Line const & l1, Line const & l2)
{
return std::make_tuple(l1.GetId(), l1.GetRouteId(), l1.GetTitle(), l1.GetStopIds(), l1.GetSchedule(),
l1.GetShapeLink()) == std::make_tuple(l2.GetId(), l2.GetRouteId(), l2.GetTitle(),
l2.GetStopIds(), l2.GetSchedule(), l2.GetShapeLink());
}
inline bool Equal(LineMetadata const & lm1, LineMetadata const & lm2)
{
return std::make_tuple(lm1.GetId(), lm1.GetLineSegmentsOrder()) ==
std::make_tuple(lm2.GetId(), lm2.GetLineSegmentsOrder());
}
inline bool Equal(Stop const & s1, Stop const & s2)
{
return (std::make_tuple(s1.GetId(), s1.GetFeatureId(), s1.GetOsmId(), s1.GetTitle(), s1.GetTimeTable(),
s1.GetTransferIds(), s1.GetBestPedestrianSegments()) ==
std::make_tuple(s2.GetId(), s2.GetFeatureId(), s2.GetOsmId(), s2.GetTitle(), s2.GetTimeTable(),
s2.GetTransferIds(), s2.GetBestPedestrianSegments())) &&
AlmostEqualAbs(s1.GetPoint(), s2.GetPoint(), kPointsEqualEpsilon);
}
inline bool Equal(Gate const & g1, Gate const & g2)
{
return (std::make_tuple(g1.GetId(), g1.GetFeatureId(), g1.GetOsmId(), g1.IsEntrance(), g1.IsExit(),
g1.GetStopsWithWeight(), g1.GetBestPedestrianSegments()) ==
std::make_tuple(g2.GetId(), g2.GetFeatureId(), g2.GetOsmId(), g2.IsEntrance(), g2.IsExit(),
g2.GetStopsWithWeight(), g2.GetBestPedestrianSegments())) &&
AlmostEqualAbs(g1.GetPoint(), g2.GetPoint(), kPointsEqualEpsilon);
}
inline bool Equal(Edge const & e1, Edge const & e2)
{
return (std::make_tuple(e1.GetStop1Id(), e1.GetStop2Id(), e1.GetLineId(), e1.GetWeight(), e1.IsTransfer(),
e1.GetShapeLink()) == std::make_tuple(e2.GetStop1Id(), e2.GetStop2Id(), e2.GetLineId(),
e2.GetWeight(), e2.IsTransfer(), e2.GetShapeLink()));
}
inline bool Equal(Transfer const & t1, Transfer const & t2)
{
return (std::make_tuple(t1.GetId(), t1.GetStopIds()) == std::make_tuple(t2.GetId(), t2.GetStopIds())) &&
AlmostEqualAbs(t1.GetPoint(), t2.GetPoint(), kPointsEqualEpsilon);
}
inline bool Equal(Shape const & s1, Shape const & s2)
{
return std::make_tuple(s1.GetId(), s1.GetPolyline()) == std::make_tuple(s2.GetId(), s2.GetPolyline());
}
template <class Item>
void TestEqual(std::vector<Item> const & actual, std::vector<Item> const & expected)
{
TEST_EQUAL(actual.size(), expected.size(), ());
for (size_t i = 0; i < actual.size(); ++i)
TEST(Equal(actual[i], expected[i]), (i, actual[i], expected[i]));
}
} // namespace experimental
} // namespace transit