1#![warn(missing_docs)]
6
7use async_trait::async_trait;
11use fidl::endpoints::{create_proxy, ClientEnd, ProtocolMarker, Proxy};
12use fidl::prelude::*;
13use futures::{StreamExt as _, TryStreamExt as _};
14use {fidl_fuchsia_io as fio, fidl_fuchsia_io_test as io_test};
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 async fn get_open_status(node_proxy: &fio::NodeProxy) -> zx::Status {
41 let mut events = Clone::clone(node_proxy).take_event_stream();
42 if let Some(result) = events.next().await {
43 match result.expect("FIDL error") {
44 fio::NodeEvent::OnOpen_ { s, info: _ } => zx::Status::from_raw(s),
45 fio::NodeEvent::OnRepresentation { .. } => panic!(
46 "This function should only be used with fuchsia.io/Directory.Open, *not* Open3!"
47 ),
48 fio::NodeEvent::_UnknownEvent { .. } => {
49 panic!("This function should only be used with fuchsia.io/Directory.Open")
50 }
51 }
52 } else {
53 zx::Status::PEER_CLOSED
54 }
55}
56
57pub fn convert_node_proxy<T: Proxy>(proxy: fio::NodeProxy) -> T {
60 T::from_channel(proxy.into_channel().expect("Cannot convert node proxy to channel"))
61}
62
63pub async fn get_token(dir: &fio::DirectoryProxy) -> fidl::Handle {
66 let (status, token) = dir.get_token().await.expect("get_token failed");
67 assert_eq!(zx::Status::from_raw(status), zx::Status::OK);
68 token.expect("handle missing")
69}
70
71pub async fn read_file(dir: &fio::DirectoryProxy, path: &str) -> Vec<u8> {
74 let file =
75 dir.open_node::<fio::FileMarker>(path, fio::Flags::PERM_READ_BYTES, None).await.unwrap();
76 file.read(100).await.expect("read failed").map_err(zx::Status::from_raw).expect("read error")
77}
78
79pub fn get_directory_entry_name(dir_entry: &io_test::DirectoryEntry) -> String {
81 use io_test::DirectoryEntry;
82 match dir_entry {
83 DirectoryEntry::Directory(entry) => &entry.name,
84 DirectoryEntry::RemoteDirectory(entry) => &entry.name,
85 DirectoryEntry::File(entry) => &entry.name,
86 DirectoryEntry::ExecutableFile(entry) => &entry.name,
87 }
88 .clone()
89}
90
91pub fn validate_vmo_rights(vmo: &zx::Vmo, expected_vmo_rights: fio::VmoFlags) {
96 let vmo_rights: zx::Rights = vmo.basic_info().expect("failed to get VMO info").rights;
97
98 assert!(vmo_rights.contains(zx::Rights::BASIC));
100 assert!(vmo_rights.contains(zx::Rights::MAP));
101 assert!(vmo_rights.contains(zx::Rights::GET_PROPERTY));
102
103 assert!(
105 vmo_rights.contains(zx::Rights::READ) == expected_vmo_rights.contains(fio::VmoFlags::READ)
106 );
107 assert!(
108 vmo_rights.contains(zx::Rights::WRITE)
109 == expected_vmo_rights.contains(fio::VmoFlags::WRITE)
110 );
111 assert!(
112 vmo_rights.contains(zx::Rights::EXECUTE)
113 == expected_vmo_rights.contains(fio::VmoFlags::EXECUTE)
114 );
115
116 if expected_vmo_rights.contains(fio::VmoFlags::PRIVATE_CLONE) {
118 assert!(vmo_rights.contains(zx::Rights::SET_PROPERTY));
119 }
120}
121
122pub async fn create_file_and_get_backing_memory(
125 dir_entry: io_test::DirectoryEntry,
126 test_harness: &test_harness::TestHarness,
127 file_flags: fio::Flags,
128 vmo_flags: fio::VmoFlags,
129) -> Result<(zx::Vmo, (fio::DirectoryProxy, fio::FileProxy)), zx::Status> {
130 let file_path = get_directory_entry_name(&dir_entry);
131 let dir_proxy = test_harness.get_directory(vec![dir_entry], file_flags);
132 let file_proxy = dir_proxy.open_node::<fio::FileMarker>(&file_path, file_flags, None).await?;
133 let vmo = file_proxy
134 .get_backing_memory(vmo_flags)
135 .await
136 .expect("get_backing_memory failed")
137 .map_err(zx::Status::from_raw)?;
138 Ok((vmo, (dir_proxy, file_proxy)))
139}
140
141pub fn directory(name: &str, entries: Vec<io_test::DirectoryEntry>) -> io_test::DirectoryEntry {
143 let entries: Vec<Option<Box<io_test::DirectoryEntry>>> =
144 entries.into_iter().map(|e| Some(Box::new(e))).collect();
145 io_test::DirectoryEntry::Directory(io_test::Directory { name: name.to_string(), entries })
146}
147
148pub fn remote_directory(name: &str, remote_dir: fio::DirectoryProxy) -> io_test::DirectoryEntry {
150 let remote_client = ClientEnd::<fio::DirectoryMarker>::new(
151 remote_dir.into_channel().unwrap().into_zx_channel(),
152 );
153
154 io_test::DirectoryEntry::RemoteDirectory(io_test::RemoteDirectory {
155 name: name.to_string(),
156 remote_client,
157 })
158}
159
160pub fn file(name: &str, contents: Vec<u8>) -> io_test::DirectoryEntry {
162 io_test::DirectoryEntry::File(io_test::File { name: name.to_string(), contents })
163}
164
165pub fn executable_file(name: &str) -> io_test::DirectoryEntry {
167 io_test::DirectoryEntry::ExecutableFile(io_test::ExecutableFile { name: name.to_string() })
168}
169
170#[async_trait]
173pub trait DirectoryProxyExt {
174 async fn open_node<T: ProtocolMarker>(
179 &self,
180 path: &str,
181 flags: fio::Flags,
182 options: Option<fio::Options>,
183 ) -> Result<T::Proxy, zx::Status>;
184
185 async fn open_node_repr<T: ProtocolMarker>(
190 &self,
191 path: &str,
192 flags: fio::Flags,
193 options: Option<fio::Options>,
194 ) -> Result<(T::Proxy, fio::Representation), zx::Status>;
195}
196
197#[async_trait]
198impl DirectoryProxyExt for fio::DirectoryProxy {
199 async fn open_node<T: ProtocolMarker>(
200 &self,
201 path: &str,
202 flags: fio::Flags,
203 options: Option<fio::Options>,
204 ) -> Result<T::Proxy, zx::Status> {
205 open_node_impl::<T>(self, path, flags, options).await.map(|(proxy, _representation)| proxy)
206 }
207
208 async fn open_node_repr<T: ProtocolMarker>(
209 &self,
210 path: &str,
211 flags: fio::Flags,
212 options: Option<fio::Options>,
213 ) -> Result<(T::Proxy, fio::Representation), zx::Status> {
214 assert!(
215 flags.contains(fio::Flags::FLAG_SEND_REPRESENTATION),
216 "flags must specify the FLAG_SEND_REPRESENTATION flag to use this function!"
217 );
218 let (proxy, representation) = open_node_impl::<T>(self, path, flags, options).await?;
219 Ok((proxy, representation.unwrap()))
220 }
221}
222
223async fn open_node_impl<T: ProtocolMarker>(
224 dir: &fio::DirectoryProxy,
225 path: &str,
226 flags: fio::Flags,
227 options: Option<fio::Options>,
228) -> Result<(T::Proxy, Option<fio::Representation>), zx::Status> {
229 let (proxy, server) = create_proxy::<fio::NodeMarker>();
230 dir.open(path, flags, &options.unwrap_or_default(), server.into_channel())
231 .expect("Failed to call open3");
232 let representation = if flags.contains(fio::Flags::FLAG_SEND_REPRESENTATION) {
233 Some(get_on_representation_event(&proxy).await?)
234 } else {
235 let _ = proxy.get_attributes(Default::default()).await.map_err(|e| {
237 if let fidl::Error::ClientChannelClosed { status, .. } = e {
238 status
239 } else {
240 panic!("Unhandled FIDL error: {:?}", e);
241 }
242 })?;
243 None
244 };
245 Ok((convert_node_proxy(proxy), representation))
246}
247
248async fn get_on_representation_event(
250 node_proxy: &fio::NodeProxy,
251) -> Result<fio::Representation, zx::Status> {
252 let event = Clone::clone(node_proxy)
254 .take_event_stream()
255 .try_next()
256 .await
257 .map_err(|e| {
258 if let fidl::Error::ClientChannelClosed { status, .. } = e {
259 status
260 } else {
261 panic!("Unhandled FIDL error: {:?}", e);
262 }
263 })?
264 .expect("Missing NodeEvent in stream!");
265 let representation = match event {
266 fio::NodeEvent::OnRepresentation { payload } => payload,
267 _ => panic!("Found unexpected NodeEvent type in stream!"),
268 };
269 Ok(representation)
270}