#undef NDEBUG #include"Boss/Mod/NeedsConnectSolicitor.hpp" #include"Boss/Msg/NeedsConnect.hpp" #include"Boss/Msg/ProposeConnectCandidates.hpp" #include"Boss/Msg/RequestConnect.hpp" #include"Boss/Msg/ResponseConnect.hpp" #include"Boss/Msg/SolicitConnectCandidates.hpp" #include"Boss/Msg/TaskCompletion.hpp" #include"Ev/Io.hpp" #include"Ev/start.hpp" #include"S/Bus.hpp" #include #include #include #include #include namespace { class DummyConnector { public: /* What to return in Boss::Msg::ResponseConnect. */ bool success; /* How many times have we gotten RequestConnect. */ size_t connects; DummyConnector(S::Bus& bus) { success = true; connects = 0; bus.subscribe([this, &bus](Boss::Msg::RequestConnect const& req) { ++connects; return bus.raise(Boss::Msg::ResponseConnect{req.node, success}); }); } DummyConnector(DummyConnector&&) =delete; }; class DummyConnectDiscoverer { public: std::vector to_return; DummyConnectDiscoverer(S::Bus& bus) { bus.subscribe([this, &bus](Boss::Msg::SolicitConnectCandidates const& _) { if (to_return.empty()) return Ev::lift(); return bus.raise(Boss::Msg::ProposeConnectCandidates{ std::move(to_return) }); }); } DummyConnectDiscoverer(DummyConnectDiscoverer&&) =delete; }; class TaskCompletionWaiter { private: size_t pending_completions; std::queue> passes; void trigger() { while (pending_completions && !passes.empty()) { auto pass = std::move(passes.front()); passes.pop(); --pending_completions; pass(); } } public: TaskCompletionWaiter(S::Bus& bus ) : pending_completions(0) , passes() { bus.subscribe([this](Boss::Msg::TaskCompletion const& _) { ++pending_completions; trigger(); return Ev::lift(); }); } TaskCompletionWaiter(TaskCompletionWaiter&&) =delete; Ev::Io wait() { return Ev::Io([this ]( std::function pass , std::function fail ) { passes.emplace(std::move(pass)); trigger(); }); } }; } int main() { S::Bus bus; DummyConnector connector(bus); DummyConnectDiscoverer discoverer(bus); TaskCompletionWaiter waiter(bus); Boss::Mod::NeedsConnectSolicitor solicitor(bus); auto needs_connect = [&bus, &waiter]() { return bus.raise(Boss::Msg::NeedsConnect()).then([&waiter]() { return waiter.wait(); }); }; auto test = Ev::lift().then([&]() { connector.success = true; /* Nothing to connect to. */ discoverer.to_return.clear(); return needs_connect(); }).then([&]() { /* Since there is nothing to connect, there should not * have been any connection attempts. */ assert(connector.connects == 0); /* Now add some things to connect. */ discoverer.to_return.push_back("1"); discoverer.to_return.push_back("2"); discoverer.to_return.push_back("3"); discoverer.to_return.push_back("4"); discoverer.to_return.push_back("5"); return needs_connect(); }).then([&]() { /* Since the dummy connector is in "success" mode, * there should have been at most two connect attempts, * but more than 0. */ assert(connector.connects > 0); assert(connector.connects <= 2); /* Reset, then make connections fail. */ connector.connects = 0; connector.success = false; /* Add some things to connect. */ discoverer.to_return.push_back("1"); discoverer.to_return.push_back("2"); discoverer.to_return.push_back("3"); discoverer.to_return.push_back("4"); discoverer.to_return.push_back("5"); return needs_connect(); }).then([&]() { /* The dummy connector failed them all, so * there should be 5 connect attempts. */ assert(connector.connects == 5); return Ev::lift(0); }); return Ev::start(test); }