1#![warn(missing_docs)]
6
7use async_trait::async_trait;
11use fidl::endpoints::{ClientEnd, ProtocolMarker, Proxy, create_proxy};
12use fidl_fuchsia_io as fio;
13use fidl_fuchsia_io_test as io_test;
14use futures::TryStreamExt as _;
15
16pub mod test_harness;
18
19pub mod flags;
21
22pub const TEST_FILE: &str = "testing.txt";
24
25pub const TEST_FILE_CONTENTS: &[u8] = "abcdef".as_bytes();
27
28pub const EMPTY_NODE_ATTRS: fio::NodeAttributes = fio::NodeAttributes {
30 mode: 0,
31 id: 0,
32 content_size: 0,
33 storage_size: 0,
34 link_count: 0,
35 creation_time: 0,
36 modification_time: 0,
37};
38
39pub fn convert_node_proxy<T: Proxy>(proxy: fio::NodeProxy) -> T {
42 T::from_channel(proxy.into_channel().expect("Cannot convert node proxy to channel"))
43}
44
45pub async fn get_token(dir: &fio::DirectoryProxy) -> fidl::NullableHandle {
48 let (status, token) = dir.get_token().await.expect("get_token failed");
49 assert_eq!(zx::Status::from_raw(status), zx::Status::OK);
50 token.expect("handle missing")
51}
52
53pub async fn read_file(dir: &fio::DirectoryProxy, path: &str) -> Vec<u8> {
56 let file =
57 dir.open_node::<fio::FileMarker>(path, fio::Flags::PERM_READ_BYTES, None).await.unwrap();
58 file.read(100).await.expect("read failed").map_err(zx::Status::from_raw).expect("read error")
59}
60
61pub fn get_directory_entry_name(dir_entry: &io_test::DirectoryEntry) -> String {
63 use io_test::DirectoryEntry;
64 match dir_entry {
65 DirectoryEntry::Directory(entry) => &entry.name,
66 DirectoryEntry::RemoteDirectory(entry) => &entry.name,
67 DirectoryEntry::File(entry) => &entry.name,
68 DirectoryEntry::ExecutableFile(entry) => &entry.name,
69 }
70 .clone()
71}
72
73pub fn validate_vmo_rights(vmo: &zx::Vmo, expected_vmo_rights: fio::VmoFlags) {
78 let vmo_rights: zx::Rights = vmo.basic_info().expect("failed to get VMO info").rights;
79
80 assert!(vmo_rights.contains(zx::Rights::BASIC));
82 assert!(vmo_rights.contains(zx::Rights::MAP));
83 assert!(vmo_rights.contains(zx::Rights::GET_PROPERTY));
84
85 assert!(
87 vmo_rights.contains(zx::Rights::READ) == expected_vmo_rights.contains(fio::VmoFlags::READ)
88 );
89 assert!(
90 vmo_rights.contains(zx::Rights::WRITE)
91 == expected_vmo_rights.contains(fio::VmoFlags::WRITE)
92 );
93 assert!(
94 vmo_rights.contains(zx::Rights::EXECUTE)
95 == expected_vmo_rights.contains(fio::VmoFlags::EXECUTE)
96 );
97
98 if expected_vmo_rights.contains(fio::VmoFlags::PRIVATE_CLONE) {
100 assert!(vmo_rights.contains(zx::Rights::SET_PROPERTY));
101 }
102}
103
104pub async fn create_file_and_get_backing_memory(
107 dir_entry: io_test::DirectoryEntry,
108 test_harness: &test_harness::TestHarness,
109 file_flags: fio::Flags,
110 vmo_flags: fio::VmoFlags,
111) -> Result<(zx::Vmo, (fio::DirectoryProxy, fio::FileProxy)), zx::Status> {
112 let file_path = get_directory_entry_name(&dir_entry);
113 let dir_proxy =
114 test_harness.get_directory(vec![dir_entry], test_harness.dir_rights.all_flags());
115 let file_proxy = dir_proxy.open_node::<fio::FileMarker>(&file_path, file_flags, None).await?;
116 let vmo = file_proxy
117 .get_backing_memory(vmo_flags)
118 .await
119 .expect("get_backing_memory failed")
120 .map_err(zx::Status::from_raw)?;
121 Ok((vmo, (dir_proxy, file_proxy)))
122}
123
124pub fn directory(name: &str, entries: Vec<io_test::DirectoryEntry>) -> io_test::DirectoryEntry {
126 let entries: Vec<Option<Box<io_test::DirectoryEntry>>> =
127 entries.into_iter().map(|e| Some(Box::new(e))).collect();
128 io_test::DirectoryEntry::Directory(io_test::Directory { name: name.to_string(), entries })
129}
130
131pub fn remote_directory(name: &str, remote_dir: fio::DirectoryProxy) -> io_test::DirectoryEntry {
133 let remote_client = ClientEnd::<fio::DirectoryMarker>::new(
134 remote_dir.into_channel().unwrap().into_zx_channel(),
135 );
136
137 io_test::DirectoryEntry::RemoteDirectory(io_test::RemoteDirectory {
138 name: name.to_string(),
139 remote_client,
140 })
141}
142
143pub fn file(name: &str, contents: Vec<u8>) -> io_test::DirectoryEntry {
145 io_test::DirectoryEntry::File(io_test::File { name: name.to_string(), contents })
146}
147
148pub fn executable_file(name: &str) -> io_test::DirectoryEntry {
150 io_test::DirectoryEntry::ExecutableFile(io_test::ExecutableFile { name: name.to_string() })
151}
152
153#[async_trait]
156pub trait DirectoryProxyExt {
157 async fn open_node<T: ProtocolMarker>(
162 &self,
163 path: &str,
164 flags: fio::Flags,
165 options: Option<fio::Options>,
166 ) -> Result<T::Proxy, zx::Status>;
167
168 async fn open_node_repr<T: ProtocolMarker>(
173 &self,
174 path: &str,
175 flags: fio::Flags,
176 options: Option<fio::Options>,
177 ) -> Result<(T::Proxy, fio::Representation), zx::Status>;
178}
179
180#[async_trait]
181impl DirectoryProxyExt for fio::DirectoryProxy {
182 async fn open_node<T: ProtocolMarker>(
183 &self,
184 path: &str,
185 flags: fio::Flags,
186 options: Option<fio::Options>,
187 ) -> Result<T::Proxy, zx::Status> {
188 open_node_impl::<T>(self, path, flags, options).await.map(|(proxy, _representation)| proxy)
189 }
190
191 async fn open_node_repr<T: ProtocolMarker>(
192 &self,
193 path: &str,
194 flags: fio::Flags,
195 options: Option<fio::Options>,
196 ) -> Result<(T::Proxy, fio::Representation), zx::Status> {
197 assert!(
198 flags.contains(fio::Flags::FLAG_SEND_REPRESENTATION),
199 "flags must specify the FLAG_SEND_REPRESENTATION flag to use this function!"
200 );
201 let (proxy, representation) = open_node_impl::<T>(self, path, flags, options).await?;
202 Ok((proxy, representation.unwrap()))
203 }
204}
205
206async fn open_node_impl<T: ProtocolMarker>(
207 dir: &fio::DirectoryProxy,
208 path: &str,
209 flags: fio::Flags,
210 options: Option<fio::Options>,
211) -> Result<(T::Proxy, Option<fio::Representation>), zx::Status> {
212 let (proxy, server) = create_proxy::<fio::NodeMarker>();
213 dir.open(path, flags, &options.unwrap_or_default(), server.into_channel())
214 .expect("Failed to call open3");
215 let representation = if flags.contains(fio::Flags::FLAG_SEND_REPRESENTATION) {
216 Some(get_on_representation_event(&proxy).await?)
217 } else {
218 let _ = proxy.get_attributes(Default::default()).await.map_err(|e| {
220 if let fidl::Error::ClientChannelClosed { epitaph, .. } = e {
221 match epitaph.into() {
222 Err(s) => s,
223 Ok(()) => zx::Status::PEER_CLOSED,
224 }
225 } else {
226 panic!("Unhandled FIDL error: {:?}", e);
227 }
228 })?;
229 None
230 };
231 Ok((convert_node_proxy(proxy), representation))
232}
233
234async fn get_on_representation_event(
236 node_proxy: &fio::NodeProxy,
237) -> Result<fio::Representation, zx::Status> {
238 let event = Clone::clone(node_proxy)
240 .take_event_stream()
241 .try_next()
242 .await
243 .map_err(|e| {
244 if let fidl::Error::ClientChannelClosed { epitaph, .. } = e {
245 match epitaph.into() {
246 Err(s) => s,
247 Ok(()) => zx::Status::PEER_CLOSED,
248 }
249 } else {
250 panic!("Unhandled FIDL error: {:?}", e);
251 }
252 })?
253 .expect("Missing NodeEvent in stream!");
254 let representation = match event {
255 fio::NodeEvent::OnRepresentation { payload } => payload,
256 _ => panic!("Found unexpected NodeEvent type in stream!"),
257 };
258 Ok(representation)
259}