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 = test_harness.get_directory(vec![dir_entry], file_flags);
114 let file_proxy = dir_proxy.open_node::<fio::FileMarker>(&file_path, file_flags, None).await?;
115 let vmo = file_proxy
116 .get_backing_memory(vmo_flags)
117 .await
118 .expect("get_backing_memory failed")
119 .map_err(zx::Status::from_raw)?;
120 Ok((vmo, (dir_proxy, file_proxy)))
121}
122
123pub fn directory(name: &str, entries: Vec<io_test::DirectoryEntry>) -> io_test::DirectoryEntry {
125 let entries: Vec<Option<Box<io_test::DirectoryEntry>>> =
126 entries.into_iter().map(|e| Some(Box::new(e))).collect();
127 io_test::DirectoryEntry::Directory(io_test::Directory { name: name.to_string(), entries })
128}
129
130pub fn remote_directory(name: &str, remote_dir: fio::DirectoryProxy) -> io_test::DirectoryEntry {
132 let remote_client = ClientEnd::<fio::DirectoryMarker>::new(
133 remote_dir.into_channel().unwrap().into_zx_channel(),
134 );
135
136 io_test::DirectoryEntry::RemoteDirectory(io_test::RemoteDirectory {
137 name: name.to_string(),
138 remote_client,
139 })
140}
141
142pub fn file(name: &str, contents: Vec<u8>) -> io_test::DirectoryEntry {
144 io_test::DirectoryEntry::File(io_test::File { name: name.to_string(), contents })
145}
146
147pub fn executable_file(name: &str) -> io_test::DirectoryEntry {
149 io_test::DirectoryEntry::ExecutableFile(io_test::ExecutableFile { name: name.to_string() })
150}
151
152#[async_trait]
155pub trait DirectoryProxyExt {
156 async fn open_node<T: ProtocolMarker>(
161 &self,
162 path: &str,
163 flags: fio::Flags,
164 options: Option<fio::Options>,
165 ) -> Result<T::Proxy, zx::Status>;
166
167 async fn open_node_repr<T: ProtocolMarker>(
172 &self,
173 path: &str,
174 flags: fio::Flags,
175 options: Option<fio::Options>,
176 ) -> Result<(T::Proxy, fio::Representation), zx::Status>;
177}
178
179#[async_trait]
180impl DirectoryProxyExt for fio::DirectoryProxy {
181 async fn open_node<T: ProtocolMarker>(
182 &self,
183 path: &str,
184 flags: fio::Flags,
185 options: Option<fio::Options>,
186 ) -> Result<T::Proxy, zx::Status> {
187 open_node_impl::<T>(self, path, flags, options).await.map(|(proxy, _representation)| proxy)
188 }
189
190 async fn open_node_repr<T: ProtocolMarker>(
191 &self,
192 path: &str,
193 flags: fio::Flags,
194 options: Option<fio::Options>,
195 ) -> Result<(T::Proxy, fio::Representation), zx::Status> {
196 assert!(
197 flags.contains(fio::Flags::FLAG_SEND_REPRESENTATION),
198 "flags must specify the FLAG_SEND_REPRESENTATION flag to use this function!"
199 );
200 let (proxy, representation) = open_node_impl::<T>(self, path, flags, options).await?;
201 Ok((proxy, representation.unwrap()))
202 }
203}
204
205async fn open_node_impl<T: ProtocolMarker>(
206 dir: &fio::DirectoryProxy,
207 path: &str,
208 flags: fio::Flags,
209 options: Option<fio::Options>,
210) -> Result<(T::Proxy, Option<fio::Representation>), zx::Status> {
211 let (proxy, server) = create_proxy::<fio::NodeMarker>();
212 dir.open(path, flags, &options.unwrap_or_default(), server.into_channel())
213 .expect("Failed to call open3");
214 let representation = if flags.contains(fio::Flags::FLAG_SEND_REPRESENTATION) {
215 Some(get_on_representation_event(&proxy).await?)
216 } else {
217 let _ = proxy.get_attributes(Default::default()).await.map_err(|e| {
219 if let fidl::Error::ClientChannelClosed { status, .. } = e {
220 status
221 } else {
222 panic!("Unhandled FIDL error: {:?}", e);
223 }
224 })?;
225 None
226 };
227 Ok((convert_node_proxy(proxy), representation))
228}
229
230async fn get_on_representation_event(
232 node_proxy: &fio::NodeProxy,
233) -> Result<fio::Representation, zx::Status> {
234 let event = Clone::clone(node_proxy)
236 .take_event_stream()
237 .try_next()
238 .await
239 .map_err(|e| {
240 if let fidl::Error::ClientChannelClosed { status, .. } = e {
241 status
242 } else {
243 panic!("Unhandled FIDL error: {:?}", e);
244 }
245 })?
246 .expect("Missing NodeEvent in stream!");
247 let representation = match event {
248 fio::NodeEvent::OnRepresentation { payload } => payload,
249 _ => panic!("Found unexpected NodeEvent type in stream!"),
250 };
251 Ok(representation)
252}