// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md #include #include "Service/ServiceCreator.hpp" #include "SystemManager/DependencyGraph.hpp" #include "SystemManager/graph/TopologicalSort.hpp" using namespace sys; using graph::TopologicalSort; class MockedServiceCreator : public BaseServiceCreator { public: using BaseServiceCreator::BaseServiceCreator; std::shared_ptr create() const override { return nullptr; } }; ServiceManifest createManifest(ServiceManifest::ServiceName name, std::vector deps) { ServiceManifest manifest; manifest.name = std::move(name); manifest.dependencies = std::move(deps); return manifest; } TEST_CASE("Given Dependency Graph When topological sort empty graph then verify if result is empty") { DependencyGraph graph{{}, std::make_unique()}; const auto &sorted = graph.sort(); REQUIRE(sorted.empty()); } TEST_CASE("Given Dependency Graph When topological sort without dependencies then verify order") { std::vector> services; services.push_back(std::make_unique(createManifest("S1", {}))); services.push_back(std::make_unique(createManifest("S2", {}))); services.push_back(std::make_unique(createManifest("S3", {}))); services.push_back(std::make_unique(createManifest("S4", {}))); // Graph: // S1 S2 S3 S4 DependencyGraph graph{graph::nodesFrom(services), std::make_unique()}; const auto &sorted = graph.sort(); REQUIRE(sorted[0].get().getName() == "S1"); REQUIRE(sorted[1].get().getName() == "S2"); REQUIRE(sorted[2].get().getName() == "S3"); REQUIRE(sorted[3].get().getName() == "S4"); } TEST_CASE("Given Dependency Graph When topological sort simple case then verify order") { std::vector> services; services.push_back(std::make_unique(createManifest("S1", {"S2"}))); services.push_back(std::make_unique(createManifest("S2", {"S3"}))); services.push_back(std::make_unique(createManifest("S3", {"S4"}))); services.push_back(std::make_unique(createManifest("S4", {}))); // Graph: // S1 -> S2 -> S3 -> S4 DependencyGraph graph{graph::nodesFrom(services), std::make_unique()}; const auto &sorted = graph.sort(); REQUIRE(sorted[0].get().getName() == "S4"); REQUIRE(sorted[1].get().getName() == "S3"); REQUIRE(sorted[2].get().getName() == "S2"); REQUIRE(sorted[3].get().getName() == "S1"); } TEST_CASE("Given Dependency Graph When topological sort all depending on one then verify order") { std::vector> services; services.push_back(std::make_unique(createManifest("S1", {"S2"}))); services.push_back(std::make_unique(createManifest("S2", {}))); services.push_back(std::make_unique(createManifest("S3", {"S2"}))); services.push_back(std::make_unique(createManifest("S4", {"S2"}))); // Graph: // S1 -> S2 // / ^ // S3 | // S4 DependencyGraph graph{graph::nodesFrom(services), std::make_unique()}; const auto &sorted = graph.sort(); REQUIRE(sorted[0].get().getName() == "S2"); REQUIRE(sorted[1].get().getName() == "S1"); REQUIRE(sorted[2].get().getName() == "S3"); REQUIRE(sorted[3].get().getName() == "S4"); } TEST_CASE("Given Dependency Graph When topological sort advanced case then verify order 1") { std::vector> services; services.push_back(std::make_unique(createManifest("S1", {"S2", "S3"}))); services.push_back(std::make_unique(createManifest("S2", {"S4"}))); services.push_back(std::make_unique(createManifest("S3", {"S5"}))); services.push_back(std::make_unique(createManifest("S4", {"S6"}))); services.push_back(std::make_unique(createManifest("S5", {"S6"}))); services.push_back(std::make_unique(createManifest("S6", {}))); // Graph: // -> S2 -> S4 --> // S1 -> S3 -> S5 -> S6 DependencyGraph graph{graph::nodesFrom(services), std::make_unique()}; const auto &sorted = graph.sort(); REQUIRE(sorted[0].get().getName() == "S6"); REQUIRE(sorted[1].get().getName() == "S4"); REQUIRE(sorted[2].get().getName() == "S2"); REQUIRE(sorted[3].get().getName() == "S5"); REQUIRE(sorted[4].get().getName() == "S3"); REQUIRE(sorted[5].get().getName() == "S1"); } TEST_CASE("Given Dependency Graph When topological sort advanced case then verify order 2") { std::vector> services; services.push_back(std::make_unique(createManifest("S1", {"S2", "S3", "S4"}))); services.push_back(std::make_unique(createManifest("S2", {}))); services.push_back(std::make_unique(createManifest("S3", {"S5", "S6"}))); services.push_back(std::make_unique(createManifest("S4", {"S6"}))); services.push_back(std::make_unique(createManifest("S5", {}))); services.push_back(std::make_unique(createManifest("S6", {}))); // Graph: // --> S4 ---\> // / ----> S6 // / / // S1 -> S3 -> S5 // \-> S2 DependencyGraph graph{graph::nodesFrom(services), std::make_unique()}; const auto &sorted = graph.sort(); REQUIRE(sorted[0].get().getName() == "S2"); REQUIRE(sorted[1].get().getName() == "S5"); REQUIRE(sorted[2].get().getName() == "S6"); REQUIRE(sorted[3].get().getName() == "S3"); REQUIRE(sorted[4].get().getName() == "S4"); REQUIRE(sorted[5].get().getName() == "S1"); } TEST_CASE("Given Dependency Graph When topological sort on directed cyclic graph then verify order") { std::vector> services; services.push_back(std::make_unique(createManifest("S1", {"S2"}))); services.push_back(std::make_unique(createManifest("S2", {"S3"}))); services.push_back(std::make_unique(createManifest("S3", {"S1"}))); // Graph: // S1 -> S2 -> S3 // \---------/ DependencyGraph graph{graph::nodesFrom(services), std::make_unique()}; const auto &sorted = graph.sort(); // The order is not correct. Dependency Graph doesn't handle cyclic graphs properly REQUIRE(sorted[0].get().getName() == "S3"); REQUIRE(sorted[1].get().getName() == "S2"); REQUIRE(sorted[2].get().getName() == "S1"); }