Skip to content

Commit df4bb61

Browse files
Add hardware.require_live_session fail-fast guard with config parsing, tests, and troubleshooting docs
1 parent 531f03b commit df4bb61

7 files changed

Lines changed: 84 additions & 1 deletion

File tree

docs/build.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ Start the provider for ADPP clients:
114114
The committed sample config seeds one RLHT and one DCMT device so `Hello`, `WaitReady`,
115115
`ListDevices`, `DescribeDevice`, and `GetHealth` are testable without real hardware.
116116

117+
If a config sets `hardware.require_live_session: true`, no-hardware builds fail fast at startup.
118+
Use this for Linux hardware validation profiles to avoid silent config-seeded fallback.
119+
117120
## Test Taxonomy
118121

119122
Two test executables are produced by every build:

docs/troubleshooting.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ sudo setfacl -m u:$USER:rw /dev/i2c-1 # or run as root temporarily during dev
5151

5252
Confirm `hardware.bus_path` in your config matches the actual device node.
5353

54+
### Required live session on no-hardware build
55+
56+
If config sets `hardware.require_live_session: true` but the provider binary was built with
57+
`ANOLIS_PROVIDER_BREAD_ENABLE_HARDWARE=OFF`, startup fails immediately:
58+
59+
```
60+
[ERROR] hardware.require_live_session=true but provider was built without hardware support ...
61+
```
62+
63+
Fix by rebuilding with a Linux hardware preset:
64+
65+
```bash
66+
cmake --preset dev-linux-hardware-release
67+
cmake --build --preset dev-linux-hardware-release
68+
```
69+
5470
---
5571

5672
## Device Not Responding During Startup

src/config/provider_config.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ int parse_int_value(const YAML::Node &node,
6969
}
7070
}
7171

