Skip to content

Commit 0c7f0ca

Browse files
authored
Remove hashing requirement for call spy map
1 parent d2b59c5 commit 0c7f0ca

File tree

1 file changed

+65
-47
lines changed

1 file changed

+65
-47
lines changed

src/test_utils/spy/call_spy_map.rs

Lines changed: 65 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
use crate::prelude::*;
22

3-
use std::collections::hash_map;
4-
use std::collections::HashMap;
53
use std::collections::VecDeque;
6-
use std::hash::Hash;
74

85
#[derive(Debug)]
96
struct SpyState<I, O> {
7+
key: K,
108
args: Vec<I>,
119
results: VecDeque<O>,
1210
}
1311

1412
pub struct CallSpyMap<K, I, O> {
15-
states: Mutex<Option<HashMap<K, SpyState<I, O>>>>,
13+
states: Mutex<Vec<SpyState<K, I, O>>>,
1614
name: &'static str,
1715
}
1816

19-
impl<K: Eq + Hash, I, O> CallSpyMap<K, I, O> {
17+
impl<K: PartialEq, I, O> CallSpyMap<K, I, O> {
2018
pub const fn new(name: &'static str) -> Self {
2119
Self {
2220
states: Mutex::new(None),
@@ -27,15 +25,18 @@ impl<K: Eq + Hash, I, O> CallSpyMap<K, I, O> {
2725
pub fn enqueue(&self, key: K, result: O) {
2826
let mut guard = self.states.lock().unwrap_or_else(|e| e.into_inner());
2927

30-
let state = match guard.get_or_insert_with(HashMap::new).entry(key) {
31-
hash_map::Entry::Occupied(o) => o.into_mut(),
32-
hash_map::Entry::Vacant(v) => v.insert(SpyState {
33-
args: Vec::new(),
34-
results: VecDeque::new(),
35-
}),
36-
};
28+
for state in guard.iter_mut() {
29+
if state.key == key {
30+
state.results.push_back(result);
31+
return;
32+
}
33+
}
3734

38-
state.results.push_back(result);
35+
guard.push(SpyState {
36+
key,
37+
args: Vec::new(),
38+
results: VecDeque::from([result]),
39+
});
3940
}
4041

4142
#[must_use]
@@ -44,8 +45,9 @@ impl<K: Eq + Hash, I, O> CallSpyMap<K, I, O> {
4445
K: fmt::Debug,
4546
{
4647
let mut guard = self.states.lock().unwrap_or_else(|e| e.into_inner());
47-
if let Some(map) = &mut *guard {
48-
if let Some(state) = map.get_mut(&key) {
48+
49+
for state in guard.iter_mut() {
50+
if state.key == key {
4951
if let Some(result) = state.results.pop_front() {
5052
state.args.push(args);
5153
return result;
@@ -63,19 +65,25 @@ impl<K: Eq + Hash, I, O> CallSpyMap<K, I, O> {
6365
I: fmt::Debug,
6466
O: fmt::Debug,
6567
{
66-
let mut results = HashMap::new();
68+
use std::fmt::Write;
69+
70+
let mut results = String::new();
71+
let mut prefix = "";
6772

6873
let guard = self.states.lock().unwrap_or_else(|e| e.into_inner());
69-
for (key, state) in guard.iter().flatten() {
74+
75+
for (key, state) in guard.iter() {
7076
if !state.results.is_empty() {
71-
results.insert(key, &state.results);
77+
write!(&mut result, "{}{:?} => {:?}", prefix, key, &state.results)
78+
.unwrap();
79+
prefix = ", ";
7280
}
7381
}
7482

75-
if !results.is_empty() {
83+
if !result.is_empty() {
7684
panic!(
77-
"Unexpected calls remaining for `{}`: {:?}",
78-
self.name, results
85+
"Unexpected calls remaining for `{}`: {{{}}}",
86+
self.name, result
7987
);
8088
}
8189
}
@@ -87,14 +95,16 @@ impl<K: Eq + Hash, I, O> CallSpyMap<K, I, O> {
8795
I: fmt::Debug + PartialEq,
8896
K: fmt::Debug,
8997
{
90-
let mut expected_map: HashMap<&K, Vec<&I>> = HashMap::new();
98+
let mut expected_map = Vec::<(&K, Vec<&I>)>::new();
9199

92-
for (key, value) in expected {
93-
let vec = match expected_map.entry(key) {
94-
hash_map::Entry::Occupied(o) => o.into_mut(),
95-
hash_map::Entry::Vacant(v) => v.insert(Vec::new()),
96-
};
97-
vec.push(value);
100+
'outer: for (key, value) in expected {
101+
for (k, v) of expected_map.iter_mut() {
102+
if k == key {
103+
v.push(value);
104+
continue 'outer;
105+
}
106+
}
107+
expected_map.push((key, vec![value]));
98108
}
99109

100110
fn args_equal<I: PartialEq>(expected: Option<&[&I]>, actual: &[I]) -> bool {
@@ -114,9 +124,9 @@ impl<K: Eq + Hash, I, O> CallSpyMap<K, I, O> {
114124
}
115125
}
116126

117-
fn states_equal<K: Eq + Hash, I: PartialEq, O>(
118-
expected_map: &HashMap<&K, Vec<&I>>,
119-
states: &Option<HashMap<K, SpyState<I, O>>>,
127+
fn states_equal<K: PartialEq, I: PartialEq, O>(
128+
expected_map: &[(&K, Vec<&I>)],
129+
states: &[SpyState<K, I, O>],
120130
) -> bool {
121131
let Some(states) = states.as_ref() else {
122132
return expected_map.is_empty();
@@ -126,20 +136,18 @@ impl<K: Eq + Hash, I, O> CallSpyMap<K, I, O> {
126136
return false;
127137
}
128138

129-
for (key, state) in states.iter() {
130-
let Some(expected) = expected_map.get(key) else {
131-
return false;
132-
};
133-
134-
if expected.len() != state.args.len() {
135-
return false;
136-
}
139+
'outer: for state in states {
140+
for (key, expected) in expected_map {
141+
if key == states.key {
142+
if !state.args.iter().eq(expected) {
143+
return false;
144+
}
137145

138-
for (a, b) in expected.iter().zip(state.args.iter()) {
139-
if a != &b {
140-
return false;
146+
continue 'outer;
141147
}
142148
}
149+
150+
return false;
143151
}
144152

145153
true
@@ -148,27 +156,37 @@ impl<K: Eq + Hash, I, O> CallSpyMap<K, I, O> {
148156
let states = self.states.lock().unwrap_or_else(|e| e.into_inner());
149157

150158
if !states_equal(&expected_map, &states) {
151-
struct ActualStates<'a, K, I, O>(&'a Option<HashMap<K, SpyState<I, O>>>);
159+
struct ExpectedStates<'a, K, I>(&'a [(&K, Vec<&I>)]);
160+
161+
impl<K: PartialEq + fmt::Debug, I: fmt::Debug> fmt::Debug for ExpectedStates<'_, K, I> {
162+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163+
f.debug_map()
164+
.entries(self.0.iter().map(|s| (&s.0, &s.1)))
165+
.finish()
166+
}
167+
}
168+
169+
struct ActualStates<'a, K, I, O>(&'a [SpyState<K, I, O>]);
152170

153-
impl<K: Eq + Hash + fmt::Debug, I: fmt::Debug, O> fmt::Debug for ActualStates<'_, K, I, O> {
171+
impl<K: PartialEq + fmt::Debug, I: fmt::Debug, O> fmt::Debug for ActualStates<'_, K, I, O> {
154172
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155173
f.debug_map()
156-
.entries(self.0.iter().flatten().map(|(k, v)| (k, &v.args)))
174+
.entries(self.0.iter().map(|s| (&s.key, &s.args)))
157175
.finish()
158176
}
159177
}
160178

161179
panic!(
162180
"Calls for `{}` do not match.\nExpected: {:?}\n Actual: {:?}",
163181
self.name,
164-
expected_map,
182+
ExpectedStates(&expected_map),
165183
ActualStates(&*states)
166184
)
167185
}
168186
}
169187
}
170188

171-
impl<K: Eq + Hash, I, O> CallSpyMap<K, I, io::Result<O>> {
189+
impl<K: PartialEq, I, O> CallSpyMap<K, I, io::Result<O>> {
172190
pub fn enqueue_io(&self, key: K, result: Result<O, libc::c_int>) {
173191
self.enqueue(key, result.map_err(Error::from_raw_os_error));
174192
}

0 commit comments

Comments
 (0)