Skip to content

Commit dc0d90c

Browse files
committed
ipc/deferred: Add tests
1 parent 11345b8 commit dc0d90c

File tree

6 files changed

+411
-1
lines changed

6 files changed

+411
-1
lines changed

Cargo.lock

Lines changed: 159 additions & 0 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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,4 @@ postcard = "1.*"
5555
rand_core = "0.6.4"
5656
serde = { version = "1.0.*", default-features = false }
5757
tps6699x = { git = "https://github.com/OpenDevicePartnership/tps6699x" }
58+
tokio = { version = "1.42.0" }

embedded-service/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,14 @@ cortex-m.workspace = true
4343
[dev-dependencies]
4444
embassy-sync = { workspace = true, features = ["std"] }
4545
critical-section = { workspace = true, features = ["std"] }
46-
embassy-futures.workspace = true
46+
tokio = { workspace = true, features = ["rt", "macros", "time"] }
4747
embassy-time = { workspace = true, features = ["std"] }
4848
embassy-time-driver = { workspace = true }
4949
embassy-executor = { workspace = true, features = [
5050
"arch-std",
5151
"executor-thread",
5252
] }
53+
embassy-futures.workspace = true
5354

5455
[features]
5556
default = []

embedded-service/src/ipc/deferred.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,125 @@ impl<M: RawMutex, C, R> Request<'_, M, C, R> {
9797
self.channel.response.signal((response, self.request_id));
9898
}
9999
}
100+
101+
#[cfg(test)]
102+
mod tests {
103+
use super::*;
104+
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
105+
use embassy_sync::once_lock::OnceLock;
106+
use tokio::time::Duration;
107+
108+
#[test]
109+
fn test_autoincrement() {
110+
let channel = Channel::<CriticalSectionRawMutex, u32, u32>::new();
111+
for i in 0..100 {
112+
let id = channel.get_next_request_id();
113+
assert_eq!(id.0, i);
114+
}
115+
}
116+
117+
/// Mock commands
118+
#[derive(Debug)]
119+
enum Command {
120+
A,
121+
B,
122+
C,
123+
}
124+
125+
/// Mock responses
126+
#[derive(Debug, PartialEq)]
127+
enum Response {
128+
A,
129+
B,
130+
C,
131+
}
132+
133+
/// Mock command handler
134+
struct Handler {
135+
channel: Channel<CriticalSectionRawMutex, Command, Response>,
136+
}
137+
138+
impl Handler {
139+
/// Create a new handler
140+
fn new() -> Self {
141+
Self {
142+
channel: Channel::new(),
143+
}
144+
}
145+
146+
/// Process a command and return a response
147+
async fn process_request(&self, request: &Command) -> Response {
148+
match request {
149+
Command::A => Response::A,
150+
Command::B => Response::B,
151+
Command::C => {
152+
// Request that takes a while to finish
153+
tokio::time::sleep(Duration::from_millis(1000)).await;
154+
Response::C
155+
}
156+
}
157+
}
158+
159+
/// Send command A
160+
async fn send_a(&self) -> Response {
161+
self.channel.execute(Command::A).await
162+
}
163+
164+
/// Invoke command B
165+
async fn send_b(&self) -> Response {
166+
self.channel.execute(Command::B).await
167+
}
168+
169+
/// Invoke command C
170+
async fn send_c(&self) -> Response {
171+
self.channel.execute(Command::C).await
172+
}
173+
174+
/// Main processing task
175+
async fn process(&self) {
176+
loop {
177+
let request = self.channel.receive().await;
178+
let response = self.process_request(&request.command).await;
179+
request.respond(response);
180+
}
181+
}
182+
}
183+
184+
/// Task that executes command C followed by command A
185+
async fn task_0(handler: &'static Handler) {
186+
let response = tokio::time::timeout(Duration::from_millis(250), handler.send_c()).await;
187+
// Tokio's timeout error value has a private constructor so is_err is the best we can do
188+
assert!(response.is_err());
189+
190+
let response = handler.send_a().await;
191+
assert_eq!(response, Response::A);
192+
}
193+
194+
/// Task that executes command B
195+
async fn task_1(handler: &'static Handler) {
196+
let response = handler.send_b().await;
197+
assert_eq!(response, Response::B);
198+
}
199+
200+
/// Task that handles device commands
201+
async fn handler_task(handler: &'static Handler) {
202+
loop {
203+
handler.process().await;
204+
}
205+
}
206+
207+
/// Test the command execution and response handling
208+
#[tokio::test]
209+
async fn test_send_receive() {
210+
static DEVICE: OnceLock<Handler> = OnceLock::new();
211+
212+
let device = DEVICE.get_or_init(Handler::new);
213+
let _handler = tokio::spawn(handler_task(device));
214+
let handle_0 = tokio::spawn(task_0(device));
215+
let handle_1 = tokio::spawn(task_1(device));
216+
217+
// Wait for invokers to finish
218+
handle_0.await.unwrap();
219+
handle_1.await.unwrap();
220+
}
221+
}

0 commit comments

Comments
 (0)