Skip to content

phase 19g: SOME/IP Client+Server roundtrip over embassy-net loopback#103

Draft
JustinKovacich wants to merge 1 commit into
feature/phase19f_server_local_boundsfrom
feature/phase19g_someip_loopback_test
Draft

phase 19g: SOME/IP Client+Server roundtrip over embassy-net loopback#103
JustinKovacich wants to merge 1 commit into
feature/phase19f_server_local_boundsfrom
feature/phase19g_someip_loopback_test

Conversation

@JustinKovacich
Copy link
Copy Markdown
Contributor

Lifts the parent crate's tests/bare_metal_e2e.rs harness onto the two-stack LoopbackDriver pair from 19e, using 19f's relaxed Server bounds (SocketHandle abstraction) so an Arc<EmbassyNetSocket>!Sync because embassy-net's UdpSocket<'static> borrows from Stack's RefCell<Inner<D>> — satisfies H for the Server.

Two new tests in simple-someip-embassy-net/tests/loopback.rs:

  • client_receives_server_sd_announcement: real Server on stack A emits SD OfferService via announcement_loop_local (19f's !Send variant), real Client on stack B receives it via bind_discovery. Asserts a ClientUpdate::DiscoveryUpdated surfaces within 5 s. Both stacks join 224.0.23.0 at the smoltcp level before construction (the adapter's join_multicast_v4 is a documented no-op since multicast group membership lives on the Stack, not the UdpSocket).
  • client_send_request_server_runloop_stable: passive Server on stack A; Client on stack B does add_endpoint + send_to_service to push a SOME/IP request through the loopback. Asserts the send returns Ok and the run-loop survives. No response assertion because simple_someip::Server exposes no public request-handler API — matching the parent crate's reference test.

Harness additions:

  • define_static_channels! LoopbackTestChannels with the same pool sizing shape as tests/bare_metal_e2e.rs's E2ETestChannels.
  • LocalTokioSpawner: LocalSpawner over tokio::task::spawn_local. LocalSpawner (not Spawner) because the Client's run-future captures &self.unicast_socket-style borrows across awaits over a !Sync socket, making the run future itself !Send.
  • LocalTimer: Timer over tokio::time::sleep, boxed-future shape matching the bare-metal-e2e MockTimer.
  • MockSubscriptions(Arc<Mutex<Vec<SubKey>>>) — same shape as bare-metal-e2e's mock.

Cargo.toml: dev-dependency simple-someip is now re-pinned with features = ["client", "server", "bare_metal", "std"]. The std addition gates RawPayload / VecSdHeader / Arc<Mutex<E2ERegistry>> default impls — all needed for the host-side test harness — without affecting the production [dependencies] build (which stays default-features = false).

Type-inference notes:

  • Both Server constructions in the new tests carry an explicit Server<_, _, _, _, Arc<simple_someip_embassy_net::EmbassyNetSocket>> annotation. Without it the compiler can't decide H across the ServerDeps indirection — same situation as simple_someip's own SD-NACK test (mod.rs:1345) addresses.
  • embassy_net::Stack::join_multicast_group takes T: Into<IpAddress> but embassy-net 0.4 has no core::net::Ipv4Addr -> IpAddress blanket impl. Constructed via embassy_net::Ipv4Address(addr.octets()) per the smoltcp shape.

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 (3/3 pass)
  • cargo build -p simple-someip-embassy-net --target thumbv7em-none-eabihf
  • cargo check --workspace --all-targets

Phase 19 status: 19a-g all complete. Remaining 19 sub-phases:

  • 19h — examples/embassy_net_client/ in-tree binary example.
  • 19i — CI: cross-build for thumbv7em + run loopback test.
  • 19j — Adapter README / CHANGELOG / simple-someip-embassy-net 0.1.0 release.

