Skip to main content

windowed_stats/experimental/
testing.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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}