Skip to main content

io_conformance_util/
flags.rs

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.
4
5use fidl_fuchsia_io as fio;
6
7/// Helper struct that encapsulates generation of valid/invalid sets of flags based on
8/// which rights are supported by a particular node type.
9pub struct Rights {
10    rights: fio::Rights,
11}
12
13impl Rights {
14    /// Creates a new Rights struct based on the rights in specified in `fio::Rights`.
15    pub fn new(rights: fio::Rights) -> Rights {
16        Rights { rights }
17    }
18
19    /// Returns all supported rights.
20    pub fn all_rights(&self) -> fio::Rights {
21        self.rights
22    }
23
24    /// Returns all supported rights as `fio::Flags` right flags.
25    pub fn all_flags(&self) -> fio::Flags {
26        // We can do this conversion because Flags has a 1:1 to Rights.
27        fio::Flags::from_bits_truncate(self.rights.bits())
28    }
29
30    /// Returns a vector of all valid rights combinations.
31    pub fn rights_combinations(&self) -> Vec<fio::Rights> {
32        build_flag_combinations(fio::Rights::empty(), self.rights)
33    }
34
35    /// Returns a vector of all valid flag combinations as `fio::Flags` flags.
36    pub fn combinations(&self) -> Vec<fio::Flags> {
37        build_flag_combinations(fio::Flags::empty(), self.all_flags())
38    }
39
40    // Returns all rights combinations that contains all the rights specified in `with_rights`.
41    fn rights_combinations_containing(&self, with_rights: fio::Rights) -> Vec<fio::Rights> {
42        let mut right_combinations = self.rights_combinations();
43        right_combinations.retain(|&flags| flags.contains(with_rights));
44        right_combinations
45    }
46
47    /// Returns all rights combinations that include *all* the specified rights in `with_rights` as
48    /// `fio::Flags` flags. Will be empty if none of the requested rights are supported.
49    pub fn combinations_containing(&self, with_rights: fio::Rights) -> Vec<fio::Flags> {
50        self.rights_combinations_containing(with_rights)
51            .into_iter()
52            .map(|combination| fio::Flags::from_bits_truncate(combination.bits()))
53            .collect()
54    }
55
56    /// Returns all rights combinations without the rights specified in `without_rights`,
57    fn combinations_without_as_rights(&self, without_rights: fio::Rights) -> Vec<fio::Rights> {
58        let mut right_combinations = self.rights_combinations();
59        right_combinations.retain(|&flags| !flags.intersects(without_rights));
60        right_combinations
61    }
62
63    /// Returns all rights combinations that does not include the specified rights in
64    /// `without_rights` as `fio::Flags` flags. Will be empty if none are supported.
65    pub fn combinations_without(&self, without_rights: fio::Rights) -> Vec<fio::Flags> {
66        self.combinations_without_as_rights(without_rights)
67            .into_iter()
68            .map(|combination| fio::Flags::from_bits_truncate(combination.bits()))
69            .collect()
70    }
71}
72
73/// Returns a list of flag combinations to test. Returns a vector of the aggregate of
74/// every constant flag and every combination of variable flags. For example, calling
75/// build_flag_combinations(100, 011) would return [100, 110, 101, 111] (in binary),
76/// whereas build_flag_combinations(0, 011) would return [000, 001, 010, 011].
77pub fn build_flag_combinations<T: bitflags::Flags + Copy>(
78    constant_flags: T,
79    variable_flags: T,
80) -> Vec<T> {
81    let mut vec = vec![constant_flags];
82    for flag in variable_flags.iter() {
83        for i in 0..vec.len() {
84            vec.push(vec[i].union(flag));
85        }
86    }
87    vec
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_rights_combinations_flags() {
96        const TEST_RIGHTS: fio::Rights = fio::Rights::empty()
97            .union(fio::Rights::READ_BYTES)
98            .union(fio::Rights::WRITE_BYTES)
99            .union(fio::Rights::EXECUTE);
100
101        // We should get 0, R, W, X, RW, RX, WX, RWX (8 in total).
102        const EXPECTED_COMBINATIONS: [fio::Flags; 8] = [
103            fio::Flags::empty(),
104            fio::Flags::PERM_READ_BYTES,
105            fio::Flags::PERM_WRITE_BYTES,
106            fio::Flags::PERM_EXECUTE,
107            fio::Flags::empty()
108                .union(fio::Flags::PERM_READ_BYTES)
109                .union(fio::Flags::PERM_WRITE_BYTES),
110            fio::Flags::empty().union(fio::Flags::PERM_READ_BYTES).union(fio::Flags::PERM_EXECUTE),
111            fio::Flags::empty().union(fio::Flags::PERM_WRITE_BYTES).union(fio::Flags::PERM_EXECUTE),
112            fio::Flags::empty()
113                .union(fio::Flags::PERM_READ_BYTES)
114                .union(fio::Flags::PERM_WRITE_BYTES)
115                .union(fio::Flags::PERM_EXECUTE),
116        ];
117
118        // Check that we get the expected Flags.
119        let rights = Rights::new(TEST_RIGHTS);
120        assert_eq!(
121            rights.all_flags(),
122            fio::Flags::PERM_READ_BYTES | fio::Flags::PERM_WRITE_BYTES | fio::Flags::PERM_EXECUTE
123        );
124
125        // Test that all possible combinations are generated correctly.
126        let all_combinations = rights.combinations();
127        assert_eq!(all_combinations.len(), EXPECTED_COMBINATIONS.len());
128        for expected_rights in EXPECTED_COMBINATIONS {
129            assert!(all_combinations.contains(&expected_rights));
130        }
131
132        // Test that combinations including READABLE are generated correctly.
133        // We should get R, RW, RX, and RWX (4 in total).
134        const EXPECTED_READABLE_COMBOS: [fio::Flags; 4] = [
135            fio::Flags::PERM_READ_BYTES,
136            fio::Flags::empty()
137                .union(fio::Flags::PERM_READ_BYTES)
138                .union(fio::Flags::PERM_WRITE_BYTES),
139            fio::Flags::empty().union(fio::Flags::PERM_READ_BYTES).union(fio::Flags::PERM_EXECUTE),
140            fio::Flags::empty()
141                .union(fio::Flags::PERM_READ_BYTES)
142                .union(fio::Flags::PERM_WRITE_BYTES)
143                .union(fio::Flags::PERM_EXECUTE),
144        ];
145        let readable_combos = rights.combinations_containing(fio::Rights::READ_BYTES);
146        assert_eq!(readable_combos.len(), EXPECTED_READABLE_COMBOS.len());
147        for expected_rights in EXPECTED_READABLE_COMBOS {
148            assert!(readable_combos.contains(&expected_rights));
149        }
150
151        // Test that combinations excluding READABLE are generated correctly.
152        // We should get 0, W, X, and WX (4 in total).
153        const EXPECTED_NONREADABLE_COMBOS: [fio::Flags; 4] = [
154            fio::Flags::empty(),
155            fio::Flags::PERM_WRITE_BYTES,
156            fio::Flags::PERM_EXECUTE,
157            fio::Flags::empty().union(fio::Flags::PERM_WRITE_BYTES).union(fio::Flags::PERM_EXECUTE),
158        ];
159        let nonreadable_combos = rights.combinations_without(fio::Rights::READ_BYTES);
160        assert_eq!(nonreadable_combos.len(), EXPECTED_NONREADABLE_COMBOS.len());
161        for expected_rights in EXPECTED_NONREADABLE_COMBOS {
162            assert!(nonreadable_combos.contains(&expected_rights));
163        }
164    }
165}