windowed_stats/experimental/
testing.rs1use derivative::Derivative;
6use fuchsia_sync::Mutex;
7use std::any::{self, Any};
8use std::collections::HashMap;
9use std::marker::PhantomData;
10use std::sync::Arc;
11
12use crate::experimental::clock::{Timed, Timestamp};
13use crate::experimental::inspect::{InspectSender, InspectedTimeMatrix};
14use crate::experimental::series::interpolation::InterpolationKind;
15use crate::experimental::series::statistic::{FoldError, Metadata, SerialStatistic};
16use crate::experimental::series::{SerializedBuffer, TimeMatrix, TimeMatrixFold, TimeMatrixTick};
17
18type DynamicSample = Box<dyn Any + Send>;
19
20#[derive(Derivative)]
21#[derivative(Debug, PartialEq)]
22pub enum TimeMatrixCall<T> {
23 Fold(Timed<T>),
24 Tick(Timestamp),
25}
26
27impl<T> TimeMatrixCall<T> {
28 fn map<U, F>(self, f: F) -> TimeMatrixCall<U>
29 where
30 F: FnOnce(T) -> U,
31 {
32 match self {
33 TimeMatrixCall::Fold(timed) => TimeMatrixCall::Fold(timed.map(f)),
34 TimeMatrixCall::Tick(timestamp) => TimeMatrixCall::Tick(timestamp),
35 }
36 }
37}
38
39impl<T, E> TimeMatrixCall<Result<T, E>> {
40 fn transpose(self) -> Result<TimeMatrixCall<T>, E> {
41 match self {
42 TimeMatrixCall::Fold(result) => match result.transpose() {
43 Ok(sample) => Ok(TimeMatrixCall::Fold(sample)),
44 Err(error) => Err(error),
45 },
46 TimeMatrixCall::Tick(timestamp) => Ok(TimeMatrixCall::Tick(timestamp)),
47 }
48 }
49}
50
51#[derive(Debug)]
52pub struct TimeMatrixCallLog {
53 calls: HashMap<String, Vec<TimeMatrixCall<DynamicSample>>>,
54}
55
56impl TimeMatrixCallLog {
57 pub fn drain<T: Any + Send + Clone>(&mut self, name: &str) -> Vec<TimeMatrixCall<T>> {
58 self.calls
59 .remove(name)
60 .unwrap_or_default()
61 .into_iter()
62 .map(|call| call.map(|sample| sample.downcast::<T>().map(|sample| *sample)))
63 .map(TimeMatrixCall::transpose)
64 .collect::<Result<Vec<_>, _>>()
65 .unwrap_or_else(|_| {
66 panic!(
67 "in time matrix \"{}\": failed to downcast dynamic sample of type `{}`",
68 name,
69 any::type_name::<T>()
70 )
71 })
72 }
73
74 pub fn as_hash_map(&self) -> &HashMap<String, Vec<TimeMatrixCall<DynamicSample>>> {
75 &self.calls
76 }
77
78 pub fn is_empty(&self) -> bool {
79 self.calls.is_empty()
80 }
81}
82
83#[derive(Clone)]
84pub struct MockTimeMatrixClient {
85 calls: Arc<Mutex<Vec<(String, TimeMatrixCall<DynamicSample>)>>>,
86 prefix: String,
87}
88
89impl MockTimeMatrixClient {
90 pub fn new() -> Self {
91 Self { calls: Arc::new(Mutex::new(vec![])), prefix: String::new() }
92 }
93}
94
95impl MockTimeMatrixClient {
96 pub fn drain_calls(&self) -> TimeMatrixCallLog {
97 let mut calls = HashMap::<_, Vec<_>>::new();
98 for (name, call) in self.calls.lock().drain(..) {
99 calls.entry(name).or_default().push(call);
100 }
101 TimeMatrixCallLog { calls }
102 }
103}
104
105impl InspectSender for MockTimeMatrixClient {
106 fn inspect_time_matrix<F, P>(
107 &self,
108 name: impl Into<String>,
109 _matrix: TimeMatrix<F, P>,
110 ) -> InspectedTimeMatrix<F::Sample>
111 where
112 TimeMatrix<F, P>: 'static + TimeMatrixFold<F::Sample> + Send,
113 Metadata<F>: 'static + Send + Sync,
114 F: SerialStatistic<P>,
115 F::Sample: Send,
116 P: InterpolationKind,
117 {
118 let name = format!("{}{}", self.prefix, name.into());
119 let matrix = MockTimeMatrix {
120 name: name.clone(),
121 calls: Arc::clone(&self.calls),
122 phantom: std::marker::PhantomData,
123 };
124 InspectedTimeMatrix::new(name, Arc::new(Mutex::new(matrix)))
125 }
126
127 fn inspect_time_matrix_with_metadata<F, P>(
128 &self,
129 name: impl Into<String>,
130 matrix: TimeMatrix<F, P>,
131 _metadata: impl Into<Metadata<F>>,
132 ) -> InspectedTimeMatrix<F::Sample>
133 where
134 TimeMatrix<F, P>: 'static + TimeMatrixFold<F::Sample> + Send,
135 Metadata<F>: 'static + Send + Sync,
136 F: SerialStatistic<P>,
137 F::Sample: Send,
138 P: InterpolationKind,
139 {
140 self.inspect_time_matrix(name, matrix)
141 }
142
143 fn clone_with_child(&self, name: &str) -> Self {
144 let mut new_prefix = self.prefix.clone();
145 new_prefix.push_str(name);
146 new_prefix.push_str("/");
147 Self { calls: Arc::clone(&self.calls), prefix: new_prefix }
148 }
149}
150
151struct MockTimeMatrix<T> {
152 name: String,
153 calls: Arc<Mutex<Vec<(String, TimeMatrixCall<DynamicSample>)>>>,
154 phantom: PhantomData<fn() -> T>,
155}
156
157impl<T> MockTimeMatrix<T> {
158 pub fn new(
159 name: impl Into<String>,
160 calls: Arc<Mutex<Vec<(String, TimeMatrixCall<DynamicSample>)>>>,
161 ) -> Self {
162 MockTimeMatrix { name: name.into(), calls, phantom: PhantomData }
163 }
164}
165
166impl<T> TimeMatrixFold<T> for MockTimeMatrix<T>
167where
168 T: 'static + Send,
169{
170 fn fold(&mut self, sample: Timed<T>) -> Result<(), FoldError> {
171 let sample = sample.map(|v| Box::new(v) as DynamicSample);
172 self.calls.lock().push((self.name.clone(), TimeMatrixCall::Fold(sample)));
173 Ok(())
174 }
175}
176
177impl<T> TimeMatrixTick for MockTimeMatrix<T> {
178 fn tick(&mut self, timestamp: Timestamp) -> Result<(), FoldError> {
179 self.calls.lock().push((self.name.clone(), TimeMatrixCall::Tick(timestamp)));
180 Ok(())
181 }
182
183 fn tick_and_get_buffers(
184 &mut self,
185 timestamp: Timestamp,
186 ) -> Result<SerializedBuffer, FoldError> {
187 self.tick(timestamp)?;
188 Ok(SerializedBuffer { data_semantic: "mock".to_string(), data: vec![] })
189 }
190}