fidl_fuchsia_net_neighbor_ext/
testutil.rs1use futures::future::FusedFuture;
12use futures::{FutureExt, Stream, StreamExt as _};
13use {fidl_fuchsia_net as fnet, fidl_fuchsia_net_neighbor as fnet_neighbor};
14
15fn handle_get_next(
17 request: fnet_neighbor::EntryIteratorRequest,
18 event_batch: Vec<fnet_neighbor::EntryIteratorItem>,
19) {
20 match request {
21 fnet_neighbor::EntryIteratorRequest::GetNext { responder } => {
22 responder.send(&event_batch).expect("failed to respond to `GetNext`")
23 }
24 }
25}
26
27async fn fake_entry_iterator_impl(
31 events: impl Stream<Item = Vec<fnet_neighbor::EntryIteratorItem>>,
32 server_end: fidl::endpoints::ServerEnd<fnet_neighbor::EntryIteratorMarker>,
33) {
34 let request_stream = server_end.into_stream();
35 request_stream
36 .zip(events)
37 .for_each(|(request, event_batch)| {
38 handle_get_next(request.expect("failed to receive `GetNext` request"), event_batch);
39 futures::future::ready(())
40 })
41 .await
42}
43
44pub async fn serve_view_request(
48 request: fnet_neighbor::ViewRequest,
49 event_stream: impl Stream<Item = Vec<fnet_neighbor::EntryIteratorItem>>,
50) {
51 match request {
52 fnet_neighbor::ViewRequest::OpenEntryIterator { it, .. } => {
53 fake_entry_iterator_impl(event_stream, it)
54 }
55 }
56 .await
57}
58
59pub fn create_fake_view(
64 event_stream: impl Stream<Item = Vec<fnet_neighbor::EntryIteratorItem>>,
65) -> (fnet_neighbor::ViewProxy, impl FusedFuture<Output = ()>) {
66 let (view, view_server_end) = fidl::endpoints::create_proxy::<fnet_neighbor::ViewMarker>();
67
68 let entry_iter_fut = view_server_end
69 .into_stream()
70 .into_future()
71 .then(async |(req, _rest)| {
72 serve_view_request(
73 req.expect("View request stream unexpectedly ended")
74 .expect("failed to receive `OpenEntryIterator` request"),
75 event_stream,
76 )
77 .await
78 })
79 .fuse();
80
81 (view, entry_iter_fut)
82}
83
84#[derive(Clone, Debug, PartialEq)]
86pub enum EventSpec {
87 Existing(u8),
89 Added(u8),
91 Changed(u8),
93 Removed(u8),
95 Idle,
97}
98
99fn generate_entry(seed: u8) -> fnet_neighbor::Entry {
101 fnet_neighbor::Entry {
102 interface: Some(1),
103 neighbor: Some(fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr: [192, 168, 0, seed] })),
104 state: Some(fnet_neighbor::EntryState::Reachable),
105 mac: Some(fnet::MacAddress { octets: [0, 1, 2, 3, 4, 5] }),
106 updated_at: Some(12345),
107 ..Default::default()
108 }
109}
110
111pub fn generate_event_from_spec(spec: &EventSpec) -> fnet_neighbor::EntryIteratorItem {
113 match *spec {
114 EventSpec::Existing(seed) => {
115 fnet_neighbor::EntryIteratorItem::Existing(generate_entry(seed))
116 }
117 EventSpec::Added(seed) => fnet_neighbor::EntryIteratorItem::Added(generate_entry(seed)),
118 EventSpec::Changed(seed) => fnet_neighbor::EntryIteratorItem::Changed(generate_entry(seed)),
119 EventSpec::Removed(seed) => fnet_neighbor::EntryIteratorItem::Removed(generate_entry(seed)),
120 EventSpec::Idle => fnet_neighbor::EntryIteratorItem::Idle(fnet_neighbor::IdleEvent),
121 }
122}
123
124pub fn generate_events_from_spec(spec: &[EventSpec]) -> Vec<fnet_neighbor::EntryIteratorItem> {
126 spec.into_iter().map(generate_event_from_spec).collect()
127}
128
129pub fn generate_event(seed: u8) -> fnet_neighbor::EntryIteratorItem {
132 generate_event_from_spec(&EventSpec::Added(seed))
133}
134
135pub fn generate_events_in_range(
138 seeds: std::ops::Range<u8>,
139) -> Vec<fnet_neighbor::EntryIteratorItem> {
140 seeds.into_iter().map(generate_event).collect()
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 use assert_matches::assert_matches;
148 use futures::FutureExt;
149 use test_case::test_case;
150
151 #[test_case(Vec::new(); "no events")]
152 #[test_case(vec![0..1]; "single_batch_single_event")]
153 #[test_case(vec![0..10]; "single_batch_many_events")]
154 #[test_case(vec![0..10, 10..20, 20..30]; "many_batches_many_events")]
155 #[fuchsia_async::run_singlethreaded(test)]
156 async fn fake_view_impl_against_shape(test_shape: Vec<std::ops::Range<u8>>) {
157 let (event_stream_sender, event_stream_receiver) =
160 futures::channel::mpsc::unbounded::<Vec<fnet_neighbor::EntryIteratorItem>>();
161 for batch_shape in &test_shape {
162 event_stream_sender
163 .unbounded_send(generate_events_in_range(batch_shape.clone()))
164 .expect("failed to send event batch");
165 }
166
167 let (view, entry_iter_fut) = create_fake_view(event_stream_receiver);
169 futures::pin_mut!(entry_iter_fut);
170
171 let (entry_iter, entry_iter_server_end) =
173 fidl::endpoints::create_proxy::<fnet_neighbor::EntryIteratorMarker>();
174 view.open_entry_iterator(entry_iter_server_end, &Default::default())
175 .expect("failed to open entry iterator");
176 for batch_shape in test_shape {
177 futures::select!(
178 () = entry_iter_fut => panic!("fake view implementation unexpectedly finished"),
179 events = entry_iter.get_next().fuse() => assert_eq!(
180 events.expect("failed to watch for events"),
181 generate_events_in_range(batch_shape.clone())));
182 }
183
184 event_stream_sender.close_channel();
186 entry_iter_fut.await;
187
188 assert_matches!(
191 entry_iter.get_next().await,
192 Err(fidl::Error::ClientChannelClosed { status: zx_status::Status::PEER_CLOSED, .. })
193 );
194 }
195
196 #[test]
197 fn generate_entry_unique_to_seed() {
198 assert_eq!(generate_entry(1), generate_entry(1));
199 assert_ne!(generate_entry(1), generate_entry(2));
200 }
201
202 #[test]
203 fn generate_multiple_events_from_spec() {
204 use EventSpec::*;
205 assert_eq!(
206 generate_events_from_spec(&[Existing(1), Added(2), Changed(3), Removed(4), Idle]),
207 &[
208 fnet_neighbor::EntryIteratorItem::Existing(generate_entry(1)),
209 fnet_neighbor::EntryIteratorItem::Added(generate_entry(2)),
210 fnet_neighbor::EntryIteratorItem::Changed(generate_entry(3)),
211 fnet_neighbor::EntryIteratorItem::Removed(generate_entry(4)),
212 fnet_neighbor::EntryIteratorItem::Idle(fnet_neighbor::IdleEvent),
213 ]
214 );
215 }
216}