Skip to content

Commit 6c74fab

Browse files
authored
Merge pull request #354 from physycom/test
Add some tests
2 parents d3fbe01 + 21fd056 commit 6c74fab

File tree

9 files changed

+553
-68
lines changed

9 files changed

+553
-68
lines changed

src/dsf/dsf.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
static constexpr uint8_t DSF_VERSION_MAJOR = 3;
88
static constexpr uint8_t DSF_VERSION_MINOR = 14;
9-
static constexpr uint8_t DSF_VERSION_PATCH = 0;
9+
static constexpr uint8_t DSF_VERSION_PATCH = 1;
1010

1111
static auto const DSF_VERSION =
1212
std::format("{}.{}.{}", DSF_VERSION_MAJOR, DSF_VERSION_MINOR, DSF_VERSION_PATCH);

src/dsf/headers/Geometry.hpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ namespace dsf {
1818
double m_y;
1919

2020
public:
21-
Point() = default;
2221
/// @brief Construct a Point with given x and y coordinates.
2322
/// @param x The x coordinate
2423
/// @param y The y coordinate
@@ -65,7 +64,7 @@ struct std::formatter<dsf::geometry::Point> {
6564
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
6665

6766
template <typename FormatContext>
68-
auto format(dsf::geometry::Point const& point, FormatContext&& ctx) {
67+
auto format(dsf::geometry::Point const& point, FormatContext& ctx) const {
6968
return std::format_to(ctx.out(), "POINT ({}, {})", point.x(), point.y());
7069
}
7170
};
@@ -75,7 +74,7 @@ struct fmt::formatter<dsf::geometry::Point> {
7574
constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); }
7675

7776
template <typename FormatContext>
78-
auto format(dsf::geometry::Point const& point, FormatContext& ctx) const {
77+
auto format(dsf::geometry::Point const& point, FormatContext&& ctx) const {
7978
return fmt::format_to(ctx.out(), "POINT ({}, {})", point.x(), point.y());
8079
}
8180
};
@@ -85,7 +84,7 @@ struct std::formatter<dsf::geometry::PolyLine> {
8584
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
8685

8786
template <typename FormatContext>
88-
auto format(dsf::geometry::PolyLine const& polyline, FormatContext&& ctx) {
87+
auto format(dsf::geometry::PolyLine const& polyline, FormatContext&& ctx) const {
8988
auto out = std::format_to(ctx.out(), "LINESTRING (");
9089
for (std::size_t i = 0; i < polyline.size(); ++i) {
9190
if (i > 0) {

src/dsf/headers/Intersection.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,20 +115,20 @@ struct std::formatter<dsf::Intersection> {
115115
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
116116

117117
template <typename FormatContext>
118-
auto format(dsf::Intersection const& intersection, FormatContext&& ctx) {
119-
return std::format_to(
118+
auto format(dsf::Intersection const& intersection, FormatContext&& ctx) const {
119+
auto out = std::format_to(
120120
ctx.out(),
121121
"Intersection(id: {}, name: {}, capacity: {}, transportCapacity: {}, "
122-
"nAgents: {}, coords: {})",
122+
"nAgents: {}, coords: ",
123123
intersection.id(),
124124
intersection.name(),
125125
intersection.capacity(),
126126
intersection.transportCapacity(),
127127
intersection.nAgents());
128128
if (intersection.geometry().has_value()) {
129-
return std::format_to(ctx.out(), "{})", *intersection.geometry());
129+
return std::format_to(out, "{})", *intersection.geometry());
130130
} else {
131-
return std::format_to(ctx.out(), "N/A)");
131+
return std::format_to(out, "N/A)");
132132
}
133133
}
134134
};

src/dsf/sources/Geometry.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ namespace dsf {
1515
}
1616
std::string coordStr = strPoint.substr(start + 1, end - start - 1);
1717
std::istringstream coordStream(coordStr);
18-
coordStream >> m_x >> m_y;
18+
double x, y;
19+
if (!(coordStream >> x >> y)) {
20+
throw std::invalid_argument("Malformed WKT POINT coordinates: " + strPoint);
21+
}
22+
m_x = x;
23+
m_y = y;
1924
} else {
2025
throw std::invalid_argument("Unsupported format: " + format);
2126
}
@@ -37,7 +42,15 @@ namespace dsf {
3742
while (std::getline(coordsStream, pointStr, ',')) {
3843
std::istringstream pointStream(pointStr);
3944
double x, y;
40-
pointStream >> x >> y;
45+
std::string extra;
46+
if (!(pointStream >> x >> y)) {
47+
throw std::invalid_argument("Malformed WKT LINESTRING point: " + pointStr);
48+
}
49+
// Should not be any extra tokens after two numbers
50+
if (pointStream >> extra) {
51+
throw std::invalid_argument("Too many values in WKT LINESTRING point: " +
52+
pointStr);
53+
}
4154
this->push_back(Point{x, y});
4255
}
4356
} else {

src/dsf/sources/Intersection.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace dsf {
88
void Intersection::setCapacity(Size capacity) {
99
if (capacity < m_agents.size()) {
10-
throw std::logic_error(std::format(
10+
throw std::runtime_error(std::format(
1111
"Intersection capacity ({}) is smaller than the current queue size ({}).",
1212
capacity,
1313
m_agents.size()));
@@ -16,7 +16,9 @@ namespace dsf {
1616
}
1717

1818
void Intersection::addAgent(double angle, std::unique_ptr<Agent> pAgent) {
19-
assert(!isFull());
19+
if (isFull()) {
20+
throw std::runtime_error(std::format("{} is full.", *this));
21+
}
2022
auto iAngle{static_cast<int16_t>(angle * 100)};
2123
m_agents.emplace(iAngle, std::move(pAgent));
2224
++m_agentCounter;

test/Test_geometry.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include "../src/dsf/headers/Geometry.hpp"
2+
#include "doctest.h"
3+
#include <string>
4+
5+
using namespace dsf::geometry;
6+
7+
TEST_CASE("Point constructors and equality") {
8+
SUBCASE("Double constructor") {
9+
Point p(1.5, -2.3);
10+
CHECK_EQ(p.x(), 1.5);
11+
CHECK_EQ(p.y(), -2.3);
12+
}
13+
SUBCASE("String constructor WKT") {
14+
Point p("POINT(3.2 4.5)");
15+
CHECK_EQ(p.x(), 3.2);
16+
CHECK_EQ(p.y(), 4.5);
17+
}
18+
SUBCASE("Equality operator") {
19+
Point p1(1.0, 2.0);
20+
Point p2(1.0, 2.0);
21+
Point p3(1.0, 2.0000001);
22+
CHECK(p1 == p2);
23+
CHECK_FALSE(p1 == p3);
24+
}
25+
}
26+
27+
TEST_CASE("PolyLine constructors and parsing") {
28+
SUBCASE("Default constructor") {
29+
PolyLine pl;
30+
CHECK(pl.empty());
31+
}
32+
SUBCASE("Empty WKT LINESTRING") {
33+
auto emptyPoly = PolyLine("LINESTRING()");
34+
CHECK(emptyPoly.empty());
35+
}
36+
SUBCASE("Initializer list constructor") {
37+
PolyLine pl{Point(1, 2), Point(3, 4)};
38+
CHECK_EQ(pl.size(), 2);
39+
CHECK_EQ(pl[0].x(), 1);
40+
CHECK_EQ(pl[0].y(), 2);
41+
CHECK_EQ(pl[1].x(), 3);
42+
CHECK_EQ(pl[1].y(), 4);
43+
}
44+
SUBCASE("WKT LINESTRING constructor") {
45+
PolyLine pl("LINESTRING(1 2, 3 4, 5 6)");
46+
CHECK_EQ(pl.size(), 3);
47+
CHECK_EQ(pl[0].x(), 1);
48+
CHECK_EQ(pl[0].y(), 2);
49+
CHECK_EQ(pl[1].x(), 3);
50+
CHECK_EQ(pl[1].y(), 4);
51+
CHECK_EQ(pl[2].x(), 5);
52+
CHECK_EQ(pl[2].y(), 6);
53+
}
54+
SUBCASE("WKT LINESTRING with extra spaces") {
55+
PolyLine pl("LINESTRING( 1 2 , 3 4 , 5 6 )");
56+
CHECK_EQ(pl.size(), 3);
57+
CHECK_EQ(pl[0].x(), 1);
58+
CHECK_EQ(pl[0].y(), 2);
59+
CHECK_EQ(pl[1].x(), 3);
60+
CHECK_EQ(pl[1].y(), 4);
61+
CHECK_EQ(pl[2].x(), 5);
62+
CHECK_EQ(pl[2].y(), 6);
63+
}
64+
SUBCASE("Invalid WKT format throws") {
65+
CHECK_THROWS_AS(PolyLine("LINESTRING(1,2,3,4)"), std::invalid_argument);
66+
CHECK_THROWS_AS(PolyLine("LINESTRING(1 2 3 4)"), std::invalid_argument);
67+
}
68+
}

test/Test_itinerary.cpp

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,89 @@ using Itinerary = dsf::Itinerary;
88

99
TEST_CASE("Itinerary") {
1010
SUBCASE("Constructors") {
11-
GIVEN("Some parameters") {
12-
dsf::Id itineraryId{0};
13-
dsf::Id destinationId{2};
14-
WHEN("The Itinerary is constructed") {
15-
Itinerary itinerary{itineraryId, destinationId};
16-
THEN("The source and destination are set correctly") {
17-
CHECK_EQ(itinerary.id(), itineraryId);
18-
CHECK_EQ(itinerary.destination(), destinationId);
19-
}
20-
}
11+
dsf::Id itineraryId{0};
12+
dsf::Id destinationId{2};
13+
Itinerary itinerary{itineraryId, destinationId};
14+
CHECK_EQ(itinerary.id(), itineraryId);
15+
CHECK_EQ(itinerary.destination(), destinationId);
16+
}
17+
18+
SUBCASE("Set and get path") {
19+
Itinerary itinerary{1, 42};
20+
std::unordered_map<dsf::Id, std::vector<dsf::Id>> path = {
21+
{1, {2, 3}}, {2, {4}}, {3, {5, 6, 7}}};
22+
itinerary.setPath(path);
23+
auto const& result = itinerary.path();
24+
CHECK_EQ(result.size(), path.size());
25+
for (const auto& [k, v] : path) {
26+
CHECK(result.count(k) == 1);
27+
CHECK(result.at(k) == v);
28+
}
29+
}
30+
31+
SUBCASE("Save and load itinerary") {
32+
Itinerary itinerary{7, 99};
33+
std::unordered_map<dsf::Id, std::vector<dsf::Id>> path = {
34+
{7, {8, 9}}, {8, {10}}, {9, {11, 12}}};
35+
itinerary.setPath(path);
36+
const std::string filename = "test_itinerary.bin";
37+
itinerary.save(filename);
38+
39+
Itinerary loaded{7, 0}; // destination will be overwritten by load
40+
loaded.load(filename);
41+
CHECK_EQ(loaded.destination(), 99);
42+
auto const& loadedPath = loaded.path();
43+
CHECK_EQ(loadedPath.size(), path.size());
44+
for (const auto& [k, v] : path) {
45+
CHECK(loadedPath.count(k) == 1);
46+
CHECK(loadedPath.at(k) == v);
47+
}
48+
// Clean up
49+
std::remove(filename.c_str());
50+
}
51+
52+
SUBCASE("Load from non-existent file throws") {
53+
Itinerary itinerary{1, 2};
54+
CHECK_THROWS_AS(itinerary.load("nonexistent_file.bin"), std::runtime_error);
55+
}
56+
57+
SUBCASE("Empty path") {
58+
Itinerary itinerary{123, 456};
59+
itinerary.setPath({});
60+
CHECK(itinerary.path().empty());
61+
const std::string filename = "test_empty_path.bin";
62+
itinerary.save(filename);
63+
Itinerary loaded{123, 0};
64+
loaded.load(filename);
65+
CHECK(loaded.path().empty());
66+
std::remove(filename.c_str());
67+
}
68+
69+
SUBCASE("Large path") {
70+
Itinerary itinerary{100, 200};
71+
std::unordered_map<dsf::Id, std::vector<dsf::Id>> path;
72+
for (dsf::Id i = 0; i < 100; ++i) {
73+
std::vector<dsf::Id> v;
74+
for (dsf::Id j = 0; j < 10; ++j)
75+
v.push_back(i * 10 + j);
76+
path[i] = v;
77+
}
78+
itinerary.setPath(path);
79+
CHECK_EQ(itinerary.path().size(), 100);
80+
for (dsf::Id i = 0; i < 100; ++i) {
81+
CHECK(itinerary.path().count(i) == 1);
82+
CHECK(itinerary.path().at(i).size() == 10);
83+
}
84+
const std::string filename = "test_large_path.bin";
85+
itinerary.save(filename);
86+
Itinerary loaded{100, 0};
87+
loaded.load(filename);
88+
CHECK_EQ(loaded.path().size(), 100);
89+
for (dsf::Id i = 0; i < 100; ++i) {
90+
CHECK(loaded.path().count(i) == 1);
91+
CHECK(loaded.path().at(i).size() == 10);
92+
CHECK(loaded.path().at(i) == path[i]);
2193
}
94+
std::remove(filename.c_str());
2295
}
2396
}

0 commit comments

Comments
 (0)