mock_crash_reporter/
lib.rs
1use fidl_fuchsia_feedback::{
6 CrashReporterMarker, CrashReporterProxy, CrashReporterRequestStream, FileReportResults,
7 FilingError,
8};
9use fuchsia_async::Task;
10use futures::channel::mpsc;
11use futures::future::BoxFuture;
12use futures::lock::Mutex;
13use futures::prelude::*;
14use futures::TryStreamExt;
15use std::sync::Arc;
16
17pub use fidl_fuchsia_feedback::CrashReport;
18
19pub trait Hook: Send + Sync {
21 fn file_report(
23 &self,
24 report: CrashReport,
25 ) -> BoxFuture<'static, Result<FileReportResults, FilingError>>;
26}
27
28impl<F> Hook for F
29where
30 F: Fn(CrashReport) -> Result<FileReportResults, FilingError> + Send + Sync,
31{
32 fn file_report(
33 &self,
34 report: CrashReport,
35 ) -> BoxFuture<'static, Result<FileReportResults, FilingError>> {
36 future::ready(self(report)).boxed()
37 }
38}
39
40pub struct MockCrashReporterService {
41 call_hook: Box<dyn Hook>,
42}
43
44impl MockCrashReporterService {
45 pub fn new(hook: impl Hook + 'static) -> Self {
47 Self { call_hook: Box::new(hook) }
48 }
49
50 pub fn spawn_crash_reporter_service(self: Arc<Self>) -> (CrashReporterProxy, Task<()>) {
52 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<CrashReporterMarker>();
53
54 let task = Task::spawn(self.run_crash_reporter_service(stream));
55
56 (proxy, task)
57 }
58
59 pub async fn run_crash_reporter_service(
61 self: Arc<Self>,
62 mut stream: CrashReporterRequestStream,
63 ) {
64 while let Some(event) = stream.try_next().await.expect("received CrashReporter request") {
65 match event {
66 fidl_fuchsia_feedback::CrashReporterRequest::FileReport { report, responder } => {
67 let res = self.call_hook.file_report(report).await;
68 match res {
69 Err(_) => responder.send(Err(FilingError::InvalidArgsError)).unwrap(),
70 Ok(_) => responder.send(Ok(&FileReportResults::default())).unwrap(),
71 }
72 }
73 }
74 }
75 }
76}
77
78pub struct ThrottleHook {
81 file_response: Result<FileReportResults, FilingError>,
82 sender: Arc<Mutex<mpsc::Sender<CrashReport>>>,
83}
84
85impl ThrottleHook {
86 pub fn new(
87 file_response: Result<FileReportResults, FilingError>,
88 ) -> (Self, mpsc::Receiver<CrashReport>) {
89 let (sender, recv) = mpsc::channel(0);
92 (Self { file_response, sender: Arc::new(Mutex::new(sender)) }, recv)
93 }
94}
95impl Hook for ThrottleHook {
96 fn file_report(
97 &self,
98 report: CrashReport,
99 ) -> BoxFuture<'static, Result<FileReportResults, FilingError>> {
100 let sender = Arc::clone(&self.sender);
101 let file_response = self.file_response.clone();
102
103 async move {
104 sender.lock().await.send(report).await.unwrap();
105 file_response
106 }
107 .boxed()
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use fuchsia_async as fasync;
115 use std::sync::atomic::{AtomicU32, Ordering};
116
117 #[fasync::run_singlethreaded(test)]
118 async fn test_mock_crash_reporter() {
119 let mock = Arc::new(MockCrashReporterService::new(|_| Ok(FileReportResults::default())));
120 let (proxy, _server) = mock.spawn_crash_reporter_service();
121
122 let file_result = proxy.file_report(CrashReport::default()).await.expect("made fidl call");
123
124 assert_eq!(file_result, Ok(FileReportResults::default()));
125 }
126
127 #[fasync::run_singlethreaded(test)]
128 async fn test_mock_crash_reporter_fails() {
129 let mock = Arc::new(MockCrashReporterService::new(|_| Err(FilingError::InvalidArgsError)));
130 let (proxy, _server) = mock.spawn_crash_reporter_service();
131
132 let file_result = proxy.file_report(CrashReport::default()).await.expect("made fidl call");
133
134 assert_eq!(file_result, Err(FilingError::InvalidArgsError));
135 }
136
137 #[fasync::run_singlethreaded(test)]
138 async fn test_mock_crash_reporter_with_external_state() {
139 let called = Arc::new(AtomicU32::new(0));
140 let called_clone = Arc::clone(&called);
141 let mock = Arc::new(MockCrashReporterService::new(move |_| {
142 called_clone.fetch_add(1, Ordering::SeqCst);
143 Ok(FileReportResults::default())
144 }));
145 let (proxy, _server) = mock.spawn_crash_reporter_service();
146
147 let file_result = proxy.file_report(CrashReport::default()).await.expect("made fidl call");
148
149 assert_eq!(file_result, Ok(FileReportResults::default()));
150 assert_eq!(called.load(Ordering::SeqCst), 1);
151 }
152}