1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Copyright 2021 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.

use {
    crate::flags::Rights, fidl::endpoints::create_proxy, fidl_fuchsia_component as fcomponent,
    fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_io as fio, fidl_fuchsia_io_test as io_test,
    fuchsia_zircon as zx,
};

/// Helper struct for connecting to an io1 test harness and running a conformance test on it.
pub struct TestHarness {
    /// FIDL proxy to the io1 test harness.
    pub proxy: io_test::Io1HarnessProxy,

    /// Config for the filesystem.
    pub config: io_test::Io1Config,

    /// All [`io_test::Directory`] rights supported by the filesystem.
    pub dir_rights: Rights,

    /// All [`io_test::File`] rights supported by the filesystem.
    pub file_rights: Rights,

    /// All [`io_test::ExecutableFile`] rights supported by the filesystem.
    pub executable_file_rights: Rights,
}

impl TestHarness {
    /// Connects to the test harness and returns a `TestHarness` struct.
    pub async fn new() -> TestHarness {
        let proxy = connect_to_harness().await;
        let config = proxy.get_config().await.expect("Could not get config from proxy");

        // Validate configuration options for consistency, disallow invalid combinations.
        if config.supports_rename.unwrap_or_default() || config.supports_link.unwrap_or_default() {
            assert!(
                config.supports_get_token.unwrap_or_default(),
                "GetToken must be supported for testing Rename/Link!"
            );
        }

        // Generate set of supported open rights for each object type.
        let dir_rights = Rights::new(get_supported_dir_rights(&config));
        let file_rights =
            Rights::new(fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE);
        let executable_file_rights =
            Rights::new(fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE);

        TestHarness { proxy, config, dir_rights, file_rights, executable_file_rights }
    }

    /// Creates a [`fio::DirectoryProxy`] with the given root directory structure.
    pub fn get_directory(
        &self,
        root: io_test::Directory,
        flags: fio::OpenFlags,
    ) -> fio::DirectoryProxy {
        let (client, server) = create_proxy::<fio::DirectoryMarker>().expect("Cannot create proxy");
        self.proxy
            .get_directory(root, flags, server)
            .expect("Cannot get directory from test harness");
        client
    }

    /// Returns the abilities [`io_test::File`] objects should have for the harness.
    pub fn supported_file_abilities(&self) -> fio::Abilities {
        fio::Abilities::READ_BYTES
            | fio::Abilities::WRITE_BYTES
            | fio::Abilities::GET_ATTRIBUTES
            | fio::Abilities::UPDATE_ATTRIBUTES
    }

    /// Returns the abilities [`io_test::Directory`] objects should have for the harness.
    pub fn supported_dir_abilities(&self) -> fio::Abilities {
        fio::Abilities::GET_ATTRIBUTES
            | fio::Abilities::UPDATE_ATTRIBUTES
            | fio::Abilities::ENUMERATE
            | fio::Abilities::TRAVERSE
            | fio::Abilities::MODIFY_DIRECTORY
    }
}

async fn connect_to_harness() -> io_test::Io1HarnessProxy {
    // Connect to the realm to get acccess to the outgoing directory for the harness.
    let (client, server) = zx::Channel::create();
    fuchsia_component::client::connect_channel_to_protocol::<fcomponent::RealmMarker>(server)
        .expect("Cannot connect to Realm service");
    let realm = fcomponent::RealmSynchronousProxy::new(client);
    // fs_test is the name of the child component defined in the manifest.
    let child_ref = fdecl::ChildRef { name: "fs_test".to_string(), collection: None };
    let (client, server) = zx::Channel::create();
    realm
        .open_exposed_dir(
            &child_ref,
            fidl::endpoints::ServerEnd::<fio::DirectoryMarker>::new(server),
            zx::Time::INFINITE,
        )
        .expect("FIDL error when binding to child in Realm")
        .expect("Cannot bind to test harness child in Realm");

    let exposed_dir = fio::DirectoryProxy::new(fidl::AsyncChannel::from_channel(client));

    fuchsia_component::client::connect_to_protocol_at_dir_root::<io_test::Io1HarnessMarker>(
        &exposed_dir,
    )
    .expect("Cannot connect to test harness protocol")
}

/// Returns the aggregate of all io1 rights that are supported for [`io_test::Directory`] objects.
fn get_supported_dir_rights(config: &io_test::Io1Config) -> fio::OpenFlags {
    fio::OpenFlags::RIGHT_READABLE
        | fio::OpenFlags::RIGHT_WRITABLE
        | if config.supports_executable_file.unwrap_or(false) {
            fio::OpenFlags::RIGHT_EXECUTABLE
        } else {
            fio::OpenFlags::empty()
        }
}