72+
bool parse_bool_value(const YAML::Node &node, const std::string &field_name) {
73+
const std::string text = require_scalar(node, field_name);
74+
if(text == "true") {
75+
return true;
76+
}
77+
if(text == "false") {
78+
return false;
79+
}
80+
throw std::runtime_error(field_name + " must be true or false");
81+
}
82+
7283
int parse_address_text(const std::string &text, const std::string &field_name) {
7384
const int base = (text.size() > 2 && text[0] == '0' &&
7485
(text[1] == 'x' || text[1] == 'X'))
@@ -173,9 +184,16 @@ ProviderConfig load_config(const std::string &path) {
173184
throw std::runtime_error("Missing required section: hardware");
174185
}
175186
ensure_map(hardware_node, "hardware");
176-
reject_unknown_keys(hardware_node, "hardware", {"bus_path", "query_delay_us", "timeout_ms", "retry_count"});
187+
reject_unknown_keys(
188+
hardware_node,
189+
"hardware",
190+
{"bus_path", "require_live_session", "query_delay_us", "timeout_ms", "retry_count"});
177191

178192
config.bus_path = require_scalar(hardware_node["bus_path"], "hardware.bus_path");
193+
if(hardware_node["require_live_session"]) {
194+
config.require_live_session =
195+
parse_bool_value(hardware_node["require_live_session"], "hardware.require_live_session");
196+
}
179197
if(hardware_node["query_delay_us"]) {
180198
config.query_delay_us = parse_int_value(hardware_node["query_delay_us"], "hardware.query_delay_us", false);
181199
}
@@ -267,6 +285,7 @@ std::string summarize_config(const ProviderConfig &config) {
267285
std::ostringstream out;
268286
out << "provider.name=" << config.provider_name
269287
<< ", hardware.bus_path=" << config.bus_path
288+
<< ", hardware.require_live_session=" << (config.require_live_session ? "true" : "false")
270289
<< ", hardware.query_delay_us=" << config.query_delay_us
271290
<< ", hardware.timeout_ms=" << config.timeout_ms
272291
<< ", hardware.retry_count=" << config.retry_count

src/config/provider_config.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct ProviderConfig {
2626
std::string config_file_path;
2727
std::string provider_name = "anolis-provider-bread";
2828
std::string bus_path;
29+
bool require_live_session = false;
2930
int query_delay_us = 10000;
3031
int timeout_ms = 100;
3132
int retry_count = 2;

src/core/runtime_state.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ void initialize(const ProviderConfig &config) {
104104

105105
#else
106106
// No-hardware path: seed inventory from config.
107+
if(config.require_live_session) {
108+
throw std::runtime_error(
109+
"hardware.require_live_session=true but provider was built without hardware support "
110+
"(ANOLIS_PROVIDER_BREAD_ENABLE_HARDWARE=OFF). Rebuild with dev-linux-hardware-release.");
111+
}
112+
107113
state.devices = inventory::build_seed_inventory(config);
108114
state.inventory_mode = inventory::to_string(inventory::InventorySource::ConfigSeeded);
109115
state.unsupported_probe_count = 0;

tests/unit/proto_smoke_test.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <gtest/gtest.h>
22

33
#include <sstream>
4+
#include <stdexcept>
45
#include <string>
56
#include <vector>
67

@@ -167,3 +168,16 @@ TEST(ProtoSmokeTest, InMemoryProviderLoopCanRoundTripHello) {
167168
io_error)) << io_error;
168169
EXPECT_FALSE(framed_response.str().empty());
169170
}
171+
172+
TEST(ProtoSmokeTest, RequireLiveSessionConfigBehaviorMatchesBuildMode) {
173+
auto config = make_stub_config();
174+
config.require_live_session = true;
175+
176+
anolis_provider_bread::runtime::reset();
177+
178+
#if !defined(ANOLIS_PROVIDER_BREAD_HAS_CRUMBS)
179+
EXPECT_THROW(anolis_provider_bread::runtime::initialize(config), std::runtime_error);
180+
#else
181+
GTEST_SKIP() << "no-hardware guard is only applicable when hardware integration is disabled";
182+
#endif
183+
}

tests/unit/provider_config_test.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ TEST(ProviderConfigTest, AppliesDefaultsForOptionalHardwareFields) {
7676
const ProviderConfig parsed = load_config(config.path().string());
7777
EXPECT_EQ(parsed.provider_name, "bread-lab");
7878
EXPECT_EQ(parsed.bus_path, "/dev/i2c-1");
79+
EXPECT_FALSE(parsed.require_live_session);
7980
EXPECT_EQ(parsed.query_delay_us, 10000);
8081
EXPECT_EQ(parsed.timeout_ms, 100);
8182
EXPECT_EQ(parsed.retry_count, 2);
@@ -114,6 +115,19 @@ TEST(ProviderConfigTest, ParsesManualDiscoveryAddressesAndDevices) {
114115
EXPECT_EQ(parsed.devices[1].address, 0x09);
115116
}
116117

118+
TEST(ProviderConfigTest, ParsesRequireLiveSession) {
119+
const TempConfigFile config(R"(
120+
hardware:
121+
bus_path: /dev/i2c-1
122+
require_live_session: true
123+
discovery:
124+
mode: scan
125+
)");
126+
127+
const ProviderConfig parsed = load_config(config.path().string());
128+
EXPECT_TRUE(parsed.require_live_session);
129+
}
130+
117131
TEST(ProviderConfigTest, RejectsMissingHardwareBusPath) {
118132
expect_config_error(R"(
119133
hardware:
@@ -152,6 +166,16 @@ unexpected: true
152166
)", "Unknown root key");
153167
}
154168

169+
TEST(ProviderConfigTest, RejectsInvalidRequireLiveSessionValue) {
170+
expect_config_error(R"(
171+
hardware:
172+
bus_path: /dev/i2c-1
173+
require_live_session: maybe
174+
discovery:
175+
mode: scan
176+
)", "hardware.require_live_session");
177+
}
178+
155179
TEST(ProviderConfigTest, RejectsUnknownDeviceType) {
156180
expect_config_error(R"(
157181
hardware:

0 commit comments

Comments
 (0)