]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | use tracing::{ |
2 | callsite, | |
3 | callsite::Callsite, | |
4 | field::{self, Field, Value, Visit}, | |
5 | metadata::Kind, | |
6 | }; | |
7 | ||
8 | use std::{collections::HashMap, fmt}; | |
9 | ||
10 | #[derive(Default, Debug, Eq, PartialEq)] | |
11 | pub struct Expect { | |
12 | fields: HashMap<String, MockValue>, | |
13 | only: bool, | |
14 | } | |
15 | ||
16 | #[derive(Debug)] | |
17 | pub struct MockField { | |
18 | name: String, | |
19 | value: MockValue, | |
20 | } | |
21 | ||
22 | #[derive(Debug, Eq, PartialEq)] | |
23 | pub enum MockValue { | |
24 | I64(i64), | |
25 | U64(u64), | |
26 | Bool(bool), | |
27 | Str(String), | |
28 | Debug(String), | |
29 | Any, | |
30 | } | |
31 | ||
32 | pub fn mock<K>(name: K) -> MockField | |
33 | where | |
34 | String: From<K>, | |
35 | { | |
36 | MockField { | |
37 | name: name.into(), | |
38 | value: MockValue::Any, | |
39 | } | |
40 | } | |
41 | ||
42 | impl MockField { | |
43 | /// Expect a field with the given name and value. | |
44 | pub fn with_value(self, value: &dyn Value) -> Self { | |
45 | Self { | |
46 | value: MockValue::from(value), | |
47 | ..self | |
48 | } | |
49 | } | |
50 | ||
51 | pub fn and(self, other: MockField) -> Expect { | |
52 | Expect { | |
53 | fields: HashMap::new(), | |
54 | only: false, | |
55 | } | |
56 | .and(self) | |
57 | .and(other) | |
58 | } | |
59 | ||
60 | pub fn only(self) -> Expect { | |
61 | Expect { | |
62 | fields: HashMap::new(), | |
63 | only: true, | |
64 | } | |
65 | .and(self) | |
66 | } | |
67 | } | |
68 | ||
17df50a5 XL |
69 | impl From<MockField> for Expect { |
70 | fn from(field: MockField) -> Self { | |
f035d41b XL |
71 | Expect { |
72 | fields: HashMap::new(), | |
73 | only: false, | |
74 | } | |
17df50a5 | 75 | .and(field) |
f035d41b XL |
76 | } |
77 | } | |
78 | ||
79 | impl Expect { | |
80 | pub fn and(mut self, field: MockField) -> Self { | |
81 | self.fields.insert(field.name, field.value); | |
82 | self | |
83 | } | |
84 | ||
85 | /// Indicates that no fields other than those specified should be expected. | |
86 | pub fn only(self) -> Self { | |
87 | Self { only: true, ..self } | |
88 | } | |
89 | ||
90 | fn compare_or_panic(&mut self, name: &str, value: &dyn Value, ctx: &str) { | |
91 | let value = value.into(); | |
92 | match self.fields.remove(name) { | |
93 | Some(MockValue::Any) => {} | |
94 | Some(expected) => assert!( | |
95 | expected == value, | |
96 | "\nexpected `{}` to contain:\n\t`{}{}`\nbut got:\n\t`{}{}`", | |
97 | ctx, | |
98 | name, | |
99 | expected, | |
100 | name, | |
101 | value | |
102 | ), | |
103 | None if self.only => panic!( | |
104 | "\nexpected `{}` to contain only:\n\t`{}`\nbut got:\n\t`{}{}`", | |
105 | ctx, self, name, value | |
106 | ), | |
107 | _ => {} | |
108 | } | |
109 | } | |
110 | ||
111 | pub fn checker(&mut self, ctx: String) -> CheckVisitor<'_> { | |
112 | CheckVisitor { expect: self, ctx } | |
113 | } | |
114 | ||
115 | pub fn is_empty(&self) -> bool { | |
116 | self.fields.is_empty() | |
117 | } | |
118 | } | |
119 | ||
120 | impl fmt::Display for MockValue { | |
121 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
122 | match self { | |
123 | MockValue::I64(v) => write!(f, "i64 = {:?}", v), | |
124 | MockValue::U64(v) => write!(f, "u64 = {:?}", v), | |
125 | MockValue::Bool(v) => write!(f, "bool = {:?}", v), | |
126 | MockValue::Str(v) => write!(f, "&str = {:?}", v), | |
127 | MockValue::Debug(v) => write!(f, "&fmt::Debug = {:?}", v), | |
128 | MockValue::Any => write!(f, "_ = _"), | |
129 | } | |
130 | } | |
131 | } | |
132 | ||
133 | pub struct CheckVisitor<'a> { | |
134 | expect: &'a mut Expect, | |
135 | ctx: String, | |
136 | } | |
137 | ||
138 | impl<'a> Visit for CheckVisitor<'a> { | |
139 | fn record_i64(&mut self, field: &Field, value: i64) { | |
140 | self.expect | |
141 | .compare_or_panic(field.name(), &value, &self.ctx[..]) | |
142 | } | |
143 | ||
144 | fn record_u64(&mut self, field: &Field, value: u64) { | |
145 | self.expect | |
146 | .compare_or_panic(field.name(), &value, &self.ctx[..]) | |
147 | } | |
148 | ||
149 | fn record_bool(&mut self, field: &Field, value: bool) { | |
150 | self.expect | |
151 | .compare_or_panic(field.name(), &value, &self.ctx[..]) | |
152 | } | |
153 | ||
154 | fn record_str(&mut self, field: &Field, value: &str) { | |
155 | self.expect | |
156 | .compare_or_panic(field.name(), &value, &self.ctx[..]) | |
157 | } | |
158 | ||
159 | fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { | |
160 | self.expect | |
161 | .compare_or_panic(field.name(), &field::debug(value), &self.ctx) | |
162 | } | |
163 | } | |
164 | ||
165 | impl<'a> CheckVisitor<'a> { | |
166 | pub fn finish(self) { | |
167 | assert!( | |
168 | self.expect.fields.is_empty(), | |
169 | "{}missing {}", | |
170 | self.expect, | |
171 | self.ctx | |
172 | ); | |
173 | } | |
174 | } | |
175 | ||
176 | impl<'a> From<&'a dyn Value> for MockValue { | |
177 | fn from(value: &'a dyn Value) -> Self { | |
178 | struct MockValueBuilder { | |
179 | value: Option<MockValue>, | |
180 | } | |
181 | ||
182 | impl Visit for MockValueBuilder { | |
183 | fn record_i64(&mut self, _: &Field, value: i64) { | |
184 | self.value = Some(MockValue::I64(value)); | |
185 | } | |
186 | ||
187 | fn record_u64(&mut self, _: &Field, value: u64) { | |
188 | self.value = Some(MockValue::U64(value)); | |
189 | } | |
190 | ||
191 | fn record_bool(&mut self, _: &Field, value: bool) { | |
192 | self.value = Some(MockValue::Bool(value)); | |
193 | } | |
194 | ||
195 | fn record_str(&mut self, _: &Field, value: &str) { | |
196 | self.value = Some(MockValue::Str(value.to_owned())); | |
197 | } | |
198 | ||
199 | fn record_debug(&mut self, _: &Field, value: &dyn fmt::Debug) { | |
200 | self.value = Some(MockValue::Debug(format!("{:?}", value))); | |
201 | } | |
202 | } | |
203 | ||
204 | let fake_field = callsite!(name: "fake", kind: Kind::EVENT, fields: fake_field) | |
205 | .metadata() | |
206 | .fields() | |
207 | .field("fake_field") | |
208 | .unwrap(); | |
209 | let mut builder = MockValueBuilder { value: None }; | |
210 | value.record(&fake_field, &mut builder); | |
211 | builder | |
212 | .value | |
213 | .expect("finish called before a value was recorded") | |
214 | } | |
215 | } | |
216 | ||
217 | impl fmt::Display for Expect { | |
218 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
219 | write!(f, "fields ")?; | |
220 | let entries = self | |
221 | .fields | |
222 | .iter() | |
223 | .map(|(k, v)| (field::display(k), field::display(v))); | |
224 | f.debug_map().entries(entries).finish() | |
225 | } | |
226 | } |