1// Copyright 2021 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.
45use crate::flags::Rights;
6use fidl::endpoints::create_proxy;
7use {
8 fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_decl as fdecl,
9 fidl_fuchsia_io as fio, fidl_fuchsia_io_test as io_test,
10};
1112/// Helper struct for connecting to an io1 test harness and running a conformance test on it.
13pub struct TestHarness {
14/// FIDL proxy to the io1 test harness.
15pub proxy: io_test::TestHarnessProxy,
1617/// Config for the filesystem.
18pub config: io_test::HarnessConfig,
1920/// All [`io_test::Directory`] rights supported by the filesystem.
21pub dir_rights: Rights,
2223/// All [`io_test::File`] rights supported by the filesystem.
24pub file_rights: Rights,
2526/// All [`io_test::ExecutableFile`] rights supported by the filesystem.
27pub executable_file_rights: Rights,
28}
2930impl TestHarness {
31/// Connects to the test harness and returns a `TestHarness` struct.
32pub async fn new() -> TestHarness {
33let proxy = connect_to_harness().await;
34let config = proxy.get_config().await.expect("Could not get config from proxy");
3536// Validate configuration options for consistency, disallow invalid combinations.
37if config.supports_modify_directory {
38assert!(
39 config.supports_get_token,
40"GetToken must be supported for testing Rename/Link!"
41);
42 }
43if config.supports_append {
44assert!(config.supports_mutable_file, "Files supporting append must also be mutable.");
45 }
46if config.supports_truncate {
47assert!(
48 config.supports_mutable_file,
49"Files supporting truncate must also be mutable."
50);
51 }
5253// Generate set of supported open rights for each object type.
54let dir_rights = Rights::new(get_supported_dir_rights(&config));
55let file_rights = Rights::new(get_supported_file_rights(&config));
56let executable_file_rights = Rights::new(fio::Rights::READ_BYTES | fio::Rights::EXECUTE);
5758 TestHarness { proxy, config, dir_rights, file_rights, executable_file_rights }
59 }
6061/// Creates a [`fio::DirectoryProxy`] with the given root directory structure.
62pub fn get_directory(
63&self,
64 entries: Vec<io_test::DirectoryEntry>,
65 flags: fio::Flags,
66 ) -> fio::DirectoryProxy {
67let contents: Vec<Option<Box<io_test::DirectoryEntry>>> =
68 entries.into_iter().map(|e| Some(Box::new(e))).collect();
69let (client, server) = create_proxy::<fio::DirectoryMarker>();
70self.proxy
71 .create_directory(contents, flags, server)
72 .expect("Cannot get directory from test harness");
73 client
74 }
7576/// Helper function which gets service directory from the harness as a [`fio::DirectoryProxy`].
77 /// Requires that the harness supports service directories, otherwise will panic.
78pub async fn open_service_directory(&self) -> fio::DirectoryProxy {
79assert!(self.config.supports_services);
80let client_end = self.proxy.open_service_directory().await.unwrap();
81 client_end.into_proxy()
82 }
8384/// Returns the abilities [`io_test::File`] objects should have for the harness.
85pub fn supported_file_abilities(&self) -> fio::Abilities {
86let mut abilities = fio::Abilities::READ_BYTES | fio::Abilities::GET_ATTRIBUTES;
87if self.config.supports_mutable_file {
88 abilities |= fio::Abilities::WRITE_BYTES;
89 }
90if self.supports_mutable_attrs() {
91 abilities |= fio::Abilities::UPDATE_ATTRIBUTES;
92 }
93 abilities
94 }
9596/// Returns the abilities [`io_test::Directory`] objects should have for the harness.
97pub fn supported_dir_abilities(&self) -> fio::Abilities {
98if self.config.supports_modify_directory {
99 fio::Abilities::GET_ATTRIBUTES
100 | fio::Abilities::UPDATE_ATTRIBUTES
101 | fio::Abilities::ENUMERATE
102 | fio::Abilities::TRAVERSE
103 | fio::Abilities::MODIFY_DIRECTORY
104 } else {
105 fio::Abilities::GET_ATTRIBUTES | fio::Abilities::ENUMERATE | fio::Abilities::TRAVERSE
106 }
107 }
108109/// Returns true if the harness supports at least one mutable attribute, false otherwise.
110 ///
111 /// *NOTE*: To allow testing both the io1 SetAttrs and io2 UpdateAttributes methods, harnesses
112 /// that support mutable attributes must support [`fio::NodeAttributesQuery::CREATION_TIME`]
113 /// and [`fio::NodeAttributesQuery::MODIFICATION_TIME`].
114pub fn supports_mutable_attrs(&self) -> bool {
115 supports_mutable_attrs(&self.config)
116 }
117}
118119async fn connect_to_harness() -> io_test::TestHarnessProxy {
120// Connect to the realm to get access to the outgoing directory for the harness.
121let (client, server) = zx::Channel::create();
122 fuchsia_component::client::connect_channel_to_protocol::<fcomponent::RealmMarker>(server)
123 .expect("Cannot connect to Realm service");
124let realm = fcomponent::RealmSynchronousProxy::new(client);
125// fs_test is the name of the child component defined in the manifest.
126let child_ref = fdecl::ChildRef { name: "fs_test".to_string(), collection: None };
127let (client, server) = zx::Channel::create();
128 realm
129 .open_exposed_dir(
130&child_ref,
131 fidl::endpoints::ServerEnd::<fio::DirectoryMarker>::new(server),
132 zx::MonotonicInstant::INFINITE,
133 )
134 .expect("FIDL error when binding to child in Realm")
135 .expect("Cannot bind to test harness child in Realm");
136137let exposed_dir = fio::DirectoryProxy::new(fidl::AsyncChannel::from_channel(client));
138139 fuchsia_component::client::connect_to_protocol_at_dir_root::<io_test::TestHarnessMarker>(
140&exposed_dir,
141 )
142 .expect("Cannot connect to test harness protocol")
143}
144145// Returns the aggregate of all rights that are supported for [`io_test::Directory`] objects.
146// Note that rights are specific to a connection (abilities are properties of the node).
147fn get_supported_dir_rights(config: &io_test::HarnessConfig) -> fio::Rights {
148 fio::R_STAR_DIR
149 | fio::W_STAR_DIR
150 | if config.supports_executable_file { fio::X_STAR_DIR } else { fio::Rights::empty() }
151}
152153// Returns the aggregate of all rights that are supported for [`io_test::File`] objects.
154// Note that rights are specific to a connection (abilities are properties of the node).
155fn get_supported_file_rights(config: &io_test::HarnessConfig) -> fio::Rights {
156let mut rights = fio::Rights::READ_BYTES | fio::Rights::GET_ATTRIBUTES;
157if config.supports_mutable_file {
158 rights |= fio::Rights::WRITE_BYTES;
159 }
160if supports_mutable_attrs(&config) {
161 rights |= fio::Rights::WRITE_BYTES;
162 }
163 rights
164}
165166// Returns true if the harness supports at least one mutable attribute, false otherwise.
167//
168// *NOTE*: To allow testing both the io1 SetAttrs and io2 UpdateAttributes methods, harnesses
169// that support mutable attributes must support [`fio::NodeAttributesQuery::CREATION_TIME`]
170// and [`fio::NodeAttributesQuery::MODIFICATION_TIME`].
171fn supports_mutable_attrs(config: &io_test::HarnessConfig) -> bool {
172let all_mutable_attrs: fio::NodeAttributesQuery = fio::NodeAttributesQuery::ACCESS_TIME
173 | fio::NodeAttributesQuery::MODIFICATION_TIME
174 | fio::NodeAttributesQuery::CREATION_TIME
175 | fio::NodeAttributesQuery::MODE
176 | fio::NodeAttributesQuery::GID
177 | fio::NodeAttributesQuery::UID
178 | fio::NodeAttributesQuery::RDEV;
179if config.supported_attributes.intersects(all_mutable_attrs) {
180assert!(
181 config.supported_attributes.contains(
182 fio::NodeAttributesQuery::CREATION_TIME
183 | fio::NodeAttributesQuery::MODIFICATION_TIME
184 ),
185"Harnesses must support at least CREATION_TIME if attributes are mutable."
186);
187return true;
188 }
189false
190}