vfs/directory/
test_utils.rs

1// Copyright 2019 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
5//! Common utilities used by directory related tests.
6//!
7//! Most assertions are macros as they need to call async functions themselves.  As a typical test
8//! will have multiple assertions, it save a bit of typing to write `assert_something!(arg)`
9//! instead of `assert_something(arg).await`.
10
11#[doc(hidden)]
12pub mod reexport {
13    pub use fuchsia_async::Channel;
14    pub use zx_status::Status;
15    pub use {fidl, fidl_fuchsia_io as fio};
16
17    #[cfg(not(target_os = "fuchsia"))]
18    pub use fuchsia_async::emulated_handle::MessageBuf;
19    #[cfg(target_os = "fuchsia")]
20    pub use zx::MessageBuf;
21}
22
23use crate::directory::entry::DirectoryEntry;
24use crate::test_utils::run::{self, AsyncServerClientTestParams};
25
26use byteorder::{LittleEndian, WriteBytesExt};
27use fidl_fuchsia_io as fio;
28use futures::Future;
29use std::convert::TryInto as _;
30use std::io::Write;
31use std::sync::Arc;
32
33pub use run::{run_client, test_client};
34
35/// A thin wrapper around [`run::run_server_client()`] that sets the `Marker` to be
36/// [`DirectoryMarker`], and providing explicit type for the `get_client` closure argument.  This
37/// makes it possible for the caller not to provide explicit types.
38pub fn run_server_client<GetClientRes>(
39    flags: fio::OpenFlags,
40    server: Arc<dyn DirectoryEntry>,
41    get_client: impl FnOnce(fio::DirectoryProxy) -> GetClientRes,
42) where
43    GetClientRes: Future<Output = ()>,
44{
45    run::run_server_client::<fio::DirectoryMarker, _, _>(flags, server, get_client)
46}
47
48/// A thin wrapper around [`run::test_server_client()`] that sets the `Marker` to be
49/// [`DirectoryMarker`], and providing explicit type for the `get_client` closure argument.  This
50/// makes it possible for the caller not to provide explicit types.
51pub fn test_server_client<'test_refs, GetClientRes>(
52    flags: fio::OpenFlags,
53    server: Arc<dyn DirectoryEntry>,
54    get_client: impl FnOnce(fio::DirectoryProxy) -> GetClientRes + 'test_refs,
55) -> AsyncServerClientTestParams<'test_refs, fio::DirectoryMarker>
56where
57    GetClientRes: Future<Output = ()> + 'test_refs,
58{
59    run::test_server_client::<fio::DirectoryMarker, _, _>(flags, server, get_client)
60}
61
62/// A helper to build the "expected" output for a `ReadDirents` call from the Directory protocol in
63/// fuchsia.io.
64pub struct DirentsSameInodeBuilder {
65    expected: Vec<u8>,
66    inode: u64,
67}
68
69impl DirentsSameInodeBuilder {
70    pub fn new(inode: u64) -> Self {
71        DirentsSameInodeBuilder { expected: vec![], inode }
72    }
73
74    pub fn add(&mut self, type_: fio::DirentType, name: &[u8]) -> &mut Self {
75        assert!(
76            name.len() <= fio::MAX_NAME_LENGTH as usize,
77            "Expected entry name should not exceed MAX_FILENAME ({}) bytes.\n\
78             Got: {:?}\n\
79             Length: {} bytes",
80            fio::MAX_NAME_LENGTH,
81            name,
82            name.len()
83        );
84
85        self.expected.write_u64::<LittleEndian>(self.inode).unwrap();
86        self.expected.write_u8(name.len().try_into().unwrap()).unwrap();
87        self.expected.write_u8(type_.into_primitive()).unwrap();
88        self.expected.write_all(name).unwrap();
89
90        self
91    }
92
93    pub fn into_vec(self) -> Vec<u8> {
94        self.expected
95    }
96}
97
98/// Calls `rewind` on the provided `proxy`, checking that the result status is Status::OK.
99#[macro_export]
100macro_rules! assert_rewind {
101    ($proxy:expr) => {{
102        use $crate::directory::test_utils::reexport::Status;
103
104        let status = $proxy.rewind().await.expect("rewind failed");
105        assert_eq!(Status::from_raw(status), Status::OK);
106    }};
107}
108
109/// Opens the specified path as a VMO file and checks its content.  Also see all the `assert_*`
110/// macros in `../test_utils.rs`.
111#[macro_export]
112macro_rules! open_as_vmo_file_assert_content {
113    ($proxy:expr, $flags:expr, $path:expr, $expected_content:expr) => {{
114        let file = open_get_vmo_file_proxy_assert_ok!($proxy, $flags, $path);
115        assert_read!(file, $expected_content);
116        assert_close!(file);
117    }};
118}
119
120#[macro_export]
121macro_rules! assert_watch {
122    ($proxy:expr, $mask:expr) => {{
123        use $crate::directory::test_utils::reexport::{fidl, Channel, Status};
124
125        let (client, server) = fidl::endpoints::create_endpoints();
126
127        let status = $proxy.watch($mask, 0, server).await.expect("watch failed");
128        assert_eq!(Status::from_raw(status), Status::OK);
129
130        Channel::from_channel(client.into_channel())
131    }};
132}
133
134#[macro_export]
135macro_rules! assert_watch_err {
136    ($proxy:expr, $mask:expr, $expected_status:expr) => {{
137        use $crate::directory::test_utils::reexport::{fidl, Status};
138
139        let (_client, server) = fidl::endpoints::create_endpoints();
140
141        let status = $proxy.watch($mask, 0, server).await.expect("watch failed");
142        assert_eq!(Status::from_raw(status), $expected_status);
143    }};
144}
145
146#[macro_export]
147macro_rules! assert_watcher_one_message_watched_events {
148    ($watcher:expr, $( { $type:tt, $name:expr $(,)* } ),* $(,)*) => {{
149        #[allow(unused)]
150        use $crate::directory::test_utils::reexport::{MessageBuf, fio::WatchEvent};
151        use std::convert::TryInto as _;
152
153        let mut buf = MessageBuf::new();
154        $watcher.recv_msg(&mut buf).await.unwrap();
155
156        let (bytes, handles) = buf.split();
157        assert_eq!(
158            handles.len(),
159            0,
160            "Received buffer with handles.\n\
161             Handle count: {}\n\
162             Buffer: {:X?}",
163            handles.len(),
164            bytes
165        );
166
167        let expected = &mut vec![];
168        $({
169            let type_ = assert_watcher_one_message_watched_events!(@expand_event_type $type);
170            let name = Vec::<u8>::from($name);
171            assert!(name.len() <= std::u8::MAX as usize);
172
173            expected.push(type_.into_primitive());
174            expected.push(name.len().try_into().unwrap());
175            expected.extend_from_slice(&name);
176        })*
177
178        assert!(bytes == *expected,
179                "Received buffer does not match the expectation.\n\
180                 Expected: {:X?}\n\
181                 Received: {:X?}\n\
182                 Expected as UTF-8 lossy: {:?}\n\
183                 Received as UTF-8 lossy: {:?}",
184                *expected, bytes,
185                String::from_utf8_lossy(expected), String::from_utf8_lossy(&bytes));
186    }};
187
188    (@expand_event_type EXISTING) => { fio::WatchEvent::Existing };
189    (@expand_event_type IDLE) => { fio::WatchEvent::Idle };
190    (@expand_event_type ADDED) => { fio::WatchEvent::Added };
191    (@expand_event_type REMOVED) => { fio::WatchEvent::Removed };
192}