Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-29 08:25:01

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2026 Wouter Deconinck
0003 //
0004 // Tests for the AlgorithmSvc factory service
0005 //
0006 // The AlgorithmSvc only works after ServiceSvc::init(), which can only be called once per
0007 // process, so the full lifecycle is tested in a single test case.
0008 //
0009 #include <algorithms/algorithm.h>
0010 
0011 #include <catch2/catch_test_macros.hpp>
0012 #include <catch2/matchers/catch_matchers_string.hpp>
0013 
0014 #include <cstddef>
0015 #include <latch>
0016 #include <string>
0017 #include <thread>
0018 #include <vector>
0019 
0020 using namespace algorithms;
0021 using Catch::Matchers::ContainsSubstring;
0022 
0023 namespace algorithm_svc_test {
0024 
0025 using IntAlgorithm    = Algorithm<Input<int>, Output<int>>;
0026 using DoubleAlgorithm = Algorithm<Input<double>, Output<double>>;
0027 
0028 class AddOne : public IntAlgorithm {
0029 public:
0030   AddOne() : IntAlgorithm{"AddOne", {"in"}, {"out"}, "Adds one"} {}
0031   void process(const Input& input, const Output& output) const final {
0032     *std::get<0>(output) = *std::get<0>(input) + 1;
0033   }
0034 };
0035 
0036 class Twice : public IntAlgorithm {
0037 public:
0038   Twice() : IntAlgorithm{"Twice", {"in"}, {"out"}, "Multiplies by two"} {}
0039   void process(const Input& input, const Output& output) const final {
0040     *std::get<0>(output) = *std::get<0>(input) * 2;
0041   }
0042 };
0043 
0044 class Half : public DoubleAlgorithm {
0045 public:
0046   Half() : DoubleAlgorithm{"Half", {"in"}, {"out"}, "Divides by two"} {}
0047   void process(const Input& input, const Output& output) const final {
0048     *std::get<0>(output) = *std::get<0>(input) / 2;
0049   }
0050 };
0051 
0052 } // namespace algorithm_svc_test
0053 
0054 using namespace algorithm_svc_test;
0055 
0056 TEST_CASE("AlgorithmSvc creates registered algorithms by type and name", "[algorithm]") {
0057   auto& svc = AlgorithmSvc::instance();
0058   CHECK(svc.name() == "AlgorithmSvc");
0059 
0060   svc.add<AddOne>();
0061   svc.add<Twice>();
0062   svc.add<Half>();
0063   // adding an algorithm twice has no effect
0064   svc.add<AddOne>();
0065 
0066   // the service cannot be used before all services are initialized
0067   CHECK_THROWS_AS(svc.ls<IntAlgorithm>(), Error);
0068   CHECK_THROWS_WITH(svc.get<IntAlgorithm>("algorithm_svc_test::AddOne"),
0069                     "Attempt to use AlgorithmSvc, but service not yet marked as ready");
0070 
0071   ServiceSvc::instance().init();
0072   REQUIRE(svc.ready());
0073 
0074   // algorithms are listed by type, with their demangled type name
0075   CHECK(svc.ls<IntAlgorithm>() ==
0076         std::vector<std::string>{"algorithm_svc_test::AddOne", "algorithm_svc_test::Twice"});
0077   CHECK(svc.ls<DoubleAlgorithm>() == std::vector<std::string>{"algorithm_svc_test::Half"});
0078   CHECK(svc.ls<Algorithm<Input<float>, Output<float>>>().empty());
0079 
0080   // each get creates a new instance
0081   const auto add_one = svc.get<IntAlgorithm>("algorithm_svc_test::AddOne");
0082   const auto twice   = svc.get<IntAlgorithm>("algorithm_svc_test::Twice");
0083   const auto half    = svc.get<DoubleAlgorithm>("algorithm_svc_test::Half");
0084   REQUIRE(add_one != nullptr);
0085   REQUIRE(twice != nullptr);
0086   REQUIRE(half != nullptr);
0087   CHECK(add_one != svc.get<IntAlgorithm>("algorithm_svc_test::AddOne"));
0088   CHECK(add_one->name() == "AddOne");
0089   CHECK(twice->name() == "Twice");
0090 
0091   const int i = 3;
0092   int j       = 0;
0093   add_one->process({&i}, {&j});
0094   CHECK(j == 4);
0095   twice->process({&i}, {&j});
0096   CHECK(j == 6);
0097 
0098   const double x = 3.;
0099   double y       = 0.;
0100   half->process({&x}, {&y});
0101   CHECK(y == 1.5);
0102 
0103   // unknown names, types and invalid names are rejected
0104   CHECK_THROWS_WITH(svc.get<IntAlgorithm>("algorithm_svc_test::Half"),
0105                     ContainsSubstring("No factory with name algorithm_svc_test::Half and type"));
0106   CHECK_THROWS_WITH((svc.get<Algorithm<Input<float>, Output<float>>>("AddOne")),
0107                     ContainsSubstring("No factory with name AddOne"));
0108   CHECK_THROWS_WITH(svc.get<IntAlgorithm>(""), "Invalid name provided: ''");
0109 
0110   // algorithms can be created, listed and added concurrently
0111   constexpr std::size_t kThreads = 8;
0112   std::vector<int> results(kThreads, 0);
0113   {
0114     std::latch start{kThreads};
0115     std::vector<std::jthread> threads;
0116     for (std::size_t t = 0; t < kThreads; ++t) {
0117       threads.emplace_back([&svc, &start, &results, t] {
0118         start.arrive_and_wait();
0119         svc.add<Twice>();
0120         const auto names = svc.ls<IntAlgorithm>();
0121         const auto algo  = svc.get<IntAlgorithm>(names[t % names.size()]);
0122         const int in     = static_cast<int>(t);
0123         algo->process({&in}, {&results[t]});
0124       });
0125     }
0126   }
0127   for (std::size_t t = 0; t < kThreads; ++t) {
0128     CHECK(results[t] == ((t % 2 == 0) ? static_cast<int>(t) + 1 : 2 * static_cast<int>(t)));
0129   }
0130 }