blob: 50b41b991c9e9f89989d7ea58d5506abc80a15be [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Unit tests for fuchsia-component that exercise only the public API.
#![cfg(test)]
use {
anyhow::{Context as _, Error},
fidl::endpoints::{create_proxy, ServerEnd, UnifiedServiceMarker},
fidl_fuchsia_component_test::{
CounterRequest, CounterRequestStream, CounterServiceMarker, CounterServiceRequest,
},
fidl_fuchsia_io::{
DirectoryMarker, DirectoryProxy, FileMarker, FileProxy, NodeEvent, NodeInfo, NodeMarker,
NodeProxy, SeekOrigin, Service, OPEN_RIGHT_READABLE,
},
files_async::readdir,
fuchsia_async::{self as fasync, run_until_stalled},
fuchsia_component::server::{ServiceFs, ServiceFsDir, ServiceObj},
fuchsia_vfs_pseudo_fs::{
directory::entry::DirectoryEntry, file::simple::read_only_static, pseudo_directory,
},
fuchsia_zircon::{self as zx, HandleBased as _},
futures::{future::try_join, stream::TryStreamExt, FutureExt, StreamExt},
matches::assert_matches,
std::{future::Future, path::Path, sync::atomic},
};
#[run_until_stalled(test)]
async fn complete_with_no_clients() {
ServiceFs::new().collect().await
}
#[run_until_stalled(test)]
async fn complete_with_no_remaining_clients() {
let (fs, _) = fs_with_connection();
fs.collect().await
}
fn fs_with_connection<'a, T: 'a>() -> (ServiceFs<ServiceObj<'a, T>>, DirectoryProxy) {
let mut fs = ServiceFs::new();
let (dir_proxy, dir_server_end) =
create_proxy::<DirectoryMarker>().expect("Unable to create directory proxy");
fs.serve_connection(dir_server_end.into_channel()).expect("unable to serve main dir proxy");
(fs, dir_proxy)
}
#[run_until_stalled(test)]
async fn check_bad_flags_root() -> Result<(), Error> {
let (fs, dir_proxy) = fs_with_connection::<'_, ()>();
let serve_fut = fs.collect().map(Ok);
let test_fut = async move {
// attempt to open . with CREATE flags
let flags = fidl_fuchsia_io::OPEN_FLAG_DESCRIBE
| fidl_fuchsia_io::OPEN_FLAG_DIRECTORY
| fidl_fuchsia_io::OPEN_FLAG_CREATE;
let mode = fidl_fuchsia_io::MODE_TYPE_DIRECTORY;
let (node_proxy, server_end) = create_proxy::<NodeMarker>()?;
dir_proxy.open(flags, mode, ".", server_end.into()).unwrap();
assert_open_status(&node_proxy, zx::Status::NOT_SUPPORTED).await;
Ok::<(), Error>(())
};
let ((), ()) = try_join(serve_fut, test_fut).await?;
Ok(())
}
#[run_until_stalled(test)]
async fn check_bad_flags_folder() -> Result<(), Error> {
let (mut fs, dir_proxy) = fs_with_connection::<'_, ()>();
fs.dir("foo").add_service_at("bar", |_chan| Some(()));
let serve_fut = fs.collect().map(Ok);
let test_fut = async move {
// attempt to create a folder that already exists in ServiceFS
let flags = fidl_fuchsia_io::OPEN_FLAG_DESCRIBE
| fidl_fuchsia_io::OPEN_FLAG_DIRECTORY
| fidl_fuchsia_io::OPEN_FLAG_CREATE;
let mode = fidl_fuchsia_io::MODE_TYPE_DIRECTORY;
let (node_proxy, server_end) = create_proxy::<NodeMarker>()?;
dir_proxy.open(flags, mode, "foo", server_end.into()).unwrap();
assert_open_status(&node_proxy, zx::Status::NOT_SUPPORTED).await;
Ok::<(), Error>(())
};
let ((), ()) = try_join(serve_fut, test_fut).await?;
Ok(())
}
#[run_until_stalled(test)]
async fn check_bad_flags_file() -> Result<(), Error> {
let (mut fs, dir_proxy) = fs_with_connection::<'_, ()>();
fs.dir("foo").add_service_at("bar", |_chan| Some(()));
let serve_fut = fs.collect().map(Ok);
let test_fut = async move {
// attempt to create a file that already exists in ServiceFS
let flags = fidl_fuchsia_io::OPEN_FLAG_DESCRIBE
| fidl_fuchsia_io::OPEN_FLAG_NOT_DIRECTORY
| fidl_fuchsia_io::OPEN_FLAG_CREATE
| fidl_fuchsia_io::OPEN_FLAG_TRUNCATE;
let mode = fidl_fuchsia_io::MODE_TYPE_FILE;
let (node_proxy, server_end) = create_proxy::<NodeMarker>()?;
dir_proxy.open(flags, mode, "foo/bar", server_end.into()).unwrap();
assert_open_status(&node_proxy, zx::Status::NOT_SUPPORTED).await;
Ok::<(), Error>(())
};
let ((), ()) = try_join(serve_fut, test_fut).await?;
Ok(())
}
#[run_until_stalled(test)]
async fn check_bad_flags_new_file() -> Result<(), Error> {
let (fs, dir_proxy) = fs_with_connection::<'_, ()>();
let serve_fut = fs.collect().map(Ok);
let test_fut = async move {
// attempt to create a new file in ServiceFS
let flags = fidl_fuchsia_io::OPEN_FLAG_DESCRIBE
| fidl_fuchsia_io::OPEN_FLAG_NOT_DIRECTORY
| fidl_fuchsia_io::OPEN_FLAG_CREATE
| fidl_fuchsia_io::OPEN_FLAG_TRUNCATE;
let mode = fidl_fuchsia_io::MODE_TYPE_FILE;
let (node_proxy, server_end) = create_proxy::<NodeMarker>()?;
dir_proxy.open(flags, mode, "qaz", server_end.into()).unwrap();
assert_open_status(&node_proxy, zx::Status::NOT_SUPPORTED).await;
Ok::<(), Error>(())
};
let ((), ()) = try_join(serve_fut, test_fut).await?;
Ok(())
}
#[run_until_stalled(test)]
async fn serve_on_root_and_subdir() -> Result<(), Error> {
const SERVICE_NAME: &str = "foo";
// Example of serving a dummy service that just throws away
// the channel on an arbitrary directory.
fn serve_on_dir(dir: &mut ServiceFsDir<'_, ServiceObj<'_, ()>>) {
dir.add_service_at(SERVICE_NAME, |_chan| Some(()));
}
fn assert_peer_closed(chan: zx::Channel) {
let err = chan
.read_raw(&mut vec![], &mut vec![])
.expect("unexpected too small buffer")
.0
.expect_err("should've been a PEER_CLOSED error");
assert_eq!(err, zx::Status::PEER_CLOSED);
}
async fn assert_has_service_child(
fs: &mut ServiceFs<ServiceObj<'_, ()>>,
dir_proxy: &DirectoryProxy,
) {
let flags = 0;
let mode = fidl_fuchsia_io::MODE_TYPE_SERVICE;
let (server_end, client_end) = zx::Channel::create().expect("create channel");
dir_proxy.open(flags, mode, SERVICE_NAME, server_end.into()).expect("open");
fs.next().await.expect("expected one service to have been started");
assert_peer_closed(client_end);
}
let (mut fs, dir_proxy) = fs_with_connection();
// serve at both the root directory and a child dir /fooey
serve_on_dir(&mut fs.root_dir());
serve_on_dir(&mut fs.dir("fooey"));
// attempt to connect to the root service
assert_has_service_child(&mut fs, &dir_proxy).await;
// attempt to connect to the /fooey dir
let flags = fidl_fuchsia_io::OPEN_FLAG_DIRECTORY;
let mode = fidl_fuchsia_io::MODE_TYPE_DIRECTORY;
let (subdir_proxy, server_end) = create_proxy::<DirectoryMarker>()?;
dir_proxy.open(flags, mode, "fooey", server_end.into_channel().into())?;
// attempt to connect ot the service under /fooey
assert_has_service_child(&mut fs, &subdir_proxy).await;
// drop the connections and ensure the fs has no outstanding
// clients or service connections
drop(dir_proxy);
drop(subdir_proxy);
assert!(fs.next().await.is_none());
Ok(())
}
#[run_until_stalled(test)]
async fn open_service_node_reference() -> Result<(), Error> {
const PATH: &str = "service_name";
let (mut fs, dir_proxy) = fs_with_connection();
fs.add_service_at(PATH, |_chan| Some(()));
let serve_fut = fs.collect().map(Ok);
let open_reference_fut = async {
let flags = fidl_fuchsia_io::OPEN_FLAG_NODE_REFERENCE;
let mode = fidl_fuchsia_io::MODE_TYPE_SERVICE;
let (node_proxy, node_server_end) = create_proxy::<NodeMarker>()?;
dir_proxy.open(flags, mode, PATH, node_server_end)?;
drop(dir_proxy);
let info = node_proxy.describe().await?;
if let NodeInfo::Service(Service {}) = info {
// ok
} else {
panic!("expected service node, found {:?}", info);
}
drop(node_proxy);
Ok::<(), Error>(())
};
let ((), ()) = try_join(serve_fut, open_reference_fut).await?;
Ok(())
}
// Identical to `open_service_node_reference`, but clones the opened directory before attempting to
// open the service node.
#[run_until_stalled(test)]
async fn clone_service_dir() -> Result<(), Error> {
const PATH: &str = "service_name";
let (mut fs, dir_proxy) = fs_with_connection();
fs.add_service_at(PATH, |_chan| Some(()));
let serve_fut = fs.collect().map(Ok);
let open_reference_fut = async {
let (dir_proxy_clone, dir_server_end_clone) = create_proxy::<DirectoryMarker>()?;
dir_proxy.clone(
fidl_fuchsia_io::CLONE_FLAG_SAME_RIGHTS,
ServerEnd::new(dir_server_end_clone.into_channel()),
)?;
drop(dir_proxy);
let flags = fidl_fuchsia_io::OPEN_FLAG_NODE_REFERENCE;
let mode = fidl_fuchsia_io::MODE_TYPE_SERVICE;
let (node_proxy, node_server_end) = create_proxy::<NodeMarker>()?;
dir_proxy_clone.open(flags, mode, PATH, node_server_end)?;
drop(dir_proxy_clone);
let info = node_proxy.describe().await?;
if let NodeInfo::Service(Service {}) = info {
// ok
} else {
panic!("expected service node, found {:?}", info);
}
drop(node_proxy);
Ok::<(), Error>(())
};
let ((), ()) = try_join(serve_fut, open_reference_fut).await?;
Ok(())
}
#[run_until_stalled(test)]
async fn handles_dir_not_dir_flags() -> Result<(), Error> {
let service_open_count = atomic::AtomicU32::new(0);
let (mut fs, root_proxy) = fs_with_connection();
fs.dir("dir").add_service_at("notdir", |chan| {
service_open_count.fetch_add(1, atomic::Ordering::SeqCst);
let (_request_stream, control_handle) =
ServerEnd::<NodeMarker>::new(chan).into_stream_and_control_handle().unwrap();
control_handle.send_on_open_(zx::Status::OK.into_raw(), None).unwrap();
None
});
let serve_fut = fs.collect().map(Ok);
let service_open_count = &service_open_count;
let test_fut = async move {
let dir_flags = fidl_fuchsia_io::OPEN_FLAG_DESCRIBE | fidl_fuchsia_io::OPEN_FLAG_DIRECTORY;
let not_dir_flags =
fidl_fuchsia_io::OPEN_FLAG_DESCRIBE | fidl_fuchsia_io::OPEN_FLAG_NOT_DIRECTORY;
// Verify flags when opening a directory.
let (node_proxy, node_end) = create_proxy::<NodeMarker>()?;
root_proxy.open(dir_flags, 0, "dir", node_end)?;
assert_open_status(&node_proxy, zx::Status::OK).await;
let (node_proxy, node_end) = create_proxy::<NodeMarker>()?;
root_proxy.open(not_dir_flags, 0, "dir", node_end)?;
assert_open_status(&node_proxy, zx::Status::NOT_FILE).await;
// Verify flags when opening a file.
assert_eq!(service_open_count.load(atomic::Ordering::SeqCst), 0);
let (node_proxy, node_end) = create_proxy::<NodeMarker>()?;
root_proxy.open(dir_flags, 0, "dir/notdir", node_end)?;
assert_open_status(&node_proxy, zx::Status::NOT_DIR).await;
assert_eq!(service_open_count.load(atomic::Ordering::SeqCst), 0);
let (node_proxy, node_end) = create_proxy::<NodeMarker>()?;
root_proxy.open(not_dir_flags, 0, "dir/notdir", node_end)?;
assert_open_status(&node_proxy, zx::Status::OK).await;
assert_eq!(service_open_count.load(atomic::Ordering::SeqCst), 1);
Ok::<(), Error>(())
};
let ((), ()) = try_join(serve_fut, test_fut).await?;
Ok(())
}
/// Serves a dummy unified service `US` that immediately drops the request channel.
fn serve_dummy_unified_service<US>() -> (impl Future<Output = Result<(), Error>>, DirectoryProxy)
where
US: UnifiedServiceMarker,
{
let (mut fs, dir_proxy) = fs_with_connection();
fs.add_unified_service(|_: US::Request| ());
(fs.collect().map(Ok), dir_proxy)
}
/// Serves an instance of a dummy unified service `US` named `instance` that immediately drops the request channel.
fn serve_dummy_unified_service_instance<US>(
instance: &str,
) -> (impl Future<Output = Result<(), Error>>, DirectoryProxy)
where
US: UnifiedServiceMarker,
{
let (mut fs, dir_proxy) = fs_with_connection();
fs.add_unified_service_instance(instance, |_: US::Request| ());
(fs.collect().map(Ok), dir_proxy)
}
/// Returns the NodeInfo of a node reference located at `path` under the directory `dir_proxy`.
async fn node_reference_type_at_path(
dir_proxy: &DirectoryProxy,
path: &str,
) -> Result<NodeInfo, Error> {
let flags = fidl_fuchsia_io::OPEN_FLAG_NODE_REFERENCE;
let mode = fidl_fuchsia_io::MODE_TYPE_DIRECTORY;
let (node_proxy, node_server_end) = create_proxy::<NodeMarker>()?;
dir_proxy.open(flags, mode, path, node_server_end)?;
Ok(node_proxy.describe().await?)
}
#[run_until_stalled(test)]
async fn open_unified_service_node_reference() -> Result<(), Error> {
let (serve_fut, dir_proxy) = serve_dummy_unified_service::<CounterServiceMarker>();
let open_reference_fut = async {
let info = node_reference_type_at_path(&dir_proxy, CounterServiceMarker::SERVICE_NAME)
.await
.expect("failed to get NodeInfo");
assert_matches!(info, NodeInfo::Directory(_));
drop(dir_proxy);
Ok::<(), Error>(())
};
let ((), ()) = try_join(serve_fut, open_reference_fut).await?;
Ok(())
}
#[run_until_stalled(test)]
async fn open_unified_service_instance_node_reference() -> Result<(), Error> {
let (serve_fut, dir_proxy) = serve_dummy_unified_service::<CounterServiceMarker>();
let open_reference_fut = async {
let path = format!("{}/default", CounterServiceMarker::SERVICE_NAME);
let info =
node_reference_type_at_path(&dir_proxy, &path).await.expect("failed to get NodeInfo");
assert_matches!(info, NodeInfo::Directory(_));
drop(dir_proxy);
Ok::<(), Error>(())
};
let ((), ()) = try_join(serve_fut, open_reference_fut).await?;
Ok(())
}
async fn list_service_entries(
dir_proxy: &DirectoryProxy,
path: &str,
) -> Result<Vec<String>, Error> {
let flags = fidl_fuchsia_io::OPEN_FLAG_DIRECTORY
| fidl_fuchsia_io::OPEN_RIGHT_READABLE
| fidl_fuchsia_io::OPEN_RIGHT_WRITABLE;
let instance_proxy = io_util::open_directory(dir_proxy, Path::new(&path), flags)?;
let mut entries =
files_async::readdir(&instance_proxy)
.await?
.into_iter()
.filter_map(|e| {
if let files_async::DirentKind::Service = e.kind {
Some(e.name)
} else {
None
}
})
.collect::<Vec<_>>();
entries.sort();
Ok(entries)
}
#[run_until_stalled(test)]
async fn list_unified_service_members_of_default_instance() -> Result<(), Error> {
let (serve_fut, dir_proxy) = serve_dummy_unified_service::<CounterServiceMarker>();
let open_reference_fut = async {
let path = format!("{}/default", CounterServiceMarker::SERVICE_NAME);
let entries =
list_service_entries(&dir_proxy, &path).await.expect("failed to list service entries");
assert_eq!(vec![String::from("counter"), String::from("counter_v2")], entries);
drop(dir_proxy);
Ok::<(), Error>(())
};
let ((), ()) = try_join(serve_fut, open_reference_fut).await?;
Ok(())
}
#[run_until_stalled(test)]
async fn list_unified_service_members_of_other_instance() -> Result<(), Error> {
let (serve_fut, dir_proxy) =
serve_dummy_unified_service_instance::<CounterServiceMarker>("other");
let open_reference_fut = async {
let path = format!("{}/other", CounterServiceMarker::SERVICE_NAME);
let entries =
list_service_entries(&dir_proxy, &path).await.expect("failed to list service entries");
assert_eq!(vec![String::from("counter"), String::from("counter_v2")], entries);
drop(dir_proxy);
Ok::<(), Error>(())
};
let ((), ()) = try_join(serve_fut, open_reference_fut).await?;
Ok(())
}
async fn serve_get_and_increment(mut stream: CounterRequestStream) -> Result<(), Error> {
let mut value: u32 = 0;
while let Some(CounterRequest::GetAndIncrement { responder }) =
stream.try_next().await.context("error running fetching next request")?
{
responder.send(value).context("error sending response")?;
value += 1;
}
Ok(())
}
#[run_until_stalled(test)]
async fn connect_to_unified_service_member_of_default_instance() -> Result<(), Error> {
enum IncomingService {
Counter(CounterServiceRequest),
}
let (mut fs, dir_proxy) = fs_with_connection();
// Serve the default instance of CounterService.
fs.add_unified_service(IncomingService::Counter);
let serve_fut = fs.for_each_concurrent(1, |IncomingService::Counter(req)| {
async {
match req {
CounterServiceRequest::Counter(stream) => {
serve_get_and_increment(stream).await.expect("serving Counter failed")
}
CounterServiceRequest::CounterV2(_) => (), // Not under test
}
}
});
fasync::Task::spawn(serve_fut).detach();
let dir_request =
dir_proxy.into_channel().expect("failed to extract channel from proxy").into_zx_channel();
// Connect to the default instance of CounterService and make calls to the "counter" member.
let service_proxy = fuchsia_component::client::connect_to_unified_service_at_dir::<
CounterServiceMarker,
>(&dir_request)?;
let counter_proxy = service_proxy.counter().expect("failed conencting to counter member");
let value: u32 =
counter_proxy.get_and_increment().await.expect("first call to get_and_increment failed");
assert_eq!(value, 0);
let value =
counter_proxy.get_and_increment().await.expect("second call to get_and_increment failed");
assert_eq!(value, 1);
drop(dir_request);
Ok(())
}
async fn assert_open_status(proxy: &NodeProxy, expected: zx::Status) {
let mut events = proxy.take_event_stream();
let NodeEvent::OnOpen_ { s: actual, info: _ } =
events.next().await.expect("event").expect("no error");
assert_eq!(zx::Status::from_raw(actual), expected);
}
async fn assert_read<'a>(
file_proxy: &'a FileProxy,
length: u64,
expected: &'a [u8],
) -> Result<(), Error> {
let (status, read_data) = file_proxy.read(length).await?;
zx::Status::ok(status)?;
assert_eq!(&*read_data, expected);
Ok(())
}
// close the file and check that further reads fail.
async fn assert_close(file_proxy: &FileProxy) -> Result<(), Error> {
let status = file_proxy.close().await?;
zx::Status::ok(status)?;
assert!(file_proxy.read(0).await.is_err());
Ok(())
}
#[run_until_stalled(test)]
async fn open_remote_directory_files() -> Result<(), Error> {
let mut root = ServiceFs::new();
let mut fs = ServiceFs::new();
let (remote_proxy, remote_server_end) = create_proxy::<DirectoryMarker>()?;
let data = b"test";
let vmo = zx::Vmo::create(4096)?;
vmo.write(data, 0)?;
root.dir("files").add_vmo_file_at(
"test.txt",
vmo.duplicate_handle(zx::Rights::READ)?,
0,
data.len() as u64,
);
root.serve_connection(remote_server_end.into_channel())?;
// Add the remote as "test"
fs.add_remote("test", remote_proxy);
let (dir_proxy, dir_server_end) = create_proxy::<DirectoryMarker>()?;
fs.serve_connection(dir_server_end.into_channel())?;
fuchsia_async::Task::spawn(root.collect::<()>()).detach();
fuchsia_async::Task::spawn(fs.collect::<()>()).detach();
// Open the test file
let (file_proxy, file_server_end) = create_proxy::<FileMarker>()?;
let flags = fidl_fuchsia_io::OPEN_RIGHT_READABLE;
let mode = fidl_fuchsia_io::MODE_TYPE_FILE;
dir_proxy.open(flags, mode, "test/files/test.txt", file_server_end.into_channel().into())?;
// Open the top of the remote hierarchy.
let (top_proxy, top_end) = create_proxy::<DirectoryMarker>()?;
dir_proxy.open(
fidl_fuchsia_io::OPEN_RIGHT_READABLE,
fidl_fuchsia_io::MODE_TYPE_DIRECTORY,
"test",
top_end.into_channel().into(),
)?;
drop(dir_proxy);
// Check that we can read the contents of the file.
assert_read(&file_proxy, data.len() as u64, data).await.expect("read data did not match");
top_proxy.read_dirents(128).await.expect("failed to read top directory entries");
Ok(())
}
#[run_until_stalled(test)]
async fn open_remote_pseudo_directory_files() -> Result<(), Error> {
let data = "test";
let mut root = pseudo_directory! {
"test.txt" => read_only_static(data)
};
let mut fs = ServiceFs::new();
let (remote_proxy, remote_server_end) = create_proxy::<DirectoryMarker>()?;
root.open(
fidl_fuchsia_io::OPEN_RIGHT_READABLE,
0,
&mut vec![].into_iter(),
fidl::endpoints::ServerEnd::<NodeMarker>::from(remote_server_end.into_channel()),
);
// Add the remote as "test"
fs.add_remote("test", remote_proxy);
let (dir_proxy, dir_server_end) = create_proxy::<DirectoryMarker>()?;
fs.serve_connection(dir_server_end.into_channel())?;
fuchsia_async::Task::spawn(async move {
root.await;
})
.detach();
fuchsia_async::Task::spawn(fs.collect::<()>()).detach();
// Open the test file
let (file_proxy, file_server_end) = create_proxy::<FileMarker>()?;
let flags = fidl_fuchsia_io::OPEN_RIGHT_READABLE;
let mode = fidl_fuchsia_io::MODE_TYPE_FILE;
dir_proxy.open(flags, mode, "test/test.txt", file_server_end.into_channel().into())?;
// Check that we can read the contents of the file.
assert_read(&file_proxy, data.len() as u64, data.as_bytes())
.await
.expect("could not read expected data");
Ok(())
}
#[run_until_stalled(test)]
async fn open_remote_nested_servicefs_files() -> Result<(), Error> {
let mut fs = ServiceFs::new();
// Create a nested ServiceFs contains directories "temp/folder".
let mut nested_fs = ServiceFs::new();
let (nested_proxy, nested_server_end) = create_proxy::<DirectoryMarker>()?;
nested_fs.dir("temp").dir("folder");
nested_fs.serve_connection(nested_server_end.into_channel())?;
fuchsia_async::Task::spawn(nested_fs.collect::<()>()).detach();
// Add the remote as "test"
// "temp/folder" should appear in this directory as "test/folder".
fs.add_remote(
"test",
io_util::open_directory(&nested_proxy, Path::new("temp"), OPEN_RIGHT_READABLE)?,
);
let (dir_proxy, dir_server_end) = create_proxy::<DirectoryMarker>()?;
fs.serve_connection(dir_server_end.into_channel())?;
fuchsia_async::Task::spawn(fs.collect::<()>()).detach();
// Open and read "test"
let temp_proxy = io_util::open_directory(&dir_proxy, Path::new("test"), OPEN_RIGHT_READABLE)?;
let result = readdir(&temp_proxy).await;
assert!(!result.is_err(), "got Err instead of Ok: {:?}", result.unwrap_err());
let files = result.unwrap();
assert_eq!(1, files.len());
assert_eq!("folder", files[0].name);
Ok(())
}
#[run_until_stalled(test)]
async fn create_nested_env_with_sub_dir() {
for dir_name in &["svc", "bin", "foobar"] {
let mut fs: ServiceFs<ServiceObj<'_, ()>> = ServiceFs::new();
fs.dir(*dir_name).add_service_at("test", |_chan: zx::Channel| None);
if fs.create_nested_environment("should-not-be-created").is_ok() {
panic!("create_nested_environment should fail when a svc dir exists");
}
}
}
/// Sets up a new filesystem containing a vmofile.
///
/// Returns a future which runs the filesystem, a proxy connected to the vmofile, and the data
/// expected to be in the file.
fn set_up_and_connect_to_vmo_file(
) -> Result<(impl Future<Output = Result<(), Error>>, FileProxy, Vec<u8>), Error> {
const PATH: &str = "foo";
const VMO_SIZE: u64 = 256;
const VMO_FILE_OFFSET: usize = 5;
const VMO_FILE_LENGTH: usize = 22;
// 0, 1, 2, 3, 4, 5...
let mut data = vec![];
let mut data_i = 0u8;
data.resize_with(VMO_SIZE as usize, || {
data_i = data_i.wrapping_add(1);
data_i
});
let (mut fs, dir_proxy) = fs_with_connection();
let vmo = zx::Vmo::create(VMO_SIZE)?;
vmo.write(&*data, 0)?;
fs.add_vmo_file_at(
PATH,
vmo.duplicate_handle(zx::Rights::READ)?,
VMO_FILE_OFFSET as u64,
VMO_FILE_LENGTH as u64,
);
// Open a connection to the file within the directory
let (file_proxy, file_server_end) = create_proxy::<FileMarker>()?;
let flags = fidl_fuchsia_io::OPEN_RIGHT_READABLE;
let mode = fidl_fuchsia_io::MODE_TYPE_FILE;
dir_proxy.open(flags, mode, PATH, file_server_end.into_channel().into())?;
// truncate `data` to just the data that appears in the file
data.drain(..VMO_FILE_OFFSET);
data.truncate(VMO_FILE_LENGTH);
Ok((fs.collect().map(Ok), file_proxy, data))
}
macro_rules! async_test_with_vmo_file {
(
|$file_proxy:ident, $file_data:ident|
$( $test_name:ident => $test_future:expr ,)*
) => { $(
#[run_until_stalled(test)]
async fn $test_name() -> Result<(), Error> {
let (serve_fut, $file_proxy, $file_data) = set_up_and_connect_to_vmo_file()?;
let $file_data = &*$file_data;
let test_future = $test_future;
let ((), ()) = try_join(serve_fut, test_future).await?;
Ok(())
}
)* }
}
async_test_with_vmo_file![
|file_proxy, file_data|
describe_vmo_file => async {
// Describe the file
let (status, attrs) = file_proxy.get_attr().await?;
zx::Status::ok(status)?;
assert!(attrs.mode & fidl_fuchsia_io::MODE_TYPE_FILE != 0);
assert_eq!(attrs.content_size, file_data.len() as u64);
assert_eq!(attrs.storage_size, file_data.len() as u64);
drop(file_proxy);
Ok(())
},
read_from_vmo_file => async {
// Read the whole file
assert_read(&file_proxy, file_data.len() as u64, file_data).await?;
drop(file_proxy);
Ok(())
},
seek_around_vmo_file => async {
// Read the whole file
assert_read(&file_proxy, file_data.len() as u64, file_data).await?;
// Try and read the whole file again, while our cursor is at the end.
// This should return no more data.
assert_read(&file_proxy, file_data.len() as u64, &[]).await?;
// Seek back to 5 bytes from the end and read again.
let (status, position) = file_proxy.seek(-5, SeekOrigin::End).await?;
zx::Status::ok(status)?;
assert_eq!(position, file_data.len() as u64 - 5);
let read_at_count = 10usize;
let read_at_offset = 4usize;
let (status, read_at_data) =
file_proxy.read_at(read_at_count as u64, read_at_offset as u64).await?;
zx::Status::ok(status)?;
assert_eq!(&*read_at_data, &file_data[read_at_offset..(read_at_offset + read_at_count)]);
drop(file_proxy);
Ok(())
},
read_from_clone => async {
// Create a clone of the file
let (file_proxy_clone, file_clone_server_end) = create_proxy::<FileMarker>()?;
let flags = fidl_fuchsia_io::OPEN_RIGHT_READABLE;
file_proxy.clone(flags, file_clone_server_end.into_channel().into())?;
// Read the whole file
assert_read(&file_proxy, file_data.len() as u64, file_data).await?;
assert_close(&file_proxy).await?;
// Check that our original clone was never moved from position zero nor closed.
assert_read(&file_proxy_clone, file_data.len() as u64, file_data).await?;
drop(file_proxy);
drop(file_proxy_clone);
Ok(())
},
];