#pragma once #include #include #include #include #include #include #include #include namespace celuna { /** * @brief Orchestrator for integration tests * * TestRunnerModule discovers, loads, and executes integration test modules. * Each test is a separate .so file that implements ITestModule. * * Workflow: * 1. Scan tests/integration/ for IT_*.so files * 2. For each test: * - Load module dynamically * - Execute via execute() * - Collect result * - Unload module * 3. Generate report (console + JSON) * 4. Exit with appropriate code (0 = success, 1 = failure) */ class TestRunnerModule : public grove::IModule { public: TestRunnerModule(); ~TestRunnerModule() override; void setConfiguration(const grove::IDataNode& config, grove::IIO* io, grove::ITaskScheduler* scheduler) override; void process(const grove::IDataNode& input) override; const grove::IDataNode& getConfiguration() override; std::unique_ptr getHealthStatus() override; void shutdown() override; std::unique_ptr getState() override; void setState(const grove::IDataNode& state) override; std::string getType() const override { return "TestRunnerModule"; } int getVersion() const override { return 1; } bool isIdle() const override { return m_executed; } private: void discoverTests(); testing::TestResult runTest(const std::string& testPath); void generateReport(); void generateJsonReport(const std::string& outputPath); grove::IIO* m_io = nullptr; grove::ITaskScheduler* m_scheduler = nullptr; std::unique_ptr m_config; std::string m_testDirectory; int m_globalTimeoutMs = 300000; // 5 minutes bool m_stopOnFirstFailure = false; bool m_verboseOutput = true; std::string m_jsonOutputPath; std::vector m_testPaths; std::vector m_results; bool m_executed = false; }; } // namespace celuna