opentitanlib/test_utils/
rpc.rs1use anyhow::Result;
5use crc::{CRC_32_ISO_HDLC, Crc};
6use serde::Serialize;
7use serde::de::DeserializeOwned;
8use std::time::Duration;
9
10use crate::io::console::ext::{PassFail, PassFailResult};
11use crate::io::console::{ConsoleDevice, ConsoleError, ConsoleExt};
12use crate::regex;
13use crate::test_utils::status::Status;
14
15include!(env!("ottf"));
17
18pub trait ConsoleSend<T>
19where
20 T: ConsoleDevice + ?Sized,
21{
22 fn send(&self, device: &T) -> Result<String>;
23 fn send_with_crc(&self, device: &T) -> Result<String>;
24 fn send_with_padding(&self, device: &T, max_size: usize) -> Result<String>;
25}
26
27impl<T, U> ConsoleSend<T> for U
28where
29 T: ConsoleDevice + ?Sized,
30 U: Serialize,
31{
32 fn send(&self, device: &T) -> Result<String> {
33 let s = serde_json::to_string(self)?;
34 log::info!("Sending: {}", s);
35 device.write(s.as_bytes())?;
36 Ok(s)
37 }
38
39 fn send_with_crc(&self, device: &T) -> Result<String> {
40 let s = serde_json::to_string(self)?;
41 log::info!("Sending: {}", s);
42 device.write(s.as_bytes())?;
43 let actual_crc = OttfCrc {
44 crc: Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(s.as_bytes()),
45 };
46 let crc_s = actual_crc.send(device)?;
47 Ok(s + &crc_s)
48 }
49
50 fn send_with_padding(&self, device: &T, max_size: usize) -> Result<String> {
51 let mut s = serde_json::to_string(self)?;
52 let pad_len = max_size - s.len();
53 let pad_str = ' '.to_string().repeat(pad_len);
54 s.insert_str(s.len() - 1, &pad_str);
55 device.write(s.as_bytes())?;
56 Ok(s)
57 }
58}
59
60pub trait ConsoleRecv<T>
61where
62 T: ConsoleDevice + ?Sized,
63{
64 fn recv(device: &T, timeout: Duration, quiet: bool, skip_crc: bool) -> Result<Self>
65 where
66 Self: Sized;
67}
68
69impl<T, U> ConsoleRecv<T> for U
70where
71 T: ConsoleDevice + ?Sized,
72 U: DeserializeOwned,
73{
74 fn recv(device: &T, timeout: Duration, quiet: bool, skip_crc: bool) -> Result<Self>
75 where
76 Self: Sized,
77 {
78 let device: &dyn ConsoleDevice = if quiet { &device } else { &device.logged() };
79 let result = device.wait_for_line(
80 PassFail(
81 regex!(r"RESP_OK:(.*) CRC:([0-9]+)"),
82 regex!(r"RESP_ERR:(.*) CRC:([0-9]+)"),
83 ),
84 timeout,
85 )?;
86 println!();
87 match result {
88 PassFailResult::Pass(cap) => {
89 let json_str = &cap[1];
90 let crc_str = &cap[2];
91 if !skip_crc {
92 check_crc(json_str, crc_str)?;
93 }
94 Ok(serde_json::from_str::<Self>(json_str)?)
95 }
96 PassFailResult::Fail(cap) => {
97 let json_str = &cap[1];
98 let crc_str = &cap[2];
99 check_crc(json_str, crc_str)?;
100 Err(serde_json::from_str::<Status>(json_str)?)?
101 }
102 }
103 }
104}
105
106fn check_crc(json_str: &str, crc_str: &str) -> Result<()> {
107 let crc = crc_str.parse::<u32>()?;
108 let actual_crc = Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(json_str.as_bytes());
109 if crc != actual_crc {
110 return Err(
111 ConsoleError::GenericError("CRC didn't match received json body.".into()).into(),
112 );
113 }
114 Ok(())
115}