File indexing completed on 2026-10-29 08:25:02
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <algorithms/service.h>
0010
0011 #include <catch2/catch_test_macros.hpp>
0012 #include <catch2/matchers/catch_matchers_string.hpp>
0013
0014 #include <stdexcept>
0015 #include <string_view>
0016
0017 using namespace algorithms;
0018 using Catch::Matchers::ContainsSubstring;
0019
0020 namespace {
0021
0022
0023 class CountingSvc : public Service<CountingSvc> {
0024 public:
0025 void init() { ++m_count; }
0026 int count() const { return m_count; }
0027
0028 private:
0029 int m_count = 0;
0030
0031 ALGORITHMS_DEFINE_SERVICE(CountingSvc)
0032 };
0033
0034
0035 class FlakySvc : public Service<FlakySvc> {
0036 public:
0037 void init() {
0038 if (m_fail) {
0039 m_fail = false;
0040 throw std::runtime_error("FlakySvc failed to initialize");
0041 }
0042 }
0043
0044 private:
0045 bool m_fail = true;
0046
0047 ALGORITHMS_DEFINE_SERVICE(FlakySvc)
0048 };
0049
0050
0051 class ConfiguredSvc : public Service<ConfiguredSvc> {
0052 public:
0053 void init() {}
0054
0055 private:
0056 Property<int> m_required{this, "required", "required property without default"};
0057
0058 ALGORITHMS_DEFINE_SERVICE(ConfiguredSvc)
0059 };
0060
0061
0062 class CustomInitSvc : public Service<CustomInitSvc> {
0063 public:
0064 void init() { m_value = -1; }
0065 void init(int value) { m_value = value; }
0066 int value() const { return m_value; }
0067
0068 private:
0069 int m_value = 0;
0070
0071 ALGORITHMS_DEFINE_SERVICE(CustomInitSvc)
0072 };
0073
0074
0075 class LateSvc : public Service<LateSvc> {
0076 public:
0077 void init() { ++m_count; }
0078 int count() const { return m_count; }
0079
0080 private:
0081 int m_count = 0;
0082
0083 ALGORITHMS_DEFINE_SERVICE(LateSvc)
0084 };
0085
0086
0087 class LateCustomInitSvc : public Service<LateCustomInitSvc> {
0088 public:
0089 void init() { m_value = -1; }
0090 void init(int value) { m_value = value; }
0091 int value() const { return m_value; }
0092
0093 private:
0094 int m_value = 0;
0095
0096 ALGORITHMS_DEFINE_SERVICE(LateCustomInitSvc)
0097 };
0098
0099
0100 class ExternalSvc : public ServiceBase {
0101 public:
0102 constexpr static const char* kName = "ExternalSvc";
0103 void init() { ++m_count; }
0104 int count() const { return m_count; }
0105
0106 private:
0107 int m_count = 0;
0108 };
0109
0110
0111 class UnusedSvc : public Service<UnusedSvc> {
0112 public:
0113 void init() {}
0114
0115 ALGORITHMS_DEFINE_SERVICE(UnusedSvc)
0116 };
0117
0118 }
0119
0120 TEST_CASE("ServiceSvc initializes all registered services", "[service]") {
0121 auto& svcs = ServiceSvc::instance();
0122 CHECK(svcs.name() == "ServiceSvc");
0123
0124
0125 svcs.setInit<CustomInitSvc>([](CustomInitSvc& s) { s.init(42); });
0126
0127
0128 auto& counting = CountingSvc::instance();
0129 auto& flaky = FlakySvc::instance();
0130 auto& configured = ConfiguredSvc::instance();
0131 auto& custom = CustomInitSvc::instance();
0132
0133 CHECK(counting.name() == "CountingSvc");
0134 CHECK(counting.description() == "CountingSvc");
0135 CHECK(svcs.has<CountingSvc>());
0136 CHECK(svcs.has<FlakySvc>());
0137 CHECK(svcs.has<ConfiguredSvc>());
0138 CHECK(svcs.has<CustomInitSvc>());
0139 CHECK_FALSE(svcs.has<UnusedSvc>());
0140 CHECK(svcs.services().size() == 4);
0141 CHECK(svcs.service("CountingSvc") == &counting);
0142 CHECK(svcs.service<CountingSvc>("CountingSvc") == &counting);
0143 CHECK_THROWS_AS(svcs.service("UnusedSvc"), std::out_of_range);
0144
0145
0146 CHECK_FALSE(svcs.ready());
0147 CHECK_FALSE(counting.ready());
0148 CHECK_THROWS_AS(svcs.initSingle<CountingSvc>(), ServiceError);
0149 CHECK_THROWS_WITH(svcs.initSingle<CountingSvc>(),
0150 "initSingle<CountingSvc>() should not be called before/instead of init()");
0151
0152
0153 CHECK_THROWS_WITH(svcs.init(), ContainsSubstring(R"(Encountered uninitialized services: ["FlakySvc"])"));
0154 CHECK(counting.ready());
0155 CHECK(counting.count() == 1);
0156 CHECK_FALSE(flaky.ready());
0157 CHECK_FALSE(svcs.ready());
0158
0159
0160 CHECK_THROWS_WITH(
0161 svcs.init(),
0162 ContainsSubstring(R"(Encountered missing service properties: {"ConfiguredSvc": ["required"]})"));
0163 CHECK(counting.count() == 2);
0164 CHECK(flaky.ready());
0165 CHECK_FALSE(configured.ready());
0166 CHECK_FALSE(svcs.ready());
0167
0168
0169 configured.setProperty("required", 1);
0170 CHECK_NOTHROW(svcs.init());
0171 CHECK(svcs.ready());
0172 CHECK(counting.count() == 3);
0173 CHECK(configured.ready());
0174 CHECK(custom.ready());
0175 CHECK(custom.value() == 42);
0176 CHECK_NOTHROW(svcs.validate());
0177
0178
0179 CHECK_THROWS_WITH(svcs.init(), "Cannot initialize services twice");
0180 CHECK_THROWS_WITH(svcs.initSingle<CountingSvc>(),
0181 "Cannot initialize service CountingSvc - already initialized");
0182 CHECK(counting.count() == 3);
0183
0184
0185 counting.ready(false);
0186 CHECK_FALSE(svcs.ready());
0187 CHECK_THROWS_WITH(svcs.validate(), ContainsSubstring(R"(Encountered uninitialized services: ["CountingSvc"])"));
0188 svcs.initSingle<CountingSvc>();
0189 CHECK(counting.ready());
0190 CHECK(counting.count() == 4);
0191 CHECK(svcs.ready());
0192
0193
0194 auto& late = LateSvc::instance();
0195 CHECK(svcs.has<LateSvc>());
0196 CHECK(late.ready());
0197 CHECK(late.count() == 1);
0198 CHECK(&LateSvc::instance() == &late);
0199 CHECK(late.count() == 1);
0200 CHECK(svcs.ready());
0201
0202
0203 svcs.setInit<LateCustomInitSvc>([](LateCustomInitSvc& s) { s.init(7); });
0204 CHECK(LateCustomInitSvc::instance().ready());
0205 CHECK(LateCustomInitSvc::instance().value() == 7);
0206
0207
0208 ExternalSvc external;
0209 svcs.add<ExternalSvc>(&external);
0210 CHECK(svcs.has<ExternalSvc>());
0211 CHECK(external.ready());
0212 CHECK(external.count() == 1);
0213 CHECK(svcs.ready());
0214 }