Phase19e loopback integration test#101
Conversation
…ndtrip) Validates 19a-c against a real embassy_net::Stack: - `LoopbackDriver` pair: two in-memory `Pipe`s (queue + waker) bridge two `embassy_net::Stack` instances. No kernel TUN, no privileges; runs in any CI without setup. `HardwareAddress::Ip` medium skips ARP/Ethernet — pure IP traffic over the loopback. - `adapter_udp_roundtrip`: two stacks on 169.254.1.1 / .1.2, two `EmbassyNetFactory` + `SocketPool` pairs, bind a socket on each, send a UDP datagram A→B, assert byte-equality + source address. Tightest end-to-end exercise of `bind` / `send_to` / `recv_from` / `local_addr` / `SocketPool` slot lifecycle. - Runtime: `#[tokio::test(flavor = "current_thread")]` + a `LocalSet` driving per-stack `spawn_local` runners. `Stack<LoopbackDriver>` is `!Sync` (RefCell internals), so `Stack::run()` is `!Send` — multi-thread `tokio::spawn` does not type-check. The `current_thread` flavor matches the single-task model bare-metal targets actually run under. - Cargo: adds `tokio` (rt-multi-thread, macros, time, sync) and `futures` to dev-deps. What this leaves for follow-on phases: - 19f — `Server::new_with_deps_local` + parallel `impl Server` block with relaxed `Send + Sync` bounds. Required because `embassy_net::udp::UdpSocket<'static>` is `!Sync` (borrows from `Stack`'s `RefCell<Inner<D>>`), and Server's existing three impl blocks (`mod.rs:275/430/1065`) all require `F::Socket: Send + Sync`. Mirrors Client's existing `new_with_deps_local` pattern. - 19g — SOME/IP Client+Server integration test. Lifts the `tests/bare_metal_e2e.rs` harness onto the loopback stack pair using the 19f `_local` API. Mirrors the parent crate's `client_receives_server_sd_announcement` and `client_send_request_server_runloop_stable`, with `EmbassyNetFactory` swapped in for `MockFactory`. Scope split rationale: per phase_13_5_lessons.md lesson #2, "abstract over X" and "drop X" are separate commitments — bundling the Server bound-relaxation under "loopback test" would repeat the v2 phases-11/13a aspirational-gate mistake. Plan v3 updated 2026-04-29 (memory) with the 19e/f/g/h/i/j re-numbering. Gates green: - cargo fmt --check - cargo clippy -p simple-someip-embassy-net --all-targets -D warnings - cargo test -p simple-someip-embassy-net --test loopback - cargo build -p simple-someip-embassy-net --target thumbv7em-none-eabihf - cargo check --workspace --all-targets Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Missed in 10fdfdc; adds tokio + futures-util + transitive deps that the loopback test pulls in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a host-runnable loopback integration test to validate the simple-someip-embassy-net transport adapter against a real embassy_net::Stack, without requiring a kernel TUN/TAP device or special privileges.
Changes:
- Introduces
tests/loopback.rswith an in-memoryLoopbackDriverbridging twoembassy_net::Stackinstances and a UDP roundtrip assertion. - Updates
simple-someip-embassy-netdev-dependencies to support the Tokio-based test harness. - Refreshes
Cargo.lockfor newly added dev-deps.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| simple-someip-embassy-net/tests/loopback.rs | Implements a software loopback Driver and a Tokio LocalSet-driven UDP roundtrip test across two stacks. |
| simple-someip-embassy-net/Cargo.toml | Updates host-test dev-deps and adds Tokio (+ futures) for the new test harness. |
| Cargo.lock | Locks new transitive dependencies introduced by the dev-dependency updates. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # `tokio::spawn` for the per-stack `Stack::run()` futures, and | ||
| # `tokio::time::timeout` for bounded assertions. Same shape as the | ||
| # parent crate's `tests/bare_metal_e2e.rs` harness. |
There was a problem hiding this comment.
These comments say the harness uses tokio::spawn for the per-stack Stack::run() tasks, but the test uses tokio::task::spawn_local (required by LocalSet / current_thread). Updating the comment will avoid confusion about why rt-multi-thread isn’t actually needed for the test.
| # `tokio::spawn` for the per-stack `Stack::run()` futures, and | |
| # `tokio::time::timeout` for bounded assertions. Same shape as the | |
| # parent crate's `tests/bare_metal_e2e.rs` harness. | |
| # `tokio::task::spawn_local` for the per-stack `Stack::run()` futures | |
| # within a `LocalSet` / current-thread runtime, and | |
| # `tokio::time::timeout` for bounded assertions. Same overall shape as | |
| # the parent crate's `tests/bare_metal_e2e.rs` harness. |
| # `futures` brings `select_biased!` / `FusedFuture` / `pin_mut!` into | ||
| # scope for the test driver. | ||
| futures = "0.3" |
There was a problem hiding this comment.
The crate adds futures = "0.3" as a dev-dependency, but there’s no code in simple-someip-embassy-net (including tests/loopback.rs) that actually uses the futures crate right now. Consider dropping this dev-dep until it’s needed (or add the missing usage) to keep the dependency surface minimal.
| # `futures` brings `select_biased!` / `FusedFuture` / `pin_mut!` into | |
| # scope for the test driver. | |
| futures = "0.3" |
| //! * **`adapter_udp_roundtrip`** — bind two `EmbassyNetSocket`s, | ||
| //! one per stack, send a UDP datagram from A to B, assert | ||
| //! byte-equality + source-address. Tightest test of `bind` / | ||
| //! `send_to` / `recv_from` / `local_addr` end-to-end. |
There was a problem hiding this comment.
The module docs claim this test exercises local_addr end-to-end, but the test body never calls local_addr(). Either add an assertion that checks sock_a.local_addr() / sock_b.local_addr() matches the bound address, or adjust the docs to reflect what’s actually covered.
| //! `send_to` / `recv_from` / `local_addr` end-to-end. | |
| //! `send_to` / `recv_from` end-to-end. |
| /// Build a stack on `ip/24` with our `LoopbackDriver`. Returns a | ||
| /// `&'static Stack<LoopbackDriver>` ready for `EmbassyNetFactory` | ||
| /// and a separately-leaked future to `tokio::spawn` for the | ||
| /// stack's run loop. |
There was a problem hiding this comment.
This doc comment says build_stack returns a separately-leaked future to tokio::spawn, but the function only returns &'static Stack<LoopbackDriver>. Update the comment (or function signature) so the documentation matches the actual API.
| /// Build a stack on `ip/24` with our `LoopbackDriver`. Returns a | |
| /// `&'static Stack<LoopbackDriver>` ready for `EmbassyNetFactory` | |
| /// and a separately-leaked future to `tokio::spawn` for the | |
| /// stack's run loop. | |
| /// Build a stack on `ip/24` with our `LoopbackDriver` and return a | |
| /// leaked `&'static Stack<LoopbackDriver>` ready for | |
| /// `EmbassyNetFactory`. |
Validates 19a-c against a real embassy_net::Stack:
LoopbackDriverpair: two in-memoryPipes (queue + waker)bridge two
embassy_net::Stackinstances. No kernel TUN, noprivileges; runs in any CI without setup.
HardwareAddress::Ipmedium skips ARP/Ethernet — pure IP traffic over the loopback.
adapter_udp_roundtrip: two stacks on 169.254.1.1 / .1.2,two
EmbassyNetFactory+SocketPoolpairs, bind a socket oneach, send a UDP datagram A→B, assert byte-equality + source
address. Tightest end-to-end exercise of
bind/send_to/recv_from/local_addr/SocketPoolslot lifecycle.#[tokio::test(flavor = "current_thread")]+ aLocalSetdriving per-stackspawn_localrunners.Stack<LoopbackDriver>is!Sync(RefCell internals), soStack::run()is!Send— multi-threadtokio::spawndoesnot type-check. The
current_threadflavor matches thesingle-task model bare-metal targets actually run under.
tokio(rt-multi-thread, macros, time, sync) andfuturesto dev-deps.What this leaves for follow-on phases:
Server::new_with_deps_local+ parallelimpl Serverblock with relaxed
Send + Syncbounds. Required becauseembassy_net::udp::UdpSocket<'static>is!Sync(borrowsfrom
Stack'sRefCell<Inner<D>>), and Server's existingthree impl blocks (
mod.rs:275/430/1065) all requireF::Socket: Send + Sync. Mirrors Client's existingnew_with_deps_localpattern.tests/bare_metal_e2e.rsharness onto the loopback stackpair using the 19f
_localAPI. Mirrors the parent crate'sclient_receives_server_sd_announcementandclient_send_request_server_runloop_stable, withEmbassyNetFactoryswapped in forMockFactory.Scope split rationale: per phase_13_5_lessons.md lesson #2,
"abstract over X" and "drop X" are separate commitments —
bundling the Server bound-relaxation under "loopback test"
would repeat the v2 phases-11/13a aspirational-gate mistake.
Plan v3 updated 2026-04-29 (memory) with the 19e/f/g/h/i/j
re-numbering.
Gates green: