use core::ffi::c_int;
use core::fmt;
use core::num::NonZeroU32;
use bela_sys::BelaInitSettings;
use crate::application::BelaApplication;
use crate::cpu;
use crate::error::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Settings {
period_size: Option<u32>,
use_analog: Option<bool>,
use_digital: Option<bool>,
num_analog_in_channels: Option<u32>,
num_analog_out_channels: Option<u32>,
num_digital_channels: Option<u32>,
detect_underruns: Option<bool>,
verbose: Option<bool>,
high_performance_mode: Option<bool>,
uniform_sample_rate: Option<bool>,
stop_button_pin: Option<StopButtonPin>,
thread_count: Option<NonZeroU32>,
cpu_monitoring: Option<NonZeroU32>,
begin_muted: Option<bool>,
}
impl Default for Settings {
fn default() -> Self {
Self::new()
}
}
impl Settings {
#[must_use]
pub const fn new() -> Self {
Self {
period_size: None,
use_analog: None,
use_digital: None,
num_analog_in_channels: None,
num_analog_out_channels: None,
num_digital_channels: None,
detect_underruns: None,
verbose: None,
high_performance_mode: None,
uniform_sample_rate: None,
stop_button_pin: None,
thread_count: None,
cpu_monitoring: None,
begin_muted: None,
}
}
#[must_use]
pub const fn period_size(mut self, frames: u32) -> Self {
self.period_size = Some(frames);
self
}
#[must_use]
pub const fn use_analog(mut self, enabled: bool) -> Self {
self.use_analog = Some(enabled);
self
}
#[must_use]
pub const fn use_digital(mut self, enabled: bool) -> Self {
self.use_digital = Some(enabled);
self
}
#[must_use]
pub const fn num_analog_in_channels(mut self, channels: u32) -> Self {
self.num_analog_in_channels = Some(channels);
self
}
#[must_use]
pub const fn num_analog_out_channels(mut self, channels: u32) -> Self {
self.num_analog_out_channels = Some(channels);
self
}
#[must_use]
pub const fn num_digital_channels(mut self, channels: u32) -> Self {
self.num_digital_channels = Some(channels);
self
}
#[must_use]
pub const fn detect_underruns(mut self, enabled: bool) -> Self {
self.detect_underruns = Some(enabled);
self
}
#[must_use]
pub const fn verbose(mut self, enabled: bool) -> Self {
self.verbose = Some(enabled);
self
}
#[must_use]
pub const fn high_performance_mode(mut self, enabled: bool) -> Self {
self.high_performance_mode = Some(enabled);
self
}
#[must_use]
pub const fn uniform_sample_rate(mut self, enabled: bool) -> Self {
self.uniform_sample_rate = Some(enabled);
self
}
#[must_use]
pub const fn stop_button_pin(mut self, pin: Option<u32>) -> Self {
self.stop_button_pin = Some(match pin {
Some(pin) => StopButtonPin::Gpio(pin),
None => StopButtonPin::Disabled,
});
self
}
#[must_use]
pub const fn thread_count(mut self, threads: NonZeroU32) -> Self {
self.thread_count = Some(threads);
self
}
#[must_use]
pub const fn cpu_monitoring(mut self, measurements_per_cycle: NonZeroU32) -> Self {
self.cpu_monitoring = Some(measurements_per_cycle);
self
}
#[must_use]
pub const fn begin_muted(mut self, muted: bool) -> Self {
self.begin_muted = Some(muted);
self
}
#[cfg_attr(
not(bela_device),
allow(
dead_code,
reason = "only the device-gated audio system applies it; still unit-tested on the host"
)
)]
pub(crate) const fn cpu_monitoring_cycle(&self) -> Option<NonZeroU32> {
self.cpu_monitoring
}
pub fn apply_to(&self, raw: &mut BelaInitSettings) {
if let Some(v) = self.period_size {
raw.periodSize = to_c_int(v);
}
if let Some(v) = self.use_analog {
raw.useAnalog = c_int::from(v);
}
if let Some(v) = self.use_digital {
raw.useDigital = c_int::from(v);
}
if let Some(v) = self.num_analog_in_channels {
raw.numAnalogInChannels = to_c_int(v);
}
if let Some(v) = self.num_analog_out_channels {
raw.numAnalogOutChannels = to_c_int(v);
}
if let Some(v) = self.num_digital_channels {
raw.numDigitalChannels = to_c_int(v);
}
if let Some(v) = self.detect_underruns {
raw.detectUnderruns = c_int::from(v);
}
if let Some(v) = self.verbose {
raw.verbose = c_int::from(v);
}
if let Some(v) = self.high_performance_mode {
raw.highPerformanceMode = c_int::from(v);
}
if let Some(v) = self.uniform_sample_rate {
raw.uniformSampleRate = c_int::from(v);
}
if let Some(stop_button_pin) = self.stop_button_pin {
raw.stopButtonPin = match stop_button_pin {
StopButtonPin::Gpio(pin) => to_c_int(pin),
StopButtonPin::Disabled => -1,
};
}
if let Some(v) = self.thread_count {
raw.threadCount = v.get();
}
if let Some(v) = self.begin_muted {
raw.beginMuted = c_int::from(v);
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum StopButtonPin {
Gpio(u32),
Disabled,
}
fn to_c_int(value: u32) -> c_int {
c_int::try_from(value).unwrap_or(c_int::MAX)
}
pub struct ResolvedSettings<'a> {
raw: &'a BelaInitSettings,
cpu_monitoring: Option<NonZeroU32>,
}
impl<'a> ResolvedSettings<'a> {
#[cfg_attr(
not(bela_device),
allow(
dead_code,
reason = "only the device-gated audio system resolves settings; still unit-tested on \
the host"
)
)]
pub(crate) const fn new(raw: &'a BelaInitSettings, cpu_monitoring: Option<NonZeroU32>) -> Self {
Self {
raw,
cpu_monitoring,
}
}
#[must_use]
#[inline]
pub const fn as_sys(&self) -> &BelaInitSettings {
self.raw
}
#[must_use]
#[inline]
pub const fn period_size(&self) -> i32 {
self.raw.periodSize
}
#[must_use]
#[inline]
pub const fn audio_sample_rate(&self) -> f32 {
self.raw.audioSampleRate
}
#[must_use]
#[inline]
pub const fn use_analog(&self) -> bool {
self.raw.useAnalog != 0
}
#[must_use]
#[inline]
pub const fn num_analog_in_channels(&self) -> i32 {
self.raw.numAnalogInChannels
}
#[must_use]
#[inline]
pub const fn num_analog_out_channels(&self) -> i32 {
self.raw.numAnalogOutChannels
}
#[must_use]
#[inline]
pub const fn use_digital(&self) -> bool {
self.raw.useDigital != 0
}
#[must_use]
#[inline]
pub const fn num_digital_channels(&self) -> i32 {
self.raw.numDigitalChannels
}
#[must_use]
#[inline]
pub const fn thread_count(&self) -> usize {
render_threads(self.raw)
}
#[must_use]
#[inline]
pub const fn uniform_sample_rate(&self) -> bool {
self.raw.uniformSampleRate != 0
}
#[must_use]
#[inline]
pub const fn high_performance_mode(&self) -> bool {
self.raw.highPerformanceMode != 0
}
#[must_use]
#[inline]
pub const fn detect_underruns(&self) -> bool {
self.raw.detectUnderruns != 0
}
#[must_use]
#[inline]
pub const fn verbose(&self) -> bool {
self.raw.verbose != 0
}
#[must_use]
#[inline]
pub const fn begin_muted(&self) -> bool {
self.raw.beginMuted != 0
}
#[must_use]
#[inline]
pub const fn cpu_monitoring(&self) -> Option<NonZeroU32> {
self.cpu_monitoring
}
#[must_use]
#[inline]
pub const fn stop_button_pin(&self) -> Option<u32> {
let pin = self.raw.stopButtonPin;
if pin < 0 {
None
} else {
#[allow(
clippy::cast_sign_loss,
reason = "the branch above is what rules the negative values out"
)]
Some(pin as u32)
}
}
}
impl fmt::Debug for ResolvedSettings<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ResolvedSettings")
.field("period_size", &self.period_size())
.field("audio_sample_rate", &self.audio_sample_rate())
.field("use_analog", &self.use_analog())
.field("num_analog_in_channels", &self.num_analog_in_channels())
.field("num_analog_out_channels", &self.num_analog_out_channels())
.field("use_digital", &self.use_digital())
.field("num_digital_channels", &self.num_digital_channels())
.field("thread_count", &self.thread_count())
.field("uniform_sample_rate", &self.uniform_sample_rate())
.field("high_performance_mode", &self.high_performance_mode())
.field("detect_underruns", &self.detect_underruns())
.field("verbose", &self.verbose())
.field("begin_muted", &self.begin_muted())
.field("cpu_monitoring", &self.cpu_monitoring())
.field("stop_button_pin", &self.stop_button_pin())
.finish_non_exhaustive()
}
}
const MULTIPLEXER_ANALOG_CHANNELS: c_int = 8;
#[cfg_attr(
not(bela_device),
allow(
dead_code,
reason = "only the device-gated audio system initialises; still unit-tested on the host"
)
)]
pub(crate) const fn check_resolved(raw: &BelaInitSettings) -> Result<(), Error> {
if raw.audioSampleRate == 0.0 {
return Err(Error::SampleRate);
}
if raw.pruNumber < 0 || raw.pruNumber > 1 {
return Err(Error::PruNumber(raw.pruNumber));
}
if raw.numMuxChannels == 0 {
return Ok(());
}
if !matches!(raw.numMuxChannels, 2 | 4 | 8) {
return Err(Error::MultiplexerChannels(raw.numMuxChannels));
}
if raw.pruNumber != 1 {
return Err(Error::MultiplexerPru(raw.pruNumber));
}
if raw.useAnalog == 0 {
return Err(Error::MultiplexerWithoutAnalog);
}
if raw.numAnalogInChannels != MULTIPLEXER_ANALOG_CHANNELS {
return Err(Error::MultiplexerAnalogChannels(raw.numAnalogInChannels));
}
Ok(())
}
#[cfg_attr(
not(bela_device),
allow(
dead_code,
reason = "only the device-gated audio system initialises; still unit-tested on the host"
)
)]
pub(crate) fn check_supported<T: BelaApplication>(
raw: &BelaInitSettings,
cpu_monitoring: Option<NonZeroU32>,
application: &T,
) -> Result<(), Error> {
check_resolved(raw)?;
if cpu_monitoring.is_some() {
let period_size = u32::try_from(raw.periodSize).unwrap_or(u32::MAX);
cpu::check_period_size(period_size)?;
}
application
.validate_settings(&ResolvedSettings::new(raw, cpu_monitoring))
.map_err(Error::SettingsRefused)
}
#[cfg_attr(
not(bela_device),
allow(
dead_code,
reason = "only the device-gated audio system applies settings; still unit-tested on the host"
)
)]
pub(crate) const fn render_threads(raw: &BelaInitSettings) -> usize {
let count = raw.threadCount as usize;
if count == 0 { 1 } else { count }
}
#[cfg(test)]
#[allow(
clippy::float_cmp,
reason = "the sample rate is copied verbatim, so the expected value is exact"
)]
mod tests {
use core::mem;
use super::*;
use crate::application::ThreadInfo;
use crate::context::{RenderContext, SetupContext};
const FOUR_THREADS: NonZeroU32 = NonZeroU32::new(4).expect("the test thread count is non-zero");
const NEEDS_EIGHT: &str = "this application reads eight analog inputs";
const NEEDS_ONE_THREAD: &str = "this application renders on one thread";
const NEEDS_MONITORING: &str = "this application reports the CPU usage of every block";
fn cycle_of(measurements: u32) -> NonZeroU32 {
NonZeroU32::new(measurements).expect("the test cycles are non-zero")
}
struct Anything;
impl BelaApplication for Anything {
type RenderState = ();
fn create_render_state(&mut self, _thread: ThreadInfo, _context: &SetupContext) {}
fn render(&self, _state: &mut (), _context: &mut RenderContext) {}
}
struct NeedsEightAnalogInputs;
impl BelaApplication for NeedsEightAnalogInputs {
type RenderState = ();
fn validate_settings(&self, settings: &ResolvedSettings<'_>) -> Result<(), &'static str> {
if settings.num_analog_in_channels() < 8 {
return Err(NEEDS_EIGHT);
}
Ok(())
}
fn create_render_state(&mut self, _thread: ThreadInfo, _context: &SetupContext) {}
fn render(&self, _state: &mut (), _context: &mut RenderContext) {}
}
struct NeedsMonitoring;
impl BelaApplication for NeedsMonitoring {
type RenderState = ();
fn validate_settings(&self, settings: &ResolvedSettings<'_>) -> Result<(), &'static str> {
if settings.cpu_monitoring().is_some() {
Ok(())
} else {
Err(NEEDS_MONITORING)
}
}
fn create_render_state(&mut self, _thread: ThreadInfo, _context: &SetupContext) {}
fn render(&self, _state: &mut (), _context: &mut RenderContext) {}
}
struct SingleThreaded;
impl BelaApplication for SingleThreaded {
type RenderState = ();
fn validate_settings(&self, settings: &ResolvedSettings<'_>) -> Result<(), &'static str> {
if settings.thread_count() == 1 {
Ok(())
} else {
Err(NEEDS_ONE_THREAD)
}
}
fn create_render_state(&mut self, _thread: ThreadInfo, _context: &SetupContext) {}
fn render(&self, _state: &mut (), _context: &mut RenderContext) {}
}
fn fake_defaults() -> BelaInitSettings {
let mut raw: BelaInitSettings = unsafe { mem::zeroed() };
raw.audioSampleRate = 44100.0;
raw.periodSize = 16;
raw.useAnalog = 1;
raw.numAnalogInChannels = 8;
raw.numAnalogOutChannels = 8;
raw.numMuxChannels = 0;
raw.pruNumber = 1;
raw.uniformSampleRate = 1;
raw.stopButtonPin = 115;
raw.verbose = 0;
raw
}
fn with_multiplexer(channels: c_int) -> BelaInitSettings {
let mut raw = fake_defaults();
raw.numMuxChannels = channels;
raw
}
#[test]
fn a_whole_configuration_can_be_a_const() {
const SETTINGS: Settings = Settings::new().period_size(64).thread_count(FOUR_THREADS);
let mut raw = fake_defaults();
SETTINGS.apply_to(&mut raw);
assert_eq!(raw.periodSize, 64);
assert_eq!(raw.threadCount, 4);
}
#[test]
fn default_is_the_same_empty_set_of_overrides_as_new() {
assert_eq!(Settings::default(), Settings::new());
}
#[test]
fn empty_settings_leave_defaults_untouched() {
let mut raw = fake_defaults();
Settings::new().apply_to(&mut raw);
assert_eq!(raw.periodSize, 16);
assert_eq!(raw.useAnalog, 1);
assert_eq!(raw.uniformSampleRate, 1);
assert_eq!(raw.stopButtonPin, 115);
}
#[test]
fn set_fields_override_defaults() {
let mut raw = fake_defaults();
Settings::new()
.period_size(64)
.use_analog(false)
.verbose(true)
.stop_button_pin(None)
.thread_count(FOUR_THREADS)
.apply_to(&mut raw);
assert_eq!(raw.periodSize, 64);
assert_eq!(raw.useAnalog, 0);
assert_eq!(raw.verbose, 1);
assert_eq!(raw.stopButtonPin, -1);
assert_eq!(raw.threadCount, 4);
assert_eq!(raw.uniformSampleRate, 1);
}
#[test]
fn a_stop_button_pin_is_an_unsigned_gpio_number() {
let mut raw = fake_defaults();
Settings::new().stop_button_pin(Some(27)).apply_to(&mut raw);
assert_eq!(raw.stopButtonPin, 27);
Settings::new()
.stop_button_pin(Some(u32::MAX))
.apply_to(&mut raw);
assert_eq!(raw.stopButtonPin, c_int::MAX);
}
#[test]
fn bools_map_to_c_ints() {
let mut raw = fake_defaults();
Settings::new()
.use_digital(true)
.detect_underruns(false)
.high_performance_mode(true)
.uniform_sample_rate(false)
.begin_muted(true)
.apply_to(&mut raw);
assert_eq!(raw.useDigital, 1);
assert_eq!(raw.detectUnderruns, 0);
assert_eq!(raw.highPerformanceMode, 1);
assert_eq!(raw.uniformSampleRate, 0);
assert_eq!(raw.beginMuted, 1);
}
#[test]
fn coming_up_unmuted_is_still_said_out_loud() {
let mut raw = fake_defaults();
raw.beginMuted = 1;
Settings::new().begin_muted(false).apply_to(&mut raw);
assert_eq!(raw.beginMuted, 0);
}
#[test]
fn cpu_monitoring_is_recorded_but_not_an_init_setting() {
let cycle = NonZeroU32::new(2000).expect("2000 is not zero");
let settings = Settings::new().cpu_monitoring(cycle);
assert_eq!(settings.cpu_monitoring_cycle(), Some(cycle));
assert_eq!(
Settings::new().cpu_monitoring_cycle(),
None,
"monitoring should be off unless it was asked for"
);
let mut raw = fake_defaults();
let untouched = fake_defaults();
settings.apply_to(&mut raw);
assert_eq!(raw.periodSize, untouched.periodSize);
assert_eq!(raw.useAnalog, untouched.useAnalog);
assert_eq!(raw.uniformSampleRate, untouched.uniformSampleRate);
assert_eq!(raw.stopButtonPin, untouched.stopButtonPin);
}
#[test]
fn both_spellings_of_one_render_thread_count_as_one() {
for spelling in [0, 1] {
let mut raw: BelaInitSettings = unsafe { mem::zeroed() };
raw.threadCount = spelling;
assert_eq!(render_threads(&raw), 1, "threadCount {spelling}");
}
}
#[test]
fn extra_render_threads_are_counted_as_asked_for() {
let mut raw: BelaInitSettings = unsafe { mem::zeroed() };
raw.threadCount = 4;
assert_eq!(render_threads(&raw), 4);
}
#[test]
fn belas_own_defaults_are_accepted() {
assert_eq!(check_resolved(&fake_defaults()), Ok(()));
}
#[test]
fn a_sample_rate_of_zero_is_refused() {
let mut raw = fake_defaults();
raw.audioSampleRate = 0.0;
assert_eq!(check_resolved(&raw), Err(Error::SampleRate));
}
#[test]
fn both_prus_are_accepted() {
for number in [0, 1] {
let mut raw = fake_defaults();
raw.pruNumber = number;
assert_eq!(check_resolved(&raw), Ok(()), "PRU {number}");
}
}
#[test]
fn a_pru_the_board_does_not_have_is_refused() {
for number in [-1, 2, 5] {
let mut raw = fake_defaults();
raw.pruNumber = number;
assert_eq!(
check_resolved(&raw),
Err(Error::PruNumber(number)),
"PRU {number}"
);
}
}
#[test]
fn the_multiplexer_channel_counts_libbela_takes_are_accepted() {
for channels in [0, 2, 4, 8] {
assert_eq!(
check_resolved(&with_multiplexer(channels)),
Ok(()),
"{channels} multiplexer channels"
);
}
}
#[test]
fn any_other_multiplexer_channel_count_is_refused() {
for channels in [-1, 1, 3, 16] {
assert_eq!(
check_resolved(&with_multiplexer(channels)),
Err(Error::MultiplexerChannels(channels)),
"{channels} multiplexer channels"
);
}
}
#[test]
fn the_multiplexer_is_refused_on_the_wrong_pru() {
let mut raw = with_multiplexer(8);
raw.pruNumber = 0;
assert_eq!(check_resolved(&raw), Err(Error::MultiplexerPru(0)));
}
#[test]
fn the_multiplexer_is_refused_without_the_analog_inputs() {
let mut raw = with_multiplexer(8);
raw.useAnalog = 0;
assert_eq!(check_resolved(&raw), Err(Error::MultiplexerWithoutAnalog));
}
#[test]
fn the_multiplexer_is_refused_with_any_other_number_of_analog_inputs() {
for channels in [2, 4, 16] {
let mut raw = with_multiplexer(8);
raw.numAnalogInChannels = channels;
assert_eq!(
check_resolved(&raw),
Err(Error::MultiplexerAnalogChannels(channels)),
"{channels} analog input channels"
);
}
}
#[test]
fn the_multiplexer_rules_only_apply_when_it_is_on() {
let mut raw = fake_defaults();
raw.pruNumber = 0;
raw.useAnalog = 0;
raw.numAnalogInChannels = 2;
assert_eq!(check_resolved(&raw), Ok(()));
}
#[test]
fn the_view_reports_the_settings_as_they_resolved() {
let mut raw = fake_defaults();
Settings::new()
.period_size(64)
.num_analog_in_channels(4)
.use_digital(false)
.verbose(true)
.begin_muted(true)
.stop_button_pin(Some(27))
.apply_to(&mut raw);
raw.audioSampleRate = 48000.0;
raw.threadCount = 4;
let settings = ResolvedSettings::new(&raw, None);
assert_eq!(settings.period_size(), 64);
assert_eq!(settings.audio_sample_rate(), 48000.0);
assert!(settings.use_analog());
assert_eq!(settings.num_analog_in_channels(), 4);
assert_eq!(settings.num_analog_out_channels(), 8);
assert!(!settings.use_digital());
assert_eq!(settings.thread_count(), 4);
assert!(settings.uniform_sample_rate());
assert!(!settings.high_performance_mode());
assert!(settings.verbose());
assert!(settings.begin_muted());
assert_eq!(settings.stop_button_pin(), Some(27));
assert_eq!(settings.as_sys().numMuxChannels, 0);
}
#[test]
fn the_view_spells_one_render_thread_and_no_stop_button_the_way_the_rest_of_the_crate_does() {
let mut raw = fake_defaults();
raw.threadCount = 0;
raw.stopButtonPin = -1;
let settings = ResolvedSettings::new(&raw, None);
assert_eq!(settings.thread_count(), 1);
assert_eq!(settings.stop_button_pin(), None);
}
#[test]
fn an_application_that_says_nothing_accepts_every_configuration() {
assert_eq!(
check_supported(&fake_defaults(), None, &Anything),
Ok(()),
"the default validate_settings should accept Bela's own defaults"
);
}
#[test]
fn an_application_can_refuse_the_resolved_settings() {
let mut raw = fake_defaults();
Settings::new().num_analog_in_channels(4).apply_to(&mut raw);
assert_eq!(
check_supported(&raw, None, &NeedsEightAnalogInputs),
Err(Error::SettingsRefused(NEEDS_EIGHT))
);
assert_eq!(
check_supported(&fake_defaults(), None, &NeedsEightAnalogInputs),
Ok(())
);
}
#[test]
fn an_application_can_refuse_a_resolved_thread_count() {
let mut raw = fake_defaults();
raw.threadCount = 4;
assert_eq!(
check_supported(&raw, None, &SingleThreaded),
Err(Error::SettingsRefused(NEEDS_ONE_THREAD))
);
for spelling in [0, 1] {
raw.threadCount = spelling;
assert_eq!(
check_supported(&raw, None, &SingleThreaded),
Ok(()),
"threadCount {spelling}"
);
}
}
#[test]
fn an_application_sees_the_cpu_monitoring_that_is_not_in_the_c_settings() {
let raw = fake_defaults();
let cycle = cycle_of(2000);
assert_eq!(check_supported(&raw, Some(cycle), &NeedsMonitoring), Ok(()));
assert_eq!(
check_supported(&raw, None, &NeedsMonitoring),
Err(Error::SettingsRefused(NEEDS_MONITORING))
);
assert_eq!(
ResolvedSettings::new(&raw, Some(cycle)).cpu_monitoring(),
Some(cycle)
);
assert_eq!(ResolvedSettings::new(&raw, None).cpu_monitoring(), None);
}
#[test]
fn the_crates_own_checks_are_made_before_the_applications() {
let mut raw = fake_defaults();
raw.audioSampleRate = 0.0;
raw.numAnalogInChannels = 4;
assert_eq!(
check_supported(&raw, None, &NeedsEightAnalogInputs),
Err(Error::SampleRate)
);
}
#[test]
fn the_cpu_monitoring_period_size_is_checked_before_the_application_is_asked() {
let mut raw = fake_defaults();
raw.periodSize = c_int::try_from(crate::MAX_MONITORED_PERIOD_SIZE)
.expect("the monitored period size limit fits in a C int")
+ 1;
raw.numAnalogInChannels = 4;
let period_size = u32::try_from(raw.periodSize).expect("the period size above is positive");
assert_eq!(
check_supported(&raw, Some(cycle_of(2000)), &NeedsEightAnalogInputs),
Err(Error::CpuMonitoringPeriodSize(period_size))
);
assert_eq!(
check_supported(&raw, None, &NeedsEightAnalogInputs),
Err(Error::SettingsRefused(NEEDS_EIGHT))
);
}
#[test]
fn a_settings_and_a_command_line_half_make_the_same_refusal() {
let mut from_settings = fake_defaults();
Settings::new()
.num_analog_in_channels(4)
.apply_to(&mut from_settings);
from_settings.numMuxChannels = 8;
assert_eq!(
check_resolved(&from_settings),
Err(Error::MultiplexerAnalogChannels(4))
);
}
}