blob: 32d6cf9368541c1dc1aebfd7b4fe99a316d69f78 [file] [log] [blame]
// 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 fidl_fuchsia_io as fio;
/// Set of all rights that are valid to use with the conformance test harness.
const ALL_RIGHTS_FLAGS: fio::OpenFlags = fio::OpenFlags::empty()
.union(fio::OpenFlags::RIGHT_READABLE)
.union(fio::OpenFlags::RIGHT_WRITABLE)
.union(fio::OpenFlags::RIGHT_EXECUTABLE);
/// Helper struct that encapsulates generation of valid/invalid sets of flags based on
/// which rights are supported by a particular node type.
pub struct Rights {
rights: fio::OpenFlags,
}
impl Rights {
/// Creates a new Rights struct based on a bitset of OPEN_RIGHT_* flags OR'd together.
pub fn new(rights: fio::OpenFlags) -> Rights {
assert_eq!(rights & !ALL_RIGHTS_FLAGS, fio::OpenFlags::empty());
Rights { rights }
}
/// Returns the bitset of all supported OPEN_RIGHT_* flags.
pub fn all(&self) -> fio::OpenFlags {
self.rights
}
/// Returns a vector of all valid flag combinations.
pub fn valid_combos(&self) -> Vec<fio::OpenFlags> {
build_flag_combinations(0, self.rights.bits())
.into_iter()
.map(fio::OpenFlags::from_bits_truncate)
.collect()
}
/// Returns a vector of all valid flag combinations that include the specified `with_flags`.
/// Will be empty if none of the requested rights are supported.
pub fn valid_combos_with(&self, with_flags: fio::OpenFlags) -> Vec<fio::OpenFlags> {
let mut flag_combos = self.valid_combos();
flag_combos.retain(|&flags| flags.contains(with_flags));
flag_combos
}
/// Returns a vector of all valid flag combinations that exclude the specified `without_flags`.
/// Will be empty if none are supported.
pub fn valid_combos_without(&self, without_flags: fio::OpenFlags) -> Vec<fio::OpenFlags> {
let mut flag_combos = self.valid_combos();
flag_combos.retain(|&flags| !flags.intersects(without_flags));
flag_combos
}
}
/// Returns a list of flag combinations to test. Returns a vector of the aggregate of
/// every constant flag and every combination of variable flags.
/// Ex. build_flag_combinations(100, 011) would return [100, 110, 101, 111]
/// for flags expressed as binary. We exclude the no rights case as that is an
/// invalid argument in most cases. Ex. build_flag_combinations(0, 011)
/// would return [010, 001, 011] without the 000 case.
pub fn build_flag_combinations(constant_flags: u32, variable_flags: u32) -> Vec<u32> {
let mut vec = vec![constant_flags];
for flag in split_flags(variable_flags) {
for i in 0..vec.len() {
vec.push(vec[i] | flag);
}
}
vec
}
/// Splits a bitset into a vector of its component bits. e.g. 1011 becomes [0001, 0010, 1000].
fn split_flags(mut flags: u32) -> Vec<u32> {
let mut bits = vec![];
while flags != 0 {
// x & -x returns the lowest bit set in x. Add it to the vec and then unset that bit.
let lowest_bit = flags & flags.wrapping_neg();
bits.push(lowest_bit);
flags ^= lowest_bit;
}
bits
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_flag_combinations() {
let constant_flags = 0b100;
let variable_flags = 0b011;
let generated_combinations = build_flag_combinations(constant_flags, variable_flags);
let expected_result = [0b100, 0b101, 0b110, 0b111];
assert_eq!(generated_combinations, expected_result);
}
#[test]
fn test_build_flag_combinations_with_empty_rights() {
let constant_flags = 0;
let variable_flags = 0b011;
let generated_combinations = build_flag_combinations(constant_flags, variable_flags);
let expected_result = [0b000, 0b001, 0b010, 0b011];
assert_eq!(generated_combinations, expected_result);
}
#[test]
fn test_split_flags() {
assert_eq!(split_flags(0), vec![]);
assert_eq!(split_flags(0b001), vec![0b001]);
assert_eq!(split_flags(0b101), vec![0b001, 0b100]);
assert_eq!(split_flags(0b111), vec![0b001, 0b010, 0b100]);
}
#[test]
fn test_rights_combos() {
const TEST_RIGHTS: fio::OpenFlags = fio::OpenFlags::empty()
.union(fio::OpenFlags::RIGHT_READABLE)
.union(fio::OpenFlags::RIGHT_WRITABLE)
.union(fio::OpenFlags::RIGHT_EXECUTABLE);
// We should get 0, R, W, X, RW, RX, WX, RWX (8 in total).
const EXPECTED_COMBOS: [fio::OpenFlags; 8] = [
fio::OpenFlags::empty(),
fio::OpenFlags::RIGHT_READABLE,
fio::OpenFlags::RIGHT_WRITABLE,
fio::OpenFlags::RIGHT_EXECUTABLE,
fio::OpenFlags::empty()
.union(fio::OpenFlags::RIGHT_READABLE)
.union(fio::OpenFlags::RIGHT_WRITABLE),
fio::OpenFlags::empty()
.union(fio::OpenFlags::RIGHT_READABLE)
.union(fio::OpenFlags::RIGHT_EXECUTABLE),
fio::OpenFlags::empty()
.union(fio::OpenFlags::RIGHT_WRITABLE)
.union(fio::OpenFlags::RIGHT_EXECUTABLE),
fio::OpenFlags::empty()
.union(fio::OpenFlags::RIGHT_READABLE)
.union(fio::OpenFlags::RIGHT_WRITABLE)
.union(fio::OpenFlags::RIGHT_EXECUTABLE),
];
let rights = Rights::new(TEST_RIGHTS);
assert_eq!(rights.all(), TEST_RIGHTS);
// Test that all possible combinations are generated correctly.
let all_combos = rights.valid_combos();
assert_eq!(all_combos.len(), EXPECTED_COMBOS.len());
for expected_rights in EXPECTED_COMBOS {
assert!(all_combos.contains(&expected_rights));
}
// Test that combinations including READABLE are generated correctly.
// We should get R, RW, RX, and RWX (4 in total).
const EXPECTED_READABLE_COMBOS: [fio::OpenFlags; 4] = [
fio::OpenFlags::RIGHT_READABLE,
fio::OpenFlags::empty()
.union(fio::OpenFlags::RIGHT_READABLE)
.union(fio::OpenFlags::RIGHT_WRITABLE),
fio::OpenFlags::empty()
.union(fio::OpenFlags::RIGHT_READABLE)
.union(fio::OpenFlags::RIGHT_EXECUTABLE),
fio::OpenFlags::empty()
.union(fio::OpenFlags::RIGHT_READABLE)
.union(fio::OpenFlags::RIGHT_WRITABLE)
.union(fio::OpenFlags::RIGHT_EXECUTABLE),
];
let readable_combos = rights.valid_combos_with(fio::OpenFlags::RIGHT_READABLE);
assert_eq!(readable_combos.len(), EXPECTED_READABLE_COMBOS.len());
for expected_rights in EXPECTED_READABLE_COMBOS {
assert!(readable_combos.contains(&expected_rights));
}
// Test that combinations excluding READABLE are generated correctly.
// We should get 0, W, X, and WX (4 in total).
const EXPECTED_NONREADABLE_COMBOS: [fio::OpenFlags; 4] = [
fio::OpenFlags::empty(),
fio::OpenFlags::RIGHT_WRITABLE,
fio::OpenFlags::RIGHT_EXECUTABLE,
fio::OpenFlags::empty()
.union(fio::OpenFlags::RIGHT_WRITABLE)
.union(fio::OpenFlags::RIGHT_EXECUTABLE),
];
let nonreadable_combos = rights.valid_combos_without(fio::OpenFlags::RIGHT_READABLE);
assert_eq!(nonreadable_combos.len(), EXPECTED_NONREADABLE_COMBOS.len());
for expected_rights in EXPECTED_NONREADABLE_COMBOS {
assert!(nonreadable_combos.contains(&expected_rights));
}
}
#[test]
#[should_panic]
fn test_rights_unsupported() {
// Passing anything other than OPEN_RIGHT_* should fail.
Rights::new(fio::OpenFlags::CREATE);
}
}