blob: 01e84228509efa10d6d721bd1a5d0faba82e0dd5 [file] [log] [blame]
// Copyright 2020 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 anyhow::{Context, Error};
use argh::FromArgs;
use serde::de::DeserializeOwned;
use settings::{
AgentConfiguration, DisplayConfiguration, EnabledInterfacesConfiguration, InputConfiguration,
LightHardwareConfiguration, ServiceFlags,
};
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::hash::{Hash, Hasher};
use std::io::Read;
/// setui_config_tests validates configuration files passed to the settings service.
#[derive(FromArgs)]
struct TestConfig {
/// these configurations are the one that will determine the initial
/// display settings.
#[argh(option, short = 'd')]
display_config: Vec<OsString>,
/// these configurations are the one that will determine the behavior of individual controllers.
#[argh(option, short = 'f')]
controller_flags: Vec<OsString>,
/// these configurations control the default input devices for a product.
#[argh(option, short = 'i')]
input_device_config: Vec<OsString>,
/// these configurations control which interfaces are enabled.
#[argh(option, short = 'x')]
interface_config: Vec<OsString>,
/// these configurations control specific settings for light hardware.
#[argh(option, short = 'h')]
light_hardware_config: Vec<OsString>,
/// these configurations control which agents are enabled.
#[argh(option, short = 'a')]
agent_config: Vec<OsString>,
/// these configurations add audio policy transforms at build-time.
#[argh(option)]
audio_policy_config: Vec<OsString>,
}
fn read_config<C: DeserializeOwned>(path: &OsStr) -> Result<C, Error> {
tracing::info!("Validating {:?}", path);
let mut file = File::open(path)
.with_context(|| format!("Couldn't open path `{}`", path.to_string_lossy()))?;
let mut contents = String::new();
let _: usize = file
.read_to_string(&mut contents)
.with_context(|| format!("Couldn't read file at path `{}`", path.to_string_lossy()))?;
Ok(serde_json::from_str::<C>(&contents).context("Failed to deserialize flag configuration")?)
}
fn main() -> Result<(), Error> {
let test_config: TestConfig = argh::from_env();
for config in test_config.display_config.into_iter() {
let _ = read_config::<DisplayConfiguration>(&config)?;
}
for config in test_config.controller_flags.into_iter() {
let _ = read_config::<ServiceFlags>(&config)?;
}
for config in test_config.input_device_config.into_iter() {
let _ = read_config::<InputConfiguration>(&config)?;
}
for config in test_config.interface_config.into_iter() {
let _ = read_config::<EnabledInterfacesConfiguration>(&config)?;
}
for config in test_config.light_hardware_config.into_iter() {
let light_config: LightHardwareConfiguration =
read_config::<LightHardwareConfiguration>(&config)?;
// Verify that no two light group names hash to the same value. This guarantee is important
// for the light service to function properly. This also implicitly prevents two light
// groups from having the same name.
let mut hash_to_name: HashMap<u64, String> = HashMap::new();
for light_group in light_config.light_groups {
// Hash the name.
let mut hasher = DefaultHasher::new();
light_group.name.hash(&mut hasher);
let hash = hasher.finish();
// Insert returns the previous value at the given key, if any. If there was a value with
// the same hash, panic.
if let Some(conflicting_name) = hash_to_name.insert(hash, light_group.name.clone()) {
panic!("{:?} has the same hash as {:?}", light_group.name, conflicting_name);
}
}
}
for config in test_config.agent_config.into_iter() {
let _ = read_config::<AgentConfiguration>(&config)?;
}
for _config in test_config.audio_policy_config.into_iter() {
// Audio policy support has been deprecated.
}
Ok(())
}