#define BOOST_TEST_MODULE network #include "testHelpers.h" #include #include #include "testMainWindow.h" #include #include #include #include #include // IWYU pragma: keep #include #include #include #include #include #include #include #include BOOST_GLOBAL_FIXTURE(TestMainWindowAppBase); struct TestLinkS; struct TestLink : public virtual Link { using StraightLink = TestLinkS; using CurveLink = TestLinkS; }; struct TestLinkS : public TestLink, public LinkStraight { TestLinkS(NetworkLinkHolder & network, const Node::Ptr & a, const Node::Ptr & b) : TestLinkS {network, a, b, (a->pos - b->pos)} { } TestLinkS(NetworkLinkHolder &, Node::Ptr a, Node::Ptr b, RelativePosition2D l) : Link {{std::move(a), 0}, {std::move(b), pi}, glm::length(l)} { } struct Vertex { }; TestLinkS(NetworkLinkHolder &, Node::Ptr a, Node::Ptr b, float l) : Link {{std::move(a), 0}, {std::move(b), pi}, l} { } }; constexpr GlobalPosition3D p000 {0, 0, 500}, p100 {10500, 0, 1000}, p200 {20100, 0, 2000}, p300 {30700, 0, 3000}; constexpr GlobalPosition3D p110 {10300, 10400, 4000}; template<> NetworkLinkHolder::NetworkLinkHolder() = default; struct TestNetwork : public NetworkOf { TestNetwork() : NetworkOf {RESDIR "rails.jpg"} { // 0 1 2 // p000 <-> p100 <-> p200 <-> p300 // \ | / // \ 5 / // 3 | 4 // \-> p110 <-/ addLink(p000, p100, 1.F); addLink(p100, p200, 1.F); addLink(p200, p300, 1.F); addLink(p000, p110, 2.F); addLink(p200, p110, 2.F); addLink(p100, p110, 1.F); } void render(const SceneShader &) const override { } const Surface * getBaseSurface() const override { return nullptr; } RelativeDistance getBaseWidth() const override { return 5'700; } }; const auto VALID_NODES = boost::unit_test::data::make({ p000, p100, p200, p300, }); const auto INVALID_NODES = boost::unit_test::data::make({ {1000, 0, 0}, {0, 1000, 0}, {0, 0, 1000}, }); BOOST_FIXTURE_TEST_SUITE(tn, TestNetwork) BOOST_DATA_TEST_CASE(findNodeAt_valid, VALID_NODES, p) { auto n = findNodeAt(p); BOOST_REQUIRE(n); BOOST_CHECK_EQUAL(n->pos, p); } BOOST_DATA_TEST_CASE(findNodeAt_invalid, INVALID_NODES, p) { BOOST_REQUIRE(!findNodeAt(p)); } BOOST_DATA_TEST_CASE(nodeAt, VALID_NODES + INVALID_NODES, p) { auto n = nodeAt(p); BOOST_REQUIRE(n); BOOST_CHECK_EQUAL(n->pos, p); } BOOST_DATA_TEST_CASE(newNodeAt_existing, VALID_NODES, p) { auto n = newNodeAt(p); BOOST_CHECK_EQUAL(n.second, Network::NodeIs::InNetwork); BOOST_REQUIRE(n.first); BOOST_CHECK_EQUAL(n.first->pos, p); } BOOST_DATA_TEST_CASE(newNodeAt_new, INVALID_NODES, p) { auto n = newNodeAt(p); BOOST_CHECK_EQUAL(n.second, Network::NodeIs::NotInNetwork); BOOST_REQUIRE(n.first); BOOST_CHECK_EQUAL(n.first->pos, p); } BOOST_AUTO_TEST_CASE(network_joins) { // Ends BOOST_CHECK(links.objects[2]->ends[1].nexts.empty()); // Join 0 <-> 1 BOOST_REQUIRE_EQUAL(links.objects[0]->ends[1].nexts.size(), 2); BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[0].first.lock().get(), links.objects[1].get()); BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[0].second, 0); BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[1].first.lock().get(), links.objects[5].get()); BOOST_CHECK_EQUAL(links.objects[0]->ends[1].nexts[1].second, 0); BOOST_REQUIRE_EQUAL(links.objects[1]->ends[0].nexts.size(), 2); BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[0].first.lock().get(), links.objects[0].get()); BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[0].second, 1); BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[1].first.lock().get(), links.objects[5].get()); BOOST_CHECK_EQUAL(links.objects[1]->ends[0].nexts[1].second, 0); // Join 1 <-> 2 BOOST_REQUIRE_EQUAL(links.objects[1]->ends[1].nexts.size(), 2); BOOST_CHECK_EQUAL(links.objects[1]->ends[1].nexts[0].first.lock().get(), links.objects[2].get()); BOOST_CHECK_EQUAL(links.objects[1]->ends[1].nexts[0].second, 0); BOOST_REQUIRE_EQUAL(links.objects[2]->ends[0].nexts.size(), 2); BOOST_CHECK_EQUAL(links.objects[2]->ends[0].nexts[0].first.lock().get(), links.objects[1].get()); BOOST_CHECK_EQUAL(links.objects[2]->ends[0].nexts[0].second, 1); } BOOST_DATA_TEST_CASE(routeTo_nodeNotInNetwork, INVALID_NODES, dest) { const auto & start = links.objects.front()->ends[1]; BOOST_CHECK_THROW((void)routeFromTo(start, dest), std::out_of_range); } BOOST_AUTO_TEST_CASE(routeTo_noSteps) { const auto & start = links.objects.front()->ends[1]; auto r = this->routeFromTo(start, p100); BOOST_CHECK(r.empty()); } BOOST_AUTO_TEST_CASE(routeTo_upStream_to2) { const auto & start = links.objects.front()->ends[1]; auto r = this->routeFromTo(start, p200); BOOST_REQUIRE_EQUAL(r.size(), 1); BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[1].get()); } BOOST_AUTO_TEST_CASE(routeTo_upStream_to3) { const auto & start = links.objects[0]->ends[1]; auto r = this->routeFromTo(start, p300); BOOST_REQUIRE_EQUAL(r.size(), 2); BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[1].get()); BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[2].get()); } BOOST_AUTO_TEST_CASE(routeTo_downStream_to0) { const auto & start = links.objects[2]->ends[0]; auto r = this->routeFromTo(start, p000); BOOST_REQUIRE_EQUAL(r.size(), 2); BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[1].get()); BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[0].get()); } BOOST_AUTO_TEST_CASE(routeTo_upStream_3to300) { const auto & start = links.objects[3]->ends[1]; auto r = this->routeFromTo(start, p300); BOOST_REQUIRE_EQUAL(r.size(), 2); BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[4].get()); BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[2].get()); } BOOST_AUTO_TEST_CASE(routeTo_downStream_3to300) { const auto & start = links.objects[3]->ends[0]; auto r = this->routeFromTo(start, p300); BOOST_REQUIRE_EQUAL(r.size(), 3); BOOST_CHECK_EQUAL(r[0].first.lock().get(), links.objects[0].get()); BOOST_CHECK_EQUAL(r[1].first.lock().get(), links.objects[1].get()); BOOST_CHECK_EQUAL(r[2].first.lock().get(), links.objects[2].get()); } BOOST_AUTO_TEST_SUITE_END() namespace std { std::ostream & operator<<(std::ostream & s, const Link::End & e) { return s << std::format("End[dir: {}, loc: ({}, {}, {})]", e.dir, e.node->pos.x, e.node->pos.y, e.node->pos.z); } } BOOST_FIXTURE_TEST_CASE(test_rail_network, RailLinks) { // 0 1 2 // --p000 <-> p100 <-> p200 <-> p300 \ x // / ?----- \ x // / / \ | // | / 4 / // 3 p110 \ / // \ | \ / // \ / ------/ // -------- auto l0 = addLinksBetween(p000, p100); BOOST_CHECK(dynamic_cast(l0.get())); BOOST_CHECK_EQUAL(l0->length, glm::length(difference(p000, p100))); BOOST_CHECK_CLOSE(l0->ends[0].dir, half_pi, 0.1F); BOOST_CHECK_CLOSE(l0->ends[1].dir, -half_pi, 0.1F); BOOST_CHECK(l0->ends[0].nexts.empty()); BOOST_CHECK(l0->ends[1].nexts.empty()); auto l1 = addLinksBetween(p200, p100); BOOST_CHECK(dynamic_cast(l1.get())); BOOST_CHECK_EQUAL(l1->length, glm::length(difference(p200, p100))); BOOST_CHECK_CLOSE(l1->ends[0].dir, half_pi, 0.1F); BOOST_CHECK_CLOSE(l1->ends[1].dir, -half_pi, 0.1F); BOOST_CHECK(l0->ends[0].nexts.empty()); BOOST_CHECK_EQUAL(l0->ends[1].nexts.at(0).first.lock(), l1); BOOST_CHECK_EQUAL(l0->ends[1].nexts.at(0).second, 0); BOOST_CHECK_EQUAL(l1->ends[0].nexts.at(0).first.lock(), l0); BOOST_CHECK_EQUAL(l1->ends[0].nexts.at(0).second, 1); BOOST_CHECK(l1->ends[1].nexts.empty()); auto l2 = addLinksBetween(p200, p300); BOOST_CHECK(dynamic_cast(l2.get())); BOOST_CHECK_EQUAL(l2->length, glm::length(difference(p200, p300))); BOOST_CHECK_CLOSE(l2->ends[0].dir, half_pi, 0.1F); BOOST_CHECK_CLOSE(l2->ends[1].dir, -half_pi, 0.1F); BOOST_CHECK(l0->ends[0].nexts.empty()); BOOST_CHECK_EQUAL(l1->ends[1].nexts.at(0).first.lock(), l2); BOOST_CHECK_EQUAL(l1->ends[1].nexts.at(0).second, 0); BOOST_CHECK_EQUAL(l2->ends[0].nexts.at(0).first.lock(), l1); BOOST_CHECK_EQUAL(l2->ends[0].nexts.at(0).second, 1); BOOST_CHECK(l2->ends[1].nexts.empty()); BOOST_CHECK_IF(l3, addLinksBetween(p000, p110)) { BOOST_CHECK_IF(l3c, dynamic_cast(l3.get())) { BOOST_CHECK_CLOSE(l3c->radius, 10'300.F, 0.1F); BOOST_CHECK_CLOSE(l3c->arc.length(), pi + half_pi, 0.5F); BOOST_CHECK_CLOSE(l3->length, 48'563.F, 0.1F); BOOST_CHECK_CLOSE(l3->ends[0].dir, -half_pi, 0.5F); BOOST_CHECK_CLOSE(l3->ends[1].dir, -0.0097F, 0.5F); BOOST_CHECK_EQUAL(l0->ends[0].nexts.at(0).first.lock(), l3); BOOST_CHECK_EQUAL(l0->ends[0].nexts.at(0).second, 0); BOOST_CHECK_EQUAL(l3->ends[0].nexts.at(0).first.lock(), l0); BOOST_CHECK_EQUAL(l3->ends[0].nexts.at(0).second, 0); BOOST_CHECK(l3->ends[1].nexts.empty()); } } BOOST_CHECK_IF(l4, addLinksBetween(p110, p300)) { BOOST_CHECK_IF(l4c, dynamic_cast(l4.get())) { BOOST_CHECK_CLOSE(l4c->radius, 6950.F, 0.1F); BOOST_CHECK_CLOSE(l4c->arc.length(), 4.456F, 0.1F); BOOST_CHECK_CLOSE(l4->length, 30'981.F, 0.1F); BOOST_CHECK_BETWEEN(l4->ends[0].dir, .25F, .26F); BOOST_CHECK_CLOSE(l4->ends[1].dir, half_pi, 0.1F); } } }