Skip to content

Commit 4e01e89

Browse files
authored
smoke: deterministic transport smoke suite (c5) (#85)
* feat(smoke): add deterministic cross-transport smoke suite Introduce the transport-smoke-suite matrix/scenario runner with file-backed scenario claims, smoke CI workflow integration, and deterministic readiness controls in example-streamer-uses binaries/docs while finalizing workspace log-to-tracing migration. This boundary keeps smoke orchestration and fixture-backed contract validation isolated from architecture and benchmark changes. Key review files: utils/transport-smoke-suite/src/scenario.rs, utils/transport-smoke-suite/src/claims.rs, utils/transport-smoke-suite/src/bin/transport-smoke-matrix.rs, .github/workflows/transport-smoke-capstone.yaml, example-streamer-uses/src/bin/*, example-streamer-uses/Cargo.toml, Cargo.toml, README.md. Mechanical vs behavioral: combines mechanical dependency/config migration with behavioral smoke determinism and readiness signaling; fixture files are intentionally separate from runner/claims logic for review focus. * refactor: apply up-rust feedback sweep to smoke delta
1 parent 9ee94d8 commit 4e01e89

94 files changed

Lines changed: 5190 additions & 60 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
utils/transport-smoke-suite/tests/fixtures/** linguist-generated=true
2+
utils/transport-smoke-suite/claims/** linguist-generated=true
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# ********************************************************************************
2+
# Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
#
4+
# See the NOTICE file(s) distributed with this work for additional
5+
# information regarding copyright ownership.
6+
#
7+
# This program and the accompanying materials are made available under the
8+
# terms of the Apache License Version 2.0 which is available at
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# SPDX-License-Identifier: Apache-2.0
12+
# *******************************************************************************/
13+
14+
name: Transport Smoke Capstone
15+
16+
on:
17+
workflow_dispatch:
18+
schedule:
19+
- cron: "0 3 * * *"
20+
21+
concurrency:
22+
group: transport-smoke-capstone
23+
cancel-in-progress: false
24+
25+
jobs:
26+
mqtt-smoke:
27+
name: MQTT transport smoke matrix
28+
runs-on: ubuntu-22.04
29+
timeout-minutes: 45
30+
31+
steps:
32+
- uses: actions/checkout@v4
33+
- uses: dtolnay/rust-toolchain@stable
34+
35+
- name: Install dependencies
36+
run: sudo apt-get update && sudo apt-get install -y build-essential cmake libboost-all-dev libclang-dev pkg-config libssl-dev
37+
38+
- name: Run MQTT scenario subset
39+
run: |
40+
cargo run -p transport-smoke-suite --bin transport-smoke-matrix -- \
41+
--only smoke-zenoh-mqtt-rr-zenoh-client-mqtt-service \
42+
--only smoke-zenoh-mqtt-rr-mqtt-client-zenoh-service \
43+
--only smoke-zenoh-mqtt-ps-zenoh-publisher-mqtt-subscriber \
44+
--only smoke-zenoh-mqtt-ps-mqtt-publisher-zenoh-subscriber \
45+
--artifacts-root target/transport-smoke/ci-mqtt
46+
47+
- name: Upload MQTT artifacts
48+
if: always()
49+
uses: actions/upload-artifact@v4
50+
with:
51+
name: transport-smoke-mqtt
52+
path: target/transport-smoke/ci-mqtt
53+
retention-days: 14
54+
55+
someip-smoke:
56+
name: SOME/IP transport smoke matrix
57+
runs-on: ubuntu-22.04
58+
timeout-minutes: 60
59+
60+
steps:
61+
- uses: actions/checkout@v4
62+
- uses: dtolnay/rust-toolchain@stable
63+
64+
- name: Install dependencies
65+
run: sudo apt-get update && sudo apt-get install -y build-essential cmake libboost-all-dev libclang-dev pkg-config libssl-dev
66+
67+
- name: Run SOME/IP scenario subset (bundled profile)
68+
run: |
69+
cargo run -p transport-smoke-suite --bin transport-smoke-matrix -- \
70+
--only smoke-zenoh-someip-rr-zenoh-client-someip-service \
71+
--only smoke-zenoh-someip-rr-someip-client-zenoh-service \
72+
--only smoke-zenoh-someip-ps-zenoh-publisher-someip-subscriber \
73+
--only smoke-zenoh-someip-ps-someip-publisher-zenoh-subscriber \
74+
--artifacts-root target/transport-smoke/ci-someip
75+
76+
- name: Upload SOME/IP artifacts
77+
if: always()
78+
uses: actions/upload-artifact@v4
79+
with:
80+
name: transport-smoke-someip
81+
path: target/transport-smoke/ci-someip
82+
retention-days: 14

Cargo.lock

Lines changed: 18 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ members = [
1717
"utils/integration-test-utils",
1818
"example-streamer-implementations",
1919
"utils/criterion-guardrail",
20+
"utils/transport-smoke-suite",
2021
"configurable-streamer",
2122
"up-linux-streamer-plugin",
2223
"up-streamer",
@@ -35,10 +36,8 @@ license = "Apache-2.0"
3536
[workspace.dependencies]
3637
async-trait = { version = "0.1" }
3738
clap = { version = "4.5" }
38-
env_logger = { version = "0.10.1" }
3939
futures = { version = "0.3.30" }
4040
lazy_static = { version = "1.5.0" }
41-
log = { version = "0.4.20" }
4241
json5 = { version = "0.4.1" }
4342
serde = { version = "1.0.154", features = ["derive"] }
4443
serde_json = { version = "1.0.94" }

README.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,103 @@ Please reference the documentation for [vsomeip-sys](https://github.com/eclipse-
6565
* the build requirements for vsomeip in the linked documentation in the COVESA repo
6666
* the environment variables which must be set
6767

68+
## Deterministic transport smoke capstone
69+
70+
The workspace includes a deterministic smoke runner crate at `utils/transport-smoke-suite`.
71+
72+
Run all 8 canonical scenarios with one command:
73+
74+
```bash
75+
cargo run -p transport-smoke-suite --bin transport-smoke-matrix -- --all
76+
```
77+
78+
Run a single deterministic scenario:
79+
80+
```bash
81+
cargo run -p transport-smoke-suite --bin smoke-zenoh-mqtt-rr-mqtt-client-zenoh-service -- --send-count 12 --send-interval-ms 1000
82+
```
83+
84+
Scenario claims are file-backed and loaded strictly from JSON:
85+
86+
- Default claims directory: `utils/transport-smoke-suite/claims/`
87+
- Default per-scenario file: `utils/transport-smoke-suite/claims/<scenario-id>.json`
88+
- Missing, malformed, or scenario-id-mismatched claim files fail the run (no in-code fallback)
89+
90+
`--claims-path <path>` behavior for scenario and matrix binaries:
91+
92+
- Omitted: use the default per-scenario file in `utils/transport-smoke-suite/claims/`
93+
- Directory path: load `<path>/<scenario-id>.json`
94+
- File path: use that exact file for the selected scenario
95+
96+
Matrix restriction:
97+
98+
- `transport-smoke-matrix` rejects `--claims-path <file>` when multiple scenarios are selected
99+
- Use a directory override for multi-scenario matrix runs, or `--only <scenario-id>` for a single scenario
100+
101+
Single-scenario custom claims file example:
102+
103+
```bash
104+
cargo run -p transport-smoke-suite --bin smoke-zenoh-mqtt-rr-mqtt-client-zenoh-service -- --claims-path utils/transport-smoke-suite/tests/fixtures/custom-claims/smoke-zenoh-mqtt-rr-mqtt-client-zenoh-service.json --send-count 12 --send-interval-ms 1000
105+
```
106+
107+
Matrix custom claims directory example:
108+
109+
```bash
110+
cargo run -p transport-smoke-suite --bin transport-smoke-matrix -- --all --claims-path utils/transport-smoke-suite/claims
111+
```
112+
113+
Scenario binaries:
114+
115+
- `smoke-zenoh-mqtt-rr-zenoh-client-mqtt-service`
116+
- `smoke-zenoh-mqtt-rr-mqtt-client-zenoh-service`
117+
- `smoke-zenoh-mqtt-ps-zenoh-publisher-mqtt-subscriber`
118+
- `smoke-zenoh-mqtt-ps-mqtt-publisher-zenoh-subscriber`
119+
- `smoke-zenoh-someip-rr-zenoh-client-someip-service`
120+
- `smoke-zenoh-someip-rr-someip-client-zenoh-service`
121+
- `smoke-zenoh-someip-ps-zenoh-publisher-someip-subscriber`
122+
- `smoke-zenoh-someip-ps-someip-publisher-zenoh-subscriber`
123+
124+
Default artifacts are written under:
125+
126+
- Scenario run: `target/transport-smoke/<scenario-id>/<timestamp>/`
127+
- Matrix run: `target/transport-smoke/matrix/<timestamp>/`
128+
129+
Each scenario artifact directory includes:
130+
131+
- `streamer.log`
132+
- endpoint logs (`client.log`/`service.log`/`publisher.log`/`subscriber.log`)
133+
- `scenario-report.json`
134+
- `scenario-report.txt`
135+
136+
Matrix output includes:
137+
138+
- `matrix-summary.json`
139+
- `matrix-summary.txt`
140+
141+
Failure triage order:
142+
143+
1. preflight (missing tools/config/build prerequisites)
144+
2. readiness (missing `READY streamer_initialized` or `READY listener_registered`)
145+
3. claims (required evidence below thresholds or forbidden signatures)
146+
4. teardown (processes failing to stop cleanly)
147+
148+
Rerun one scenario deterministically:
149+
150+
```bash
151+
cargo run -p transport-smoke-suite --bin <scenario-id> -- --send-count 12 --send-interval-ms 1000
152+
```
153+
154+
Rerun only failed scenarios from a prior matrix summary:
155+
156+
```bash
157+
for s in $(jq -r '.failed_scenarios[].scenario_id' target/transport-smoke/matrix/<timestamp>/matrix-summary.json); do
158+
cargo run -p transport-smoke-suite --bin "$s" -- --send-count 12 --send-interval-ms 1000
159+
done
160+
```
161+
162+
When filing regressions, include:
163+
164+
- the failing scenario ID(s)
165+
- the exact repro command
166+
- `scenario-report.json` and `matrix-summary.json`
167+
- relevant excerpts from `streamer.log` and endpoint logs

example-streamer-uses/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,13 @@ mqtt-transport = ["up-transport-mqtt5"]
7777
async-trait = { workspace = true }
7878
chrono = { version = "0.4" }
7979
clap = { version = "4.5.9", features = ["derive"] }
80-
env_logger = { version = "0.10.2" }
8180
hello-world-protos = { path = "../utils/hello-world-protos" }
82-
log = { workspace = true }
8381
json5 = { workspace = true }
8482
protobuf = { workspace = true }
8583
serde = { workspace = true }
8684
tokio = { workspace = true }
8785
tracing = { workspace = true }
86+
tracing-subscriber = { workspace = true }
8887
up-rust = { workspace = true }
8988
up-transport-zenoh = { workspace = true, optional = true }
9089
up-transport-vsomeip = { workspace = true, optional = true }

example-streamer-uses/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,15 @@ Transport-specific flags:
9595
- Zenoh binaries: `--endpoint` (existing behavior, now composed with URI overrides)
9696
- SOME/IP binaries: `--vsomeip-config` and `--remote-authority`
9797

98+
Deterministic sender controls (active client/publisher binaries only):
99+
100+
- `--send-count <n>`
101+
- `0` (default) preserves existing infinite-send behavior
102+
- any value `>0` performs a bounded send run and then exits
103+
- `--send-interval-ms <ms>`
104+
- defaults to `1000`
105+
- controls delay between sends for bounded and unbounded runs
106+
98107
Running with no extra flags keeps prior behavior (defaults are aligned with previous constants).
99108

100109
### Numeric formats

example-streamer-uses/src/bin/mqtt_client.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ use clap::Parser;
1717
use common::cli;
1818
use common::ServiceResponseListener;
1919
use hello_world_protos::hello_world_service::HelloRequest;
20-
use log::info;
2120
use std::sync::Arc;
2221
use std::time::Duration;
22+
use tracing::info;
2323
use up_rust::{UListener, UMessageBuilder, UStatus, UTransport};
2424
use up_transport_mqtt5::{Mqtt5Transport, Mqtt5TransportOptions, MqttClientOptions};
2525

@@ -67,11 +67,17 @@ struct Args {
6767
/// MQTT broker URI in host:port format
6868
#[arg(long, default_value = DEFAULT_BROKER_URI)]
6969
broker_uri: String,
70+
/// Number of requests to send before exiting (0 means run forever)
71+
#[arg(long, default_value_t = 0)]
72+
send_count: u64,
73+
/// Milliseconds to wait between request sends
74+
#[arg(long, default_value_t = 1000)]
75+
send_interval_ms: u64,
7076
}
7177

7278
#[tokio::main]
7379
async fn main() -> Result<(), UStatus> {
74-
env_logger::init();
80+
let _ = tracing_subscriber::fmt::try_init();
7581

7682
let args = Args::parse();
7783

@@ -113,12 +119,18 @@ async fn main() -> Result<(), UStatus> {
113119
.register_listener(&sink, Some(&source), service_response_listener)
114120
.await?;
115121

116-
let mut i = 0;
122+
let mut i: u64 = 0;
123+
let mut sent_count: u64 = 0;
117124
loop {
118-
tokio::time::sleep(Duration::from_millis(1000)).await;
125+
if args.send_count > 0 && sent_count >= args.send_count {
126+
info!("Completed bounded send run: sent_count={sent_count}");
127+
break;
128+
}
129+
130+
tokio::time::sleep(Duration::from_millis(args.send_interval_ms)).await;
119131

120132
let hello_request = HelloRequest {
121-
name: format!("mqtt_client@i={}", i).to_string(),
133+
name: format!("mqtt_client@i={}", i),
122134
..Default::default()
123135
};
124136
i += 1;
@@ -129,5 +141,8 @@ async fn main() -> Result<(), UStatus> {
129141
info!("Sending Request message:\n{request_msg:?}");
130142

131143
client.send(request_msg).await?;
144+
sent_count += 1;
132145
}
146+
147+
Ok(())
133148
}

0 commit comments

Comments
 (0)