lacteApp
C++17 service for Lacte hardware
Loading...
Searching...
No Matches
TestConsoleApp.hpp
Go to the documentation of this file.
1
16#pragma once
17
18#include <chrono>
19#include <filesystem>
20#include <fstream>
21#include <memory>
22#include <stdexcept>
23#include <string>
24#include <thread>
25#include <tuple>
26
27#include "AppContext.h"
28#include "ConsoleApp.hpp"
29
38inline constexpr insitech::config::FieldSpec<const char*> K_APP_NAME{
39 "app_name", "test app name", "test-console-app"};
40inline constexpr insitech::config::FieldSpec<const char*> K_APP_DESCRIPTION{
41 "app_description", "test app description", "test-console-app"};
42inline constexpr insitech::config::FieldSpec<const char*> K_APP_DIR{
43 "app_dir", "test app dir", "/tmp"};
44inline constexpr insitech::config::FieldSpec<const char*> K_BUS_MULTICAST_GROUP{
45 "bus_multicast_group", "test multicast group", "239.255.19.40"};
46inline constexpr insitech::config::FieldSpec<int64_t> K_BUS_MULTICAST_PORT{
47 "bus_multicast_port", "test multicast port", 47800};
48inline constexpr insitech::config::FieldSpec<int64_t> K_SHARED_BUS_ADDRESS{
49 "shared_bus_address", "test shared bus address", 100};
50inline constexpr insitech::config::FieldSpec<const char*> K_LOG_DIR{
51 "log_dir", "test log dir", "/tmp"};
52inline constexpr insitech::config::FieldSpec<int64_t> K_LOG_MIN_LEVEL{
53 "log_min_level", "test log min level", 1};
54
55inline constexpr auto K_ALL_FIELDS = std::tie(
58} // namespace test_console_app_schema
59
60// Базовый UDP multicast-порт для собственной шины этого тестового набора —
61// каждый выбранный вызывающей стороной port_offset (см.
62// make_test_console_app_config() ниже) выбирает отдельный порт отсюда, так
63// что несколько одновременно существующих тестовых экземпляров никогда не
64// рискуют реальным конфликтом при bind.
65inline constexpr int K_TEST_CONSOLE_APP_BASE_MULTICAST_PORT = 47800;
66
67class TestConsoleApp : public insitech::ConsoleApp<TestConsoleApp> {
68 public:
69 using ConsoleApp::ConsoleApp;
70
106 [[nodiscard]] auto run_impl() const -> int {
107 if (const char* crash_mode = std::getenv("TEST_CONSOLE_APP_CRASH_MODE");
108 crash_mode != nullptr) {
109 const std::string mode(crash_mode);
110 const char* marker_path = std::getenv("TEST_CONSOLE_APP_CRASH_MARKER");
111 bool should_crash = true;
112 if (mode != "always" && marker_path != nullptr) {
113 if (std::filesystem::exists(marker_path)) {
114 should_crash = false;
115 } else {
116 std::ofstream(marker_path) << "crashed";
117 }
118 }
119 if (should_crash) {
120 if (mode == "segv") {
121 // `volatile` на самом указателе (а не только на том, на что он
122 // указывает) заставляет выполнить реальную загрузку-затем-запись
123 // по адресу 0 — без этого -O3 распознаёт запись по null как
124 // неопределённое поведение и вправе заменить всё целиком на
125 // trap/abort() вместо аппаратного SIGSEGV, который на самом деле
126 // нужен этому тесту (воспроизведено вживую: после оптимизации
127 // это падало с SIGABRT, а не с SIGSEGV).
128 int* volatile bad = nullptr;
129 *bad =
130 1; // NOLINT: намеренная, тестовая запись по нулевому указателю.
131 } else if (mode == "throw") {
132 std::thread([]() -> void {
133 throw std::runtime_error(
134 "TestConsoleApp: deliberate uncaught exception (crash test)");
135 }).detach();
136 // Даём отсоединённому потоку время реально бросить исключение (и
137 // TerminateHandler() — время на ExecSelfRestart()/abort()),
138 // прежде чем собственный цикл этого потока ниже иначе просто
139 // проскочил бы мимо этого в режиме ожидания.
140 constexpr auto throw_settle_time = std::chrono::seconds(5);
141 std::this_thread::sleep_for(throw_settle_time);
142 } else {
143 // "abort" или "always".
144 std::abort();
145 }
146 }
147 }
148 constexpr auto idle_poll_interval = std::chrono::milliseconds(20);
149 while (m_running.load(std::memory_order_relaxed)) {
150 std::this_thread::sleep_for(idle_poll_interval);
151 }
152 return 0;
153 }
154
155 protected:
156 void user_signal_handler(int /*signum*/) override {}
157};
158
170// NOLINTBEGIN(bugprone-easily-swappable-parameters) - у app_name/log_dir
171// ясные имена, они различимы в каждой реальной точке вызова.
172inline auto make_test_console_app_config(const std::string& app_name,
173 const std::string& log_dir,
174 const int port_offset)
175 -> std::unique_ptr<insitech::config::ConfigStore<>> {
176 // NOLINTEND(bugprone-easily-swappable-parameters)
177 static insitech::config::JsonConfigCodec codec;
178 auto config = std::make_unique<insitech::config::ConfigStore<>>(
180 config->set<std::string>(test_console_app_schema::K_APP_NAME.key, app_name);
181 config->set<std::string>(test_console_app_schema::K_LOG_DIR.key, log_dir);
182 // Собственная история сессий AppControl теперь хранится под app_dir
183 // (см. конструктор ConsoleApp.hpp), а не под log_dir — тестовому набору
184 // эта разница не важна, поэтому оба поля просто получают одну и ту же
185 // выбранную вызывающей стороной директорию.
186 config->set<std::string>(test_console_app_schema::K_APP_DIR.key, log_dir);
187 config->set<std::string>(test_console_app_schema::K_BUS_MULTICAST_GROUP.key,
188 "239.255.19.40");
189 config->set<int64_t>(test_console_app_schema::K_BUS_MULTICAST_PORT.key,
191 return config;
192}
constexpr int K_TEST_CONSOLE_APP_BASE_MULTICAST_PORT
auto make_test_console_app_config(const std::string &app_name, const std::string &log_dir, const int port_offset) -> std::unique_ptr< insitech::config::ConfigStore<> >
Строит ConfigStore только со схемой (без файла — см.
auto run_impl() const -> int
Используется ТОЛЬКО ConsoleAppCrashTest.cpp, чтобы прогнать собственный механизм ConsoleApp по логиро...
void user_signal_handler(int) override
Минимальная самодостаточная схема, объявляющая ровно собственные REQUIRED FIELDS AppContext (см.
constexpr insitech::config::FieldSpec< const char * > K_APP_DESCRIPTION
constexpr insitech::config::FieldSpec< const char * > K_APP_DIR
constexpr insitech::config::FieldSpec< const char * > K_BUS_MULTICAST_GROUP
constexpr insitech::config::FieldSpec< const char * > K_LOG_DIR
constexpr insitech::config::FieldSpec< int64_t > K_BUS_MULTICAST_PORT
constexpr insitech::config::FieldSpec< int64_t > K_SHARED_BUS_ADDRESS
constexpr insitech::config::FieldSpec< int64_t > K_LOG_MIN_LEVEL
constexpr insitech::config::FieldSpec< const char * > K_APP_NAME