googletest/matchers/
has_entry_matcher.rs1use crate::description::Description;
16use crate::matcher::{Matcher, MatcherBase, MatcherResult};
17use std::collections::HashMap;
18use std::fmt::Debug;
19use std::hash::Hash;
20
21pub fn has_entry<KeyT, MatcherT>(key: KeyT, inner: MatcherT) -> HasEntryMatcher<KeyT, MatcherT> {
61 HasEntryMatcher { key, inner }
62}
63
64#[derive(MatcherBase)]
65pub struct HasEntryMatcher<KeyT, MatcherT> {
66 key: KeyT,
67 inner: MatcherT,
68}
69
70impl<'a, KeyT: Debug + Eq + Hash, ValueT: Debug, MatcherT: Matcher<&'a ValueT>>
71 Matcher<&'a HashMap<KeyT, ValueT>> for HasEntryMatcher<KeyT, MatcherT>
72{
73 fn matches(&self, actual: &'a HashMap<KeyT, ValueT>) -> MatcherResult {
74 if let Some(value) = actual.get(&self.key) {
75 self.inner.matches(value)
76 } else {
77 MatcherResult::NoMatch
78 }
79 }
80
81 fn explain_match(&self, actual: &'a HashMap<KeyT, ValueT>) -> Description {
82 if let Some(value) = actual.get(&self.key) {
83 format!(
84 "which contains key {:?}, but is mapped to value {:#?}, {}",
85 self.key,
86 value,
87 self.inner.explain_match(value)
88 )
89 .into()
90 } else {
91 format!("which doesn't contain key {:?}", self.key).into()
92 }
93 }
94
95 fn describe(&self, matcher_result: MatcherResult) -> Description {
96 match matcher_result {
97 MatcherResult::Match => format!(
98 "contains key {:?}, which value {}",
99 self.key,
100 self.inner.describe(MatcherResult::Match)
101 )
102 .into(),
103 MatcherResult::NoMatch => format!(
104 "doesn't contain key {:?} or contains key {:?}, which value {}",
105 self.key,
106 self.key,
107 self.inner.describe(MatcherResult::NoMatch)
108 )
109 .into(),
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use crate::prelude::*;
117 use crate::Result;
118 use indoc::indoc;
119 use std::collections::HashMap;
120
121 #[test]
122 fn has_entry_does_not_match_empty_hash_map() -> Result<()> {
123 let value: HashMap<i32, i32> = HashMap::new();
124 verify_that!(&value, not(has_entry(0, eq(&0))))
125 }
126
127 #[test]
128 fn has_entry_matches_hash_map_with_value() -> Result<()> {
129 let value: HashMap<i32, i32> = HashMap::from([(0, 0)]);
130 verify_that!(&value, has_entry(0, eq(&0)))
131 }
132
133 #[test]
134 fn has_entry_does_not_match_hash_map_with_wrong_value() -> Result<()> {
135 let value: HashMap<i32, i32> = HashMap::from([(0, 1)]);
136 verify_that!(&value, not(has_entry(0, eq(&0))))
137 }
138
139 #[test]
140 fn has_entry_does_not_match_hash_map_with_wrong_key() -> Result<()> {
141 let value: HashMap<i32, i32> = HashMap::from([(1, 0)]);
142 verify_that!(&value, not(has_entry(0, eq(&0))))
143 }
144
145 #[test]
146 fn has_entry_shows_correct_message_when_key_is_not_present() -> Result<()> {
147 let result = verify_that!(HashMap::from([(0, 0)]), has_entry(1, eq(&0)));
148
149 verify_that!(
150 result,
151 err(displays_as(contains_substring(indoc!(
152 "
153 Value of: HashMap::from([(0, 0)])
154 Expected: contains key 1, which value is equal to 0
155 Actual: {0: 0},
156 which doesn't contain key 1
157 "
158 ))))
159 )
160 }
161
162 #[test]
163 fn has_entry_shows_correct_message_when_key_has_non_matching_value() -> Result<()> {
164 let result = verify_that!(HashMap::from([(0, 0)]), has_entry(0, eq(&1)));
165
166 verify_that!(
167 result,
168 err(displays_as(contains_substring(indoc!(
169 "
170 Value of: HashMap::from([(0, 0)])
171 Expected: contains key 0, which value is equal to 1
172 Actual: {0: 0},
173 which contains key 0, but is mapped to value 0, which isn't equal to 1
174 "
175 ))))
176 )
177 }
178}