Lifts the parent crate's `tests/bare_metal_e2e.rs` harness onto the
two-stack `LoopbackDriver` pair from 19e, using 19f's relaxed
`Server` bounds (`SocketHandle` abstraction) so an `Arc<EmbassyNetSocket>`
— `!Sync` because embassy-net's `UdpSocket<'static>` borrows from
`Stack`'s `RefCell<Inner<D>>` — satisfies `H` for the Server.

Two new tests in `simple-someip-embassy-net/tests/loopback.rs`:

- `client_receives_server_sd_announcement`: real Server on stack A
  emits SD `OfferService` via `announcement_loop_local` (19f's
  `!Send` variant), real Client on stack B receives it via
  `bind_discovery`. Asserts a `ClientUpdate::DiscoveryUpdated`
  surfaces within 5 s. Both stacks join 224.0.23.0 at the smoltcp
  level before construction (the adapter's `join_multicast_v4` is
  a documented no-op since multicast group membership lives on
  the `Stack`, not the `UdpSocket`).
- `client_send_request_server_runloop_stable`: passive Server on
  stack A; Client on stack B does `add_endpoint` + `send_to_service`
  to push a SOME/IP request through the loopback. Asserts the
  send returns Ok and the run-loop survives. No response assertion
  because `simple_someip::Server` exposes no public request-handler
  API — matching the parent crate's reference test.

Harness additions:

- `define_static_channels!` `LoopbackTestChannels` with the same
  pool sizing shape as `tests/bare_metal_e2e.rs`'s `E2ETestChannels`.
- `LocalTokioSpawner: LocalSpawner` over `tokio::task::spawn_local`.
  `LocalSpawner` (not `Spawner`) because the Client's run-future
  captures `&self.unicast_socket`-style borrows across awaits over
  a `!Sync` socket, making the run future itself `!Send`.
- `LocalTimer: Timer` over `tokio::time::sleep`, boxed-future
  shape matching the bare-metal-e2e `MockTimer`.
- `MockSubscriptions(Arc<Mutex<Vec<SubKey>>>)` — same shape as
  bare-metal-e2e's mock.

Cargo.toml: dev-dependency `simple-someip` is now re-pinned with
`features = ["client", "server", "bare_metal", "std"]`. The `std`
addition gates `RawPayload` / `VecSdHeader` / `Arc<Mutex<E2ERegistry>>`
default impls — all needed for the host-side test harness — without
affecting the production `[dependencies]` build (which stays
`default-features = false`).

Type-inference notes:

- Both Server constructions in the new tests carry an explicit
  `Server<_, _, _, _, Arc<simple_someip_embassy_net::EmbassyNetSocket>>`
  annotation. Without it the compiler can't decide `H` across the
  `ServerDeps` indirection — same situation as `simple_someip`'s
  own SD-NACK test (`mod.rs:1345`) addresses.
- `embassy_net::Stack::join_multicast_group` takes
  `T: Into<IpAddress>` but embassy-net 0.4 has no
  `core::net::Ipv4Addr -> IpAddress` blanket impl. Constructed
  via `embassy_net::Ipv4Address(addr.octets())` per the smoltcp
  shape.

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 (3/3 pass)
- cargo build -p simple-someip-embassy-net --target thumbv7em-none-eabihf
- cargo check --workspace --all-targets

Phase 19 status: 19a-g all complete. Remaining 19 sub-phases:
- 19h — `examples/embassy_net_client/` in-tree binary example.
- 19i — CI: cross-build for thumbv7em + run loopback test.
- 19j — Adapter README / CHANGELOG / `simple-someip-embassy-net` 0.1.0 release.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-to-end SOME/IP Client+Server integration coverage to the simple-someip-embassy-net adapter by running two embassy_net::Stack instances connected via an in-memory loopback driver, and updates test-only dependencies to support host-side execution.

Changes:

  • Extend tests/loopback.rs from adapter-level UDP validation to full Client+Server roundtrip scenarios over the two-stack LoopbackDriver setup.
  • Introduce a host-only harness in the loopback tests (static channel pools, LocalSpawner, Timer, mock subscriptions) to drive !Send/!Sync embassy-net sockets on a single-threaded Tokio runtime.
  • Re-pin simple-someip as a dev-dependency with std enabled so the host-side test harness can use std-gated types/impls.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
simple-someip-embassy-net/tests/loopback.rs Adds two SOME/IP-level loopback integration tests plus local spawner/timer/subscription harness to support !Send execution.
simple-someip-embassy-net/Cargo.toml Enables simple-someip std feature for tests via dev-dependency override.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +654 to +658
// Drive the run-loop locally — `!Send` because
// `EmbassyNetSocket: !Sync`.
tokio::task::spawn_local(async move {
let _ = server.run().await;
});
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test constructs a passive server via Server::new_passive_with_deps, but then calls server.run(). In simple_someip::Server, run() on a passive server returns Err(Error::InvalidUsage("passive_server_run")) immediately, so this spawned task exits right away and the test doesn’t actually exercise a stable server run-loop or prove the request is received. Use Server::new_with_deps (and simply don’t start announcement_loop_local) if you need a running loop, or adjust the test expectations accordingly.

Copilot uses AI. Check for mistakes.
// 17.

use core::pin::Pin;
use core::task::Poll;
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Poll isn’t needed for the LocalSpawner impl in this file, and the later dummy _poll_use function exists only to silence an unused-import warning. Please remove the Poll import and the dummy function, or (if you really expect future hand-rolled futures here) use a targeted #[allow(unused_imports)] on the import instead of adding dead code.

Suggested change
use core::task::Poll;

Copilot uses AI. Check for mistakes.
Comment on lines +464 to +474
// `Poll` is imported above for `LocalSpawner` impls; flag it as
// in-use so a `cargo clippy --tests -D warnings` build doesn't
// trip on the otherwise-unused import. (`Poll` is brought in
// because it's the canonical paired import alongside `Pin` for
// hand-rolled futures, even though `LoopbackTestChannels`'
// generated code uses the higher-level macro shape.)
#[allow(dead_code)]
fn _poll_use(p: Poll<()>) -> Poll<()> {
p
}

Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment here says Poll is imported “for LocalSpawner impls”, but the LocalSpawner impl doesn’t use Poll; this block is only present to justify keeping an otherwise-unused import. That makes the rationale misleading and adds noise to the test harness—prefer removing the import and this helper entirely.

Suggested change
// `Poll` is imported above for `LocalSpawner` impls; flag it as
// in-use so a `cargo clippy --tests -D warnings` build doesn't
// trip on the otherwise-unused import. (`Poll` is brought in
// because it's the canonical paired import alongside `Pin` for
// hand-rolled futures, even though `LoopbackTestChannels`'
// generated code uses the higher-level macro shape.)
#[allow(dead_code)]
fn _poll_use(p: Poll<()>) -> Poll<()> {
p
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants