<commit_before>//
// Copyright 2022 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! This module contains an implementation of the guest-hypervisor communications block (GHCB) page
//! that can be used for communicating with the hypervisor.

use bitflags::bitflags;
use zerocopy::FromBytes;

/// The size of the GHCB page.
pub const GHCB_PAGE_SIZE: usize = 4096;

/// The version of the GHCB protocol and page layout that we expect to use.
pub const GHCB_PROTOCOL_VERSION: u16 = 2;

/// The guest-host communications block.
///
/// See: Table 3 in <https://developer.amd.com/wp-content/resources/56421.pdf>
#[repr(C, align(4096))]
#[derive(Debug, FromBytes)]
pub struct Ghcb {
    /// Reserved. Must be 0.
    _reserved_0: [u8; 203],
    /// The current privilege level of the executing code.
    pub cpl: u8,
    /// Reserved. Must be 0.
    _reserved_1: [u8; 116],
    /// The value of the IA32_XSS model-specific reqister.
    pub xss: u64,
    /// Reserved. Must be 0.
    _reserved_2: [u8; 24],
    /// The value of the DR7 debug register.
    pub dr7: u64,
    /// Reserved. Must be 0.
    _reserved_3: [u8; 144],
    /// The value of the RAX register.
    pub rax: u64,
    /// Reserved. Must be 0.
    _reserved_4: [u8; 264],
    /// The value of the RBX register.
    pub rbx: u64,
    /// The value of the RCX register.
    pub rcx: u64,
    /// The value of the RDX register.
    pub rdx: u64,
    /// Reserved. Must be 0.
    _reserved_5: [u8; 112],
    /// Guest-controlled exit code.
    pub sw_exit_code: u64,
    /// Guest-controlled exit information 1.
    pub sw_exit_info_1: u64,
    /// Guest-controlled exit information 2.
    pub sw_exit_info_2: u64,
    /// Guest-controlled additional information.
    pub sw_scratch: u64,
    /// Reserved. Must be 0.
    _reserved_6: [u8; 56],
    /// Value of the XCR0 extended control register.
    pub xcr0: u64,
    /// Bitmap indicating which quadwords of the save state area are valid in the range from offset
    /// 0x000 through to offset 0x3ef.
    ///
    /// A flag must be set for each field that is set as part of handling a non-automatic exit
    /// event. The value must also be checked on the return from the hypervisor.
    pub valid_bitmap: ValidBitmap,
    /// The guest-physical address of the page that contains the x87-related saved state.
    pub x87_state_gpa: u64,
    /// Reserved. Must be 0.
    _reserved_7: [u8; 1016],
    /// Area that can be used as a shared buffer for communicating additional information.
    pub shared_buffer: [u8; 2032],
    /// Reserved. Must be 0.
    _reserved_8: [u8; 10],
    /// The version of the GHCB protocol and page layout in use.
    pub protocol_version: u16,
    /// The usage of the GHCB page. A value of 0 indicates the usage is in line with the definition
    /// as specified in this struct. Any other value indicates a hypervisor-defined usage.
    pub ghcb_usage: u32,
}

static_assertions::assert_eq_size!(Ghcb, [u8; GHCB_PAGE_SIZE]);

bitflags! {
    /// Flags indicating which fields in a specific GHCB instance are valid.
    #[derive(Default, FromBytes)]
    pub struct ValidBitmap: u128 {
        const CPL = (1 << 25);
        const XSS = (1 << 40);
        const DR7 = (1 << 44);
        const RAX = (1 << 63);
        const RBX = (1 << 97);
        const RCX = (1 << 98);
        const RDX = (1 << 99);
        const SW_EXIT_CODE = (1 << 114);
        const SW_EXIT_INFO_1 = (1 << 115);
        const SW_EXIT_INFO_2 = (1 << 116);
        const SW_SCRATCH = (1 << 117);
        const XCR0 = (1 << 125);
    }
}
<commit_msg>Add implementation of the GHCB protocol that supports IOIO_PROT (#3174)<commit_after>//
// Copyright 2022 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! This module contains an implementation of the guest-hypervisor communications block (GHCB) page
//! that can be used for communicating with the hypervisor.

use crate::msr::{
    register_ghcb_location, set_ghcb_address_and_exit, GhcbGpa, RegisterGhcbGpaRequest,
};
use bitflags::bitflags;
use zerocopy::FromBytes;

/// The size of the GHCB page.
pub const GHCB_PAGE_SIZE: usize = 4096;

/// The version of the GHCB protocol and page layout that we expect to use.
pub const GHCB_PROTOCOL_VERSION: u16 = 2;

/// The value of the sw_exit_code field when doing the IOIO protocol.
///
/// See section 4.1.2 in <https://developer.amd.com/wp-content/resources/56421.pdf>.
const SW_EXIT_CODE_IOIO_PROT: u64 = 0x7B;

/// Indicator bit that the address is a 16 bit number.
///
/// See section 15.10.2 of <https://www.amd.com/system/files/TechDocs/24593.pdf> for more details.
const IOIO_ADDRESS_SIZE_16: u64 = 1 << 7;

/// Indicator bit that the IO port will be used for reading data.
///
/// See section 15.10.2 of <https://www.amd.com/system/files/TechDocs/24593.pdf> for more details.
const IOIO_READ: u64 = 1;

/// Indicator bit that the IO data is an 8 bit number.
///
/// See section 15.10.2 of <https://www.amd.com/system/files/TechDocs/24593.pdf> for more details.
const IOIO_DATA_SIZE_8: u64 = 1 << 4;

/// Indicator bit that the IO data is a 32 bit number.
///
/// See section 15.10.2 of <https://www.amd.com/system/files/TechDocs/24593.pdf> for more details.
const IOIO_DATA_SIZE_32: u64 = 1 << 6;

/// The bitmap representing the minimum fields that must always be valid.
const BASE_VALID_BITMAP: ValidBitmap = ValidBitmap::SW_EXIT_CODE
    .union(ValidBitmap::SW_EXIT_INFO_1)
    .union(ValidBitmap::SW_EXIT_INFO_2);

/// The guest-host communications block.
///
/// See: Table 3 in <https://developer.amd.com/wp-content/resources/56421.pdf>
#[repr(C, align(4096))]
#[derive(Debug, FromBytes)]
pub struct Ghcb {
    /// Reserved. Must be 0.
    _reserved_0: [u8; 203],
    /// The current privilege level of the executing code.
    pub cpl: u8,
    /// Reserved. Must be 0.
    _reserved_1: [u8; 116],
    /// The value of the IA32_XSS model-specific reqister.
    pub xss: u64,
    /// Reserved. Must be 0.
    _reserved_2: [u8; 24],
    /// The value of the DR7 debug register.
    pub dr7: u64,
    /// Reserved. Must be 0.
    _reserved_3: [u8; 144],
    /// The value of the RAX register.
    pub rax: u64,
    /// Reserved. Must be 0.
    _reserved_4: [u8; 264],
    /// The value of the RBX register.
    pub rbx: u64,
    /// The value of the RCX register.
    pub rcx: u64,
    /// The value of the RDX register.
    pub rdx: u64,
    /// Reserved. Must be 0.
    _reserved_5: [u8; 112],
    /// Guest-controlled exit code.
    pub sw_exit_code: u64,
    /// Guest-controlled exit information 1.
    pub sw_exit_info_1: u64,
    /// Guest-controlled exit information 2.
    pub sw_exit_info_2: u64,
    /// Guest-controlled additional information.
    pub sw_scratch: u64,
    /// Reserved. Must be 0.
    _reserved_6: [u8; 56],
    /// Value of the XCR0 extended control register.
    pub xcr0: u64,
    /// Bitmap indicating which quadwords of the save state area are valid in the range from offset
    /// 0x000 through to offset 0x3ef.
    ///
    /// A flag must be set for each field that is set as part of handling a non-automatic exit
    /// event. The value must also be checked on the return from the hypervisor.
    pub valid_bitmap: ValidBitmap,
    /// The guest-physical address of the page that contains the x87-related saved state.
    pub x87_state_gpa: u64,
    /// Reserved. Must be 0.
    _reserved_7: [u8; 1016],
    /// Area that can be used as a shared buffer for communicating additional information.
    pub shared_buffer: [u8; 2032],
    /// Reserved. Must be 0.
    _reserved_8: [u8; 10],
    /// The version of the GHCB protocol and page layout in use.
    pub protocol_version: u16,
    /// The usage of the GHCB page. A value of 0 indicates the usage is in line with the definition
    /// as specified in this struct. Any other value indicates a hypervisor-defined usage.
    pub ghcb_usage: u32,
}

static_assertions::assert_eq_size!(Ghcb, [u8; GHCB_PAGE_SIZE]);

bitflags! {
    /// Flags indicating which fields in a specific GHCB instance are valid.
    #[derive(Default, FromBytes)]
    pub struct ValidBitmap: u128 {
        const CPL = (1 << 25);
        const XSS = (1 << 40);
        const DR7 = (1 << 44);
        const RAX = (1 << 63);
        const RBX = (1 << 97);
        const RCX = (1 << 98);
        const RDX = (1 << 99);
        const SW_EXIT_CODE = (1 << 114);
        const SW_EXIT_INFO_1 = (1 << 115);
        const SW_EXIT_INFO_2 = (1 << 116);
        const SW_SCRATCH = (1 << 117);
        const XCR0 = (1 << 125);
    }
}

impl Ghcb {
    /// Zeroes the entire GHCB.
    pub fn reset(&mut self) {
        reset_slice(&mut self._reserved_0[..]);
        self.cpl = 0;
        reset_slice(&mut self._reserved_1[..]);
        self.xss = 0;
        reset_slice(&mut self._reserved_2[..]);
        self.dr7 = 0;
        reset_slice(&mut self._reserved_3[..]);
        self.rax = 0;
        reset_slice(&mut self._reserved_4[..]);
        self.rbx = 0;
        self.rcx = 0;
        self.rcx = 0;
        reset_slice(&mut self._reserved_5[..]);
        self.sw_exit_code = 0;
        self.sw_exit_info_1 = 0;
        self.sw_exit_info_2 = 0;
        self.sw_scratch = 0;
        reset_slice(&mut self._reserved_6[..]);
        self.xcr0 = 0;
        self.valid_bitmap = ValidBitmap::empty();
        self.x87_state_gpa = 0;
        reset_slice(&mut self._reserved_7[..]);
        reset_slice(&mut self.shared_buffer[..]);
        reset_slice(&mut self._reserved_8[..]);
        self.protocol_version = 0;
        self.ghcb_usage = 0;
    }
}

/// Implementation of the GHCB protocol using the wrapped GHCB data structure.
pub struct GhcbProtocol<'a> {
    ghcb: &'a mut Ghcb,
}

impl<'a> GhcbProtocol<'a> {
    pub fn new(ghcb: &'a mut Ghcb) -> Self {
        Self { ghcb }
    }

    /// Gets the guest-physical address for the guest-hypervisor communication block.
    pub fn get_gpa(&self) -> usize {
        // Assume identity mapping for now.
        self.ghcb as *const Ghcb as usize
    }

    /// Registers the address of the GHCB with the hypervisor.
    pub fn register_with_hypervisor(&self) -> Result<(), &'static str> {
        register_ghcb_location(RegisterGhcbGpaRequest::new(self.get_gpa())?)
    }

    /// Writes an 8 bit number to an IO port via the IOIO protocol.
    ///
    /// See section 4.1.2 in <https://developer.amd.com/wp-content/resources/56421.pdf>.
    pub fn io_write_u8(&mut self, port: u16, data: u8) -> Result<(), &'static str> {
        self.ghcb.reset();
        let io_port = IOIO_ADDRESS_SIZE_16 | IOIO_DATA_SIZE_8 | ((port as u64) << 16);

        self.ghcb.sw_exit_code = SW_EXIT_CODE_IOIO_PROT;
        self.ghcb.sw_exit_info_1 = io_port;
        self.ghcb.rax = data as u64;
        self.ghcb.valid_bitmap = BASE_VALID_BITMAP.union(ValidBitmap::RAX);
        self.do_vmg_exit()
    }

    /// Writes a 32 bit number to an IO port via the IOIO protocol.
    ///
    /// See section 4.1.2 in <https://developer.amd.com/wp-content/resources/56421.pdf>.
    pub fn io_write_u32(&mut self, port: u16, data: u32) -> Result<(), &'static str> {
        self.ghcb.reset();
        let io_port = IOIO_ADDRESS_SIZE_16 | IOIO_DATA_SIZE_32 | ((port as u64) << 16);

        self.ghcb.sw_exit_code = SW_EXIT_CODE_IOIO_PROT;
        self.ghcb.sw_exit_info_1 = io_port;
        self.ghcb.rax = data as u64;
        self.do_vmg_exit()
    }

    /// Read a 32 bit number from an IO port via the IOIO protocol.
    ///
    /// See section 4.1.2 in <https://developer.amd.com/wp-content/resources/56421.pdf>.
    pub fn io_read_u32(&mut self, port: u16) -> Result<u32, &'static str> {
        self.ghcb.reset();
        let io_port = IOIO_ADDRESS_SIZE_16 | IOIO_DATA_SIZE_32 | IOIO_READ | ((port as u64) << 16);

        self.ghcb.sw_exit_code = SW_EXIT_CODE_IOIO_PROT;
        self.ghcb.sw_exit_info_1 = io_port;
        self.do_vmg_exit()?;
        Ok(self.ghcb.rax as u32)
    }

    /// Sets the address of the GHCB, exits to the hypervisor, and checks the return value when
    /// execution resumes.
    fn do_vmg_exit(&mut self) -> Result<(), &'static str> {
        self.ghcb.protocol_version = GHCB_PROTOCOL_VERSION;
        // Use a memory fence to ensure all writes happen before we hand over to the VMM.
        core::sync::atomic::fence(core::sync::atomic::Ordering::Release);
        set_ghcb_address_and_exit(GhcbGpa::new(self.get_gpa())?);
        // Use a memory fence to ensure that all earlier writes are commited before we read from the
        // GHCB.
        core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire);

        // The mask for extracting the hypervisor's return value from the sw_exit_info_1 field.
        const SW_EXIT_INFO_1_RETURN_MASK: u64 = 0xffff_ffff;

        if self.ghcb.sw_exit_info_1 & SW_EXIT_INFO_1_RETURN_MASK == 0 {
            Ok(())
        } else {
            // For now we treat all non-zero return values as unrecoverable errors.
            Err("VMGEXIT call returned an error.")
        }
    }
}

/// Resets a byte slice to all zeroes.
fn reset_slice(slice: &mut [u8]) {
    for byte in slice {
        *byte = 0;
    }
}
<|endoftext|>


<commit_before>#![cfg(test)]
use crate::behaviour::Behaviour;
use crate::multiaddr::Protocol;
use ::types::{EnrForkId, MinimalEthSpec};
use eth2_libp2p::discovery::{build_enr, CombinedKey, CombinedKeyExt};
use eth2_libp2p::*;
use futures::prelude::*;
use libp2p::core::identity::Keypair;
use libp2p::{
    core,
    core::{muxing::StreamMuxerBox, transport::boxed::Boxed},
    secio,
    swarm::{SwarmBuilder, SwarmEvent},
    PeerId, Swarm, Transport,
};
use slog::{crit, debug, info, Level};
use std::io::{Error, ErrorKind};
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;

type TSpec = MinimalEthSpec;

mod common;

type Libp2pBehaviour = Behaviour<TSpec>;

/// Build and return a eth2_libp2p Swarm with only secio support.
fn build_secio_swarm(
    config: &NetworkConfig,
    log: slog::Logger,
) -> error::Result<Swarm<Libp2pBehaviour>> {
    let local_keypair = Keypair::generate_secp256k1();
    let local_peer_id = PeerId::from(local_keypair.public());
    let enr_key = CombinedKey::from_libp2p(&local_keypair).unwrap();

    let enr = build_enr::<TSpec>(&enr_key, config, EnrForkId::default()).unwrap();
    let network_globals = Arc::new(NetworkGlobals::new(
        enr,
        config.libp2p_port,
        config.discovery_port,
        &log,
    ));

    let mut swarm = {
        // Set up the transport - tcp/ws with secio and mplex/yamux
        let transport = build_secio_transport(local_keypair.clone());
        // Lighthouse network behaviour
        let behaviour = Behaviour::new(&local_keypair, config, network_globals.clone(), &log)?;
        // requires a tokio runtime
        struct Executor(tokio::runtime::Handle);
        impl libp2p::core::Executor for Executor {
            fn exec(&self, f: Pin<Box<dyn Future<Output = ()> + Send>>) {
                self.0.spawn(f);
            }
        }
        SwarmBuilder::new(transport, behaviour, local_peer_id.clone())
            .executor(Box::new(Executor(tokio::runtime::Handle::current())))
            .build()
    };

    // listen on the specified address
    let listen_multiaddr = {
        let mut m = Multiaddr::from(config.listen_address);
        m.push(Protocol::Tcp(config.libp2p_port));
        m
    };

    match Swarm::listen_on(&mut swarm, listen_multiaddr.clone()) {
        Ok(_) => {
            let mut log_address = listen_multiaddr;
            log_address.push(Protocol::P2p(local_peer_id.clone().into()));
            info!(log, "Listening established"; "address" => format!("{}", log_address));
        }
        Err(err) => {
            crit!(
                log,
                "Unable to listen on libp2p address";
                "error" => format!("{:?}", err),
                "listen_multiaddr" => format!("{}", listen_multiaddr),
            );
            return Err("Libp2p was unable to listen on the given listen address.".into());
        }
    };

    // helper closure for dialing peers
    let mut dial_addr = |multiaddr: &Multiaddr| {
        match Swarm::dial_addr(&mut swarm, multiaddr.clone()) {
            Ok(()) => debug!(log, "Dialing libp2p peer"; "address" => format!("{}", multiaddr)),
            Err(err) => debug!(
                log,
                "Could not connect to peer"; "address" => format!("{}", multiaddr), "error" => format!("{:?}", err)
            ),
        };
    };

    // attempt to connect to any specified boot-nodes
    for bootnode_enr in &config.boot_nodes {
        for multiaddr in &bootnode_enr.multiaddr() {
            // ignore udp multiaddr if it exists
            let components = multiaddr.iter().collect::<Vec<_>>();
            if let Protocol::Udp(_) = components[1] {
                continue;
            }
            dial_addr(multiaddr);
        }
    }
    Ok(swarm)
}

/// Build a simple TCP transport with secio, mplex/yamux.
fn build_secio_transport(local_private_key: Keypair) -> Boxed<(PeerId, StreamMuxerBox), Error> {
    let transport = libp2p_tcp::TokioTcpConfig::new().nodelay(true);
    transport
        .upgrade(core::upgrade::Version::V1)
        .authenticate(secio::SecioConfig::new(local_private_key))
        .multiplex(core::upgrade::SelectUpgrade::new(
            libp2p::yamux::Config::default(),
            libp2p::mplex::MplexConfig::new(),
        ))
        .map(|(peer, muxer), _| (peer, core::muxing::StreamMuxerBox::new(muxer)))
        .timeout(Duration::from_secs(20))
        .timeout(Duration::from_secs(20))
        .map_err(|err| Error::new(ErrorKind::Other, err))
        .boxed()
}

/// Test if the encryption falls back to secio if noise isn't available
#[tokio::test]
async fn test_secio_noise_fallback() {
    // set up the logging. The level and enabled logging or not
    let log_level = Level::Trace;
    let enable_logging = false;

    let log = common::build_log(log_level, enable_logging);

    let port = common::unused_port("tcp").unwrap();
    let noisy_config = common::build_config(port, vec![]);
    let (_signal, exit) = exit_future::signal();
    let executor =
        environment::TaskExecutor::new(tokio::runtime::Handle::current(), exit, log.clone());
    let mut noisy_node = Service::new(executor, &noisy_config, EnrForkId::default(), &log)
        .expect("should build a libp2p instance")
        .1;

    let port = common::unused_port("tcp").unwrap();
    let secio_config = common::build_config(port, vec![common::get_enr(&noisy_node)]);

    // Building a custom Libp2pService from outside the crate isn't possible because of
    // private fields in the Libp2pService struct. A swarm is good enough for testing
    // compatibility with secio.
    let mut secio_swarm =
        build_secio_swarm(&secio_config, log.clone()).expect("should build a secio swarm");

    let secio_log = log.clone();

    let noisy_future = async {
        loop {
            noisy_node.next_event().await;
        }
    };

    let secio_future = async {
        loop {
            match secio_swarm.next_event().await {
                SwarmEvent::ConnectionEstablished { peer_id, .. } => {
                    // secio node negotiated a secio transport with
                    // the noise compatible node
                    info!(secio_log, "Connected to peer {}", peer_id);
                    return;
                }
                _ => {} // Ignore all other events
            }
        }
    };

    tokio::select! {
        _ = noisy_future => {}
        _ = secio_future => {}
        _ = tokio::time::delay_for(Duration::from_millis(800)) => {
            panic!("Future timed out");
        }
    }
}
<commit_msg>Remove noise test<commit_after><|endoftext|>


<commit_before>use crate::modules::{compile_hello, fib_mock, null_mock};
use criterion::Criterion;
use lucet_runtime::{DlModule, InstanceHandle, Limits, Module, Region, RegionCreate};
use lucetc::OptLevel;
use rayon::prelude::*;
use std::sync::{Arc, Mutex};
use tempfile::TempDir;

/// Common definiton of OptLevel
const BENCHMARK_OPT_LEVEL: OptLevel = OptLevel::SpeedAndSize;

/// Parallel instantiation.
///
/// This measures how well the region handles concurrent instantiations from multiple
/// threads. Scaling is not necessarily the point here, due to the locks on the region freelist and
/// memory management syscalls, but we do want to make sure the concurrent case isn't slower than
/// single-threaded.
fn par_instantiate<R: RegionCreate + 'static>(c: &mut Criterion) {
    const INSTANCES_PER_RUN: usize = 2000;

    fn setup<R: RegionCreate + 'static>() -> (Arc<R>, Vec<Option<InstanceHandle>>) {
        let region = R::create(INSTANCES_PER_RUN, &Limits::default()).unwrap();
        let mut handles = vec![];
        handles.resize_with(INSTANCES_PER_RUN, || None);
        (region, handles)
    }

    fn body<R: Region>(
        num_threads: usize,
        module: Arc<dyn Module>,
        region: Arc<R>,
        handles: &mut [Option<InstanceHandle>],
    ) {
        rayon::ThreadPoolBuilder::new()
            .num_threads(num_threads)
            .build()
            .unwrap()
            .install(|| {
                handles
                    .par_iter_mut()
                    .for_each(|handle| *handle = Some(region.new_instance(module.clone()).unwrap()))
            })
    }

    let workdir = TempDir::new().expect("create working directory");

    let so_file = workdir.path().join("out.so");
    compile_hello(&so_file, BENCHMARK_OPT_LEVEL);

    let module = DlModule::load(&so_file).unwrap();

    let bench = criterion::ParameterizedBenchmark::new(
        format!("par_instantiate ({})", R::TYPE_NAME),
        move |b, &num_threads| {
            b.iter_batched(
                setup,
                |(region, mut handles): (Arc<R>, _)| {
                    body(num_threads, module.clone(), region, handles.as_mut_slice())
                },
                criterion::BatchSize::SmallInput,
            )
        },
        (1..=num_cpus::get_physical()).collect::<Vec<usize>>(),
    )
    .sample_size(10);

    c.bench("par", bench);

    workdir.close().unwrap();
}

/// Run a function in parallel.
fn par_run<R: RegionCreate + 'static>(
    name: &str,
    instances_per_run: usize,
    module: Arc<dyn Module>,
    c: &mut Criterion,
) {
    let setup = move || {
        let region = R::create(instances_per_run, &Limits::default()).unwrap();

        (0..instances_per_run)
            .map(|_| region.new_instance(module.clone()).unwrap())
            .collect::<Vec<InstanceHandle>>()
    };

    fn body(num_threads: usize, handles: &mut [InstanceHandle]) {
        rayon::ThreadPoolBuilder::new()
            .num_threads(num_threads)
            .build()
            .unwrap()
            .install(|| {
                handles.par_iter_mut().for_each(|handle| {
                    handle.run("f", &[]).unwrap();
                })
            })
    }

    let bench = criterion::ParameterizedBenchmark::new(
        name,
        move |b, &num_threads| {
            b.iter_batched_ref(
                setup.clone(),
                |handles| body(num_threads, handles.as_mut_slice()),
                criterion::BatchSize::SmallInput,
            )
        },
        (1..=num_cpus::get_physical()).collect::<Vec<usize>>(),
    )
    .sample_size(10);

    c.bench("par", bench);
}

/// Run a function in parallel, controlling signal handlers and stacks manually.
fn par_run_manual_signals<R: RegionCreate + 'static>(
    name: &str,
    instances_per_run: usize,
    module: Arc<dyn Module>,
    c: &mut Criterion,
) {
    /// A guard to set up the manual signal handler, and automatically remove it when the benchmark
    /// is over.
    struct SignalGuard;

    impl SignalGuard {
        fn new() -> Self {
            lucet_runtime::install_lucet_signal_handler();
            Self
        }
    }

    impl Drop for SignalGuard {
        fn drop(&mut self) {
            lucet_runtime::remove_lucet_signal_handler();
        }
    }

    // make a vec full of ready-to-run instances, the Rayon thread pool, and intialize the `SignalGuard`
    let setup = move |num_threads| {
        move || {
            let region = R::create(instances_per_run, &Limits::default()).unwrap();
            let instances = (0..instances_per_run)
                .map(|_| {
                    let mut inst = region.new_instance(module.clone()).unwrap();
                    inst.ensure_signal_handler_installed(false);
                    inst.ensure_sigstack_installed(false);
                    inst
                })
                .collect::<Vec<InstanceHandle>>();
            let thread_pool = rayon::ThreadPoolBuilder::new()
                .num_threads(num_threads)
                // install an appropriately-sized sigaltstack for each thread that Rayon will run
                // instances on
                .start_handler(|_| {
                    thread_local! {
                        static SIGSTACK: Mutex<Vec<u8>> =
                            Mutex::new(vec![0; lucet_runtime::DEFAULT_SIGNAL_STACK_SIZE]);
                    }
                    SIGSTACK.with(|sigstack| {
                        let mut sigstack = sigstack.lock().unwrap();
                        let sigstack_raw = libc::stack_t {
                            ss_sp: sigstack.as_mut_ptr() as *mut _,
                            ss_flags: 0,
                            ss_size: lucet_runtime::DEFAULT_SIGNAL_STACK_SIZE,
                        };
                        unsafe {
                            libc::sigaltstack(&sigstack_raw, std::ptr::null_mut());
                        }
                    });
                })
                .build()
                .unwrap();
            let signal_guard = SignalGuard::new();

            (instances, thread_pool, signal_guard)
        }
    };

    let bench = criterion::ParameterizedBenchmark::new(
        name,
        move |b, &num_threads| {
            b.iter_batched_ref(
                setup.clone()(num_threads),
                |(handles, thread_pool, _signal_guard)| {
                    thread_pool.install(|| {
                        handles.par_iter_mut().for_each(|handle| {
                            handle.run("f", &[]).unwrap();
                        })
                    })
                },
                criterion::BatchSize::SmallInput,
            )
        },
        (1..=num_cpus::get_physical()).collect::<Vec<usize>>(),
    )
    .sample_size(10);

    c.bench("par", bench);
}

/// Run a trivial function in parallel.
///
/// This measures how well the region handles concurrent executions from multiple threads. Since the
/// body of the function is empty, scaling is not necessarily the point here, rather we want to make
/// sure that the locks for signal handling don't unduly slow the program down with multiple
/// threads.
fn par_run_null<R: RegionCreate + 'static>(c: &mut Criterion) {
    par_run::<R>(
        &format!("par_run_null ({})", R::TYPE_NAME),
        1000,
        null_mock(),
        c,
    );
    par_run_manual_signals::<R>(
        &format!("par_run_null_manual_signals ({})", R::TYPE_NAME),
        1000,
        null_mock(),
        c,
    );
}

/// Run a computation-heavy function in parallel.
///
/// Since running multiple independent fibonaccis is embarassingly parallel, this should scale close
/// to linearly.
fn par_run_fib<R: RegionCreate + 'static>(c: &mut Criterion) {
    par_run::<R>(
        &format!("par_run_fib ({})", R::TYPE_NAME),
        1000,
        fib_mock(),
        c,
    );
    par_run_manual_signals::<R>(
        &format!("par_run_fib_manual_signals ({})", R::TYPE_NAME),
        1000,
        fib_mock(),
        c,
    );
}

pub fn par_benches<R: RegionCreate + 'static>(c: &mut Criterion) {
    par_instantiate::<R>(c);
    par_run_null::<R>(c);
    par_run_fib::<R>(c);
}
<commit_msg>the thread-local for par benchmarks doesn't need to be a Mutex<commit_after>use crate::modules::{compile_hello, fib_mock, null_mock};
use criterion::Criterion;
use lucet_runtime::{DlModule, InstanceHandle, Limits, Module, Region, RegionCreate};
use lucetc::OptLevel;
use rayon::prelude::*;
use std::cell::RefCell;
use std::sync::Arc;
use tempfile::TempDir;

/// Common definiton of OptLevel
const BENCHMARK_OPT_LEVEL: OptLevel = OptLevel::SpeedAndSize;

/// Parallel instantiation.
///
/// This measures how well the region handles concurrent instantiations from multiple
/// threads. Scaling is not necessarily the point here, due to the locks on the region freelist and
/// memory management syscalls, but we do want to make sure the concurrent case isn't slower than
/// single-threaded.
fn par_instantiate<R: RegionCreate + 'static>(c: &mut Criterion) {
    const INSTANCES_PER_RUN: usize = 2000;

    fn setup<R: RegionCreate + 'static>() -> (Arc<R>, Vec<Option<InstanceHandle>>) {
        let region = R::create(INSTANCES_PER_RUN, &Limits::default()).unwrap();
        let mut handles = vec![];
        handles.resize_with(INSTANCES_PER_RUN, || None);
        (region, handles)
    }

    fn body<R: Region>(
        num_threads: usize,
        module: Arc<dyn Module>,
        region: Arc<R>,
        handles: &mut [Option<InstanceHandle>],
    ) {
        rayon::ThreadPoolBuilder::new()
            .num_threads(num_threads)
            .build()
            .unwrap()
            .install(|| {
                handles
                    .par_iter_mut()
                    .for_each(|handle| *handle = Some(region.new_instance(module.clone()).unwrap()))
            })
    }

    let workdir = TempDir::new().expect("create working directory");

    let so_file = workdir.path().join("out.so");
    compile_hello(&so_file, BENCHMARK_OPT_LEVEL);

    let module = DlModule::load(&so_file).unwrap();

    let bench = criterion::ParameterizedBenchmark::new(
        format!("par_instantiate ({})", R::TYPE_NAME),
        move |b, &num_threads| {
            b.iter_batched(
                setup,
                |(region, mut handles): (Arc<R>, _)| {
                    body(num_threads, module.clone(), region, handles.as_mut_slice())
                },
                criterion::BatchSize::SmallInput,
            )
        },
        (1..=num_cpus::get_physical()).collect::<Vec<usize>>(),
    )
    .sample_size(10);

    c.bench("par", bench);

    workdir.close().unwrap();
}

/// Run a function in parallel.
fn par_run<R: RegionCreate + 'static>(
    name: &str,
    instances_per_run: usize,
    module: Arc<dyn Module>,
    c: &mut Criterion,
) {
    let setup = move || {
        let region = R::create(instances_per_run, &Limits::default()).unwrap();

        (0..instances_per_run)
            .map(|_| region.new_instance(module.clone()).unwrap())
            .collect::<Vec<InstanceHandle>>()
    };

    fn body(num_threads: usize, handles: &mut [InstanceHandle]) {
        rayon::ThreadPoolBuilder::new()
            .num_threads(num_threads)
            .build()
            .unwrap()
            .install(|| {
                handles.par_iter_mut().for_each(|handle| {
                    handle.run("f", &[]).unwrap();
                })
            })
    }

    let bench = criterion::ParameterizedBenchmark::new(
        name,
        move |b, &num_threads| {
            b.iter_batched_ref(
                setup.clone(),
                |handles| body(num_threads, handles.as_mut_slice()),
                criterion::BatchSize::SmallInput,
            )
        },
        (1..=num_cpus::get_physical()).collect::<Vec<usize>>(),
    )
    .sample_size(10);

    c.bench("par", bench);
}

/// Run a function in parallel, controlling signal handlers and stacks manually.
fn par_run_manual_signals<R: RegionCreate + 'static>(
    name: &str,
    instances_per_run: usize,
    module: Arc<dyn Module>,
    c: &mut Criterion,
) {
    /// A guard to set up the manual signal handler, and automatically remove it when the benchmark
    /// is over.
    struct SignalGuard;

    impl SignalGuard {
        fn new() -> Self {
            lucet_runtime::install_lucet_signal_handler();
            Self
        }
    }

    impl Drop for SignalGuard {
        fn drop(&mut self) {
            lucet_runtime::remove_lucet_signal_handler();
        }
    }

    // make a vec full of ready-to-run instances, the Rayon thread pool, and intialize the `SignalGuard`
    let setup = move |num_threads| {
        move || {
            let region = R::create(instances_per_run, &Limits::default()).unwrap();
            let instances = (0..instances_per_run)
                .map(|_| {
                    let mut inst = region.new_instance(module.clone()).unwrap();
                    inst.ensure_signal_handler_installed(false);
                    inst.ensure_sigstack_installed(false);
                    inst
                })
                .collect::<Vec<InstanceHandle>>();
            let thread_pool = rayon::ThreadPoolBuilder::new()
                .num_threads(num_threads)
                // install an appropriately-sized sigaltstack for each thread that Rayon will run
                // instances on
                .start_handler(|_| {
                    thread_local! {
                        static SIGSTACK: RefCell<Box<[u8]>> =
                            RefCell::new(vec![0; lucet_runtime::DEFAULT_SIGNAL_STACK_SIZE].into_boxed_slice());
                    }
                    SIGSTACK.with(|sigstack| {
                        let sigstack_raw = libc::stack_t {
                            ss_sp: sigstack.borrow_mut().as_mut_ptr() as *mut _,
                            ss_flags: 0,
                            ss_size: lucet_runtime::DEFAULT_SIGNAL_STACK_SIZE,
                        };
                        unsafe {
                            libc::sigaltstack(&sigstack_raw, std::ptr::null_mut());
                        }
                    });
                })
                .build()
                .unwrap();
            let signal_guard = SignalGuard::new();

            (instances, thread_pool, signal_guard)
        }
    };

    let bench = criterion::ParameterizedBenchmark::new(
        name,
        move |b, &num_threads| {
            b.iter_batched_ref(
                setup.clone()(num_threads),
                |(handles, thread_pool, _signal_guard)| {
                    thread_pool.install(|| {
                        handles.par_iter_mut().for_each(|handle| {
                            handle.run("f", &[]).unwrap();
                        })
                    })
                },
                criterion::BatchSize::SmallInput,
            )
        },
        (1..=num_cpus::get_physical()).collect::<Vec<usize>>(),
    )
    .sample_size(10);

    c.bench("par", bench);
}

/// Run a trivial function in parallel.
///
/// This measures how well the region handles concurrent executions from multiple threads. Since the
/// body of the function is empty, scaling is not necessarily the point here, rather we want to make
/// sure that the locks for signal handling don't unduly slow the program down with multiple
/// threads.
fn par_run_null<R: RegionCreate + 'static>(c: &mut Criterion) {
    par_run::<R>(
        &format!("par_run_null ({})", R::TYPE_NAME),
        1000,
        null_mock(),
        c,
    );
    par_run_manual_signals::<R>(
        &format!("par_run_null_manual_signals ({})", R::TYPE_NAME),
        1000,
        null_mock(),
        c,
    );
}

/// Run a computation-heavy function in parallel.
///
/// Since running multiple independent fibonaccis is embarassingly parallel, this should scale close
/// to linearly.
fn par_run_fib<R: RegionCreate + 'static>(c: &mut Criterion) {
    par_run::<R>(
        &format!("par_run_fib ({})", R::TYPE_NAME),
        1000,
        fib_mock(),
        c,
    );
    par_run_manual_signals::<R>(
        &format!("par_run_fib_manual_signals ({})", R::TYPE_NAME),
        1000,
        fib_mock(),
        c,
    );
}

pub fn par_benches<R: RegionCreate + 'static>(c: &mut Criterion) {
    par_instantiate::<R>(c);
    par_run_null::<R>(c);
    par_run_fib::<R>(c);
}
<|endoftext|>


<commit_before><commit_msg>(#426): Make rsvg_rust_handle_new_from_file() work on Windows and the latest glib<commit_after><|endoftext|>


<commit_before>use super::{Open, Sink};
use alsa::device_name::HintIter;
use alsa::pcm::{Access, Format, Frames, HwParams, PCM};
use alsa::{Direction, Error, ValueOr};
use std::cmp::min;
use std::ffi::CString;
use std::io;
use std::process::exit;

const PERIOD_SIZE: usize = 5512; // Period of roughly 125ms
const BUFFERED_PERIODS: usize = 4; // ~ 0.5s latency

pub struct AlsaSink {
    pcm: Option<PCM>,
    device: String,
    buffer: [i16; PERIOD_SIZE as usize],
    buffered_data: usize,
}

fn list_outputs() {
    for t in &["pcm", "ctl", "hwdep"] {
        println!("{} devices:", t);
        let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap();
        for a in i {
            if let Some(Direction::Playback) = a.direction {
                // mimic aplay -L
                println!(
                    "{}\n\t{}\n",
                    a.name.unwrap(),
                    a.desc.unwrap().replace("\n", "\n\t")
                );
            }
        }
    }
}

fn open_device(dev_name: &str) -> Result<(PCM), Box<Error>> {
    let pcm = PCM::new(dev_name, Direction::Playback, false)?;
    // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8
    // latency = period_size * periods / (rate * bytes_per_frame)
    // For 16 Bit stereo data, one frame has a length of four bytes.
    // 500ms  = buffer_size / (44100 * 4)
    // buffer_size_bytes = 0.5 * 44100 / 4
    // buffer_size_frames = 0.5 * 44100 = 22050
    {
        // Set hardware parameters: 44100 Hz / Stereo / 16 bit
        let hwp = HwParams::any(&pcm)?;

        hwp.set_access(Access::RWInterleaved)?;
        hwp.set_format(Format::s16())?;
        hwp.set_rate(44100, ValueOr::Nearest)?;
        hwp.set_channels(2)?;
        hwp.set_period_size_near(PERIOD_SIZE as Frames, ValueOr::Nearest)?;
        hwp.set_buffer_size_near((PERIOD_SIZE * BUFFERED_PERIODS) as Frames)?;
        pcm.hw_params(&hwp)?;

        let swp = pcm.sw_params_current()?;
        swp.set_start_threshold(hwp.get_buffer_size()? - hwp.get_period_size()?)?;
        pcm.sw_params(&swp)?;
    }

    Ok(pcm)
}

impl Open for AlsaSink {
    fn open(device: Option<String>) -> AlsaSink {
        info!("Using alsa sink");

        let name = match device.as_ref().map(AsRef::as_ref) {
            Some("?") => {
                println!("Listing available alsa outputs");
                list_outputs();
                exit(0)
            }
            Some(device) => device,
            None => "default",
        }
        .to_string();

        AlsaSink {
            pcm: None,
            device: name,
            buffer: [0; PERIOD_SIZE],
            buffered_data: 0,
        }
    }
}

impl Sink for AlsaSink {
    fn start(&mut self) -> io::Result<()> {
        if self.pcm.is_none() {
            let pcm = open_device(&self.device);
            match pcm {
                Ok(p) => self.pcm = Some(p),
                Err(e) => {
                    error!("Alsa error PCM open {}", e);
                    return Err(io::Error::new(
                        io::ErrorKind::Other,
                        "Alsa error: PCM open failed",
                    ));
                }
            }
        }

        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        {
            let pcm = self.pcm.as_ref().unwrap();
            pcm.drain().unwrap();
        }
        self.pcm = None;
        self.buffered_data = 0;
        Ok(())
    }

    fn write(&mut self, data: &[i16]) -> io::Result<()> {
        let mut processed_data = 0;
        while processed_data < data.len() {
            let data_to_buffer = min(
                PERIOD_SIZE - self.buffered_data,
                data.len() - processed_data,
            );
            let buffer_slice =
                &mut self.buffer[self.buffered_data..self.buffered_data + data_to_buffer];
            buffer_slice.copy_from_slice(&data[processed_data..processed_data + data_to_buffer]);
            self.buffered_data += data_to_buffer;
            processed_data += data_to_buffer;
            if self.buffered_data == PERIOD_SIZE {
                self.buffered_data = 0;
                let pcm = self.pcm.as_mut().unwrap();
                let io = pcm.io_i16().unwrap();
                match io.writei(&self.buffer) {
                    Ok(_) => (),
                    Err(err) => pcm.try_recover(err, false).unwrap(),
                }
            }
        }

        Ok(())
    }
}
<commit_msg>On stop write any chunk(s) left in the period buffer<commit_after>use super::{Open, Sink};
use alsa::device_name::HintIter;
use alsa::pcm::{Access, Format, Frames, HwParams, PCM};
use alsa::{Direction, Error, ValueOr};
use std::cmp::min;
use std::ffi::CString;
use std::io;
use std::process::exit;

const PERIOD_SIZE: usize = 5512; // Period of roughly 125ms
const BUFFERED_PERIODS: usize = 4; // ~ 0.5s latency

pub struct AlsaSink {
    pcm: Option<PCM>,
    device: String,
    buffer: [i16; PERIOD_SIZE as usize],
    buffered_data: usize,
}

fn list_outputs() {
    for t in &["pcm", "ctl", "hwdep"] {
        println!("{} devices:", t);
        let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap();
        for a in i {
            if let Some(Direction::Playback) = a.direction {
                // mimic aplay -L
                println!(
                    "{}\n\t{}\n",
                    a.name.unwrap(),
                    a.desc.unwrap().replace("\n", "\n\t")
                );
            }
        }
    }
}

fn open_device(dev_name: &str) -> Result<(PCM), Box<Error>> {
    let pcm = PCM::new(dev_name, Direction::Playback, false)?;
    // http://www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8
    // latency = period_size * periods / (rate * bytes_per_frame)
    // For 16 Bit stereo data, one frame has a length of four bytes.
    // 500ms  = buffer_size / (44100 * 4)
    // buffer_size_bytes = 0.5 * 44100 / 4
    // buffer_size_frames = 0.5 * 44100 = 22050
    {
        // Set hardware parameters: 44100 Hz / Stereo / 16 bit
        let hwp = HwParams::any(&pcm)?;

        hwp.set_access(Access::RWInterleaved)?;
        hwp.set_format(Format::s16())?;
        hwp.set_rate(44100, ValueOr::Nearest)?;
        hwp.set_channels(2)?;
        hwp.set_period_size_near(PERIOD_SIZE as Frames, ValueOr::Nearest)?;
        hwp.set_buffer_size_near((PERIOD_SIZE * BUFFERED_PERIODS) as Frames)?;
        pcm.hw_params(&hwp)?;

        let swp = pcm.sw_params_current()?;
        swp.set_start_threshold(hwp.get_buffer_size()? - hwp.get_period_size()?)?;
        pcm.sw_params(&swp)?;
    }

    Ok(pcm)
}

impl Open for AlsaSink {
    fn open(device: Option<String>) -> AlsaSink {
        info!("Using alsa sink");

        let name = match device.as_ref().map(AsRef::as_ref) {
            Some("?") => {
                println!("Listing available alsa outputs");
                list_outputs();
                exit(0)
            }
            Some(device) => device,
            None => "default",
        }
        .to_string();

        AlsaSink {
            pcm: None,
            device: name,
            buffer: [0; PERIOD_SIZE],
            buffered_data: 0,
        }
    }
}

impl Sink for AlsaSink {
    fn start(&mut self) -> io::Result<()> {
        if self.pcm.is_none() {
            let pcm = open_device(&self.device);
            match pcm {
                Ok(p) => self.pcm = Some(p),
                Err(e) => {
                    error!("Alsa error PCM open {}", e);
                    return Err(io::Error::new(
                        io::ErrorKind::Other,
                        "Alsa error: PCM open failed",
                    ));
                }
            }
        }

        Ok(())
    }

    fn stop(&mut self) -> io::Result<()> {
        {
            let pcm = self.pcm.as_mut().unwrap();
            // Write any leftover data in the period buffer
            // before draining the actual buffer
            let io = pcm.io_i16().unwrap();
            match io.writei(&self.buffer[..self.buffered_data]) {
                Ok(_) => (),
                Err(err) => pcm.try_recover(err, false).unwrap(),
            }
            pcm.drain().unwrap();
        }
        self.pcm = None;
        self.buffered_data = 0;
        Ok(())
    }

    fn write(&mut self, data: &[i16]) -> io::Result<()> {
        let mut processed_data = 0;
        while processed_data < data.len() {
            let data_to_buffer = min(
                PERIOD_SIZE - self.buffered_data,
                data.len() - processed_data,
            );
            let buffer_slice =
                &mut self.buffer[self.buffered_data..self.buffered_data + data_to_buffer];
            buffer_slice.copy_from_slice(&data[processed_data..processed_data + data_to_buffer]);
            self.buffered_data += data_to_buffer;
            processed_data += data_to_buffer;
            if self.buffered_data == PERIOD_SIZE {
                self.buffered_data = 0;
                let pcm = self.pcm.as_mut().unwrap();
                let io = pcm.io_i16().unwrap();
                match io.writei(&self.buffer) {
                    Ok(_) => (),
                    Err(err) => pcm.try_recover(err, false).unwrap(),
                }
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate rosc;

#[cfg(feature = "std")]
use rosc::address::{Matcher, verify_address};
use rosc::address::verify_address_pattern;

#[cfg(feature = "std")]
#[test]
fn test_matcher() {
    let mut matcher;

    // Regular address using only alphanumeric parts
    matcher = Matcher::new("/oscillator/1/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/1/frequency").expect("Should match");
    matcher.match_address("/oscillator/1/phase").expect_err("Should not match");
    matcher.match_address("/oscillator/1/frequencyfoo").expect_err("Should not match");
    matcher.match_address("/prefix/oscillator/1/frequency").expect_err("Should not match");

    // Choice
    matcher = Matcher::new("/foo{bar,baz}").expect("Should be valid");
    matcher.match_address("/foobar").expect("Should match");
    matcher.match_address("/foobaz").expect("Should match");

    matcher = Matcher::new("/foo{bar,baz,tron}").expect("Should be valid");
    matcher.match_address("/footron").expect("Should match");

    // Character class
    // Character classes are sets or ranges of characters to match.
    // e.g. [a-z] will match any lower case alphabetic character. [abcd] will match the characters abcd.
    // They can be negated with '!', e.g. [!0-9] will match all characters except 0-9
    // Basic example
    matcher = Matcher::new("/oscillator/[0-9]").expect("Should be valid");
    matcher.match_address("/oscillator/0").expect("Should match");  // Beginning of range included
    matcher.match_address("/oscillator/6").expect("Should match");  // Middle of range
    matcher.match_address("/oscillator/9").expect("Should match");  // Last member of range included

    // Inverted order should work too
    matcher = Matcher::new("/oscillator/[9-0]").expect("Should be valid");
    matcher.match_address("/oscillator/0").expect("Should match");
    matcher.match_address("/oscillator/6").expect("Should match");
    matcher.match_address("/oscillator/9").expect("Should match");

    // Multiple ranges
    matcher = Matcher::new("/oscillator/[a-zA-Z0-9]").expect("Should be valid");
    matcher.match_address("/oscillator/0").expect("Should match");
    matcher.match_address("/oscillator/a").expect("Should match");
    matcher.match_address("/oscillator/A").expect("Should match");

    // Negated range
    matcher = Matcher::new("/oscillator/[!0-9]").expect("Should be valid");
    matcher.match_address("/oscillator/1").expect_err("Should not match");
    matcher.match_address("/oscillator/a").expect("Should match");

    // Extra exclamation points must be entirely ignored
    matcher = Matcher::new("/oscillator/[!0-9!a-z!]").expect("Should be valid");
    matcher.match_address("/oscillator/A").expect("Should match");

    // Trailing dash has no special meaning
    matcher = Matcher::new("/oscillator/[abcd-]").expect("Should be valid");
    matcher.match_address("/oscillator/a").expect("Should match");
    matcher.match_address("/oscillator/-").expect("Should match");

    // Single wildcard
    // A single wildcard '?' matches excatly one alphanumeric character
    matcher = Matcher::new("/oscillator/?/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/1/frequency").expect("Should match");
    matcher.match_address("/oscillator/F/frequency").expect("Should match");
    matcher.match_address("/oscillator//frequency").expect_err("Should not match");
    matcher.match_address("/oscillator/10/frequency").expect_err("Should not match");

    // Test if two consecutive wildcards match
    matcher = Matcher::new("/oscillator/??/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/10/frequency").expect("Should match");
    matcher.match_address("/oscillator/1/frequency").expect_err("Should not match");

    // Test if it works if it is surrounded by non-wildcards
    matcher = Matcher::new("/oscillator/prefixed?postfixed/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/prefixed1postfixed/frequency").expect("Should match");
    matcher.match_address("/oscillator/prefixedpostfixed/frequency").expect_err("Should not match");

    // Wildcard
    // Wildcards '*' match zero or more alphanumeric characters. The implementation is greedy,
    // meaning it will match the longest possible sequence
    matcher = Matcher::new("/oscillator/*/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/anything123/frequency").expect("Should match");
    matcher.match_address("/oscillator/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~/frequency").expect("Should match");
    // Test that wildcard doesn't cross part boundary
    matcher.match_address("/oscillator/extra/part/frequency").expect_err("Should not match");
    matcher.match_address("/oscillator//frequency").expect_err("Should not match");

    // Test greediness
    matcher = Matcher::new("/oscillator/*bar/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/foobar/frequency").expect("Should match");
    matcher.match_address("/oscillator/foobarbar/frequency").expect("Should match");

    // Minimum length of 2
    matcher = Matcher::new("/oscillator/*??/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/foobar/frequency").expect("Should match");
    matcher.match_address("/oscillator/f/frequency").expect_err("Should not match");

    // Minimum length of 2 and another component follows
    matcher = Matcher::new("/oscillator/*??baz/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/foobarbaz/frequency").expect("Should match");
    matcher.match_address("/oscillator/fbaz/frequency").expect_err("Should not match");

    // Mix with character class
    matcher = Matcher::new("/oscillator/*[a-d]/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/a/frequency").expect("Should match");
    matcher.match_address("/oscillator/fooa/frequency").expect("Should match");
    matcher.match_address("/oscillator/foox/frequency").expect_err("Should not match");

    // Mix with choice
    matcher = Matcher::new("/oscillator/*{bar,baz}/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/foobar/frequency").expect("Should match");
    matcher.match_address("/oscillator/baz/frequency").expect("Should match");
    matcher.match_address("/oscillator/something/frequency").expect_err("Should not match");

    // Check for allowed literal characters
    matcher = Matcher::new("/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~").expect("Should be valid");
    matcher.match_address("/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~").expect("Should match");

    // Check that single wildcard matches all legal characters
    matcher = Matcher::new("/?").expect("Should be valid");
    let legal = "!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~";
    for c in legal.chars() {
        matcher.match_address(format!("/{}", c).as_str()).expect("Should match");
    }
}

#[cfg(feature = "std")]
#[test]
fn test_verify_address() {
    verify_address("/test").expect("Should be valid");
    verify_address("/oscillator/1/frequency").expect("Should be valid");
    verify_address("/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~/foo").expect("Should be valid");

    // No '/' at beginning
    verify_address("test").expect_err("Should not be valid");
    // '/' at the end
    verify_address("/test/").expect_err("Should not be valid");
    // Different address pattern elements that are not allowed in regular addresses
    verify_address("/test*").expect_err("Should not be valid");
    verify_address("/test?").expect_err("Should not be valid");
    verify_address("/test{foo,bar}").expect_err("Should not be valid");
    verify_address("/test[a-z]").expect_err("Should not be valid");
}

#[cfg(feature = "std")]
#[test]
fn test_verify_address_pattern() {
    verify_address_pattern("/test").expect("Should be valid");
    verify_address_pattern("/oscillator/1/frequency").expect("Should be valid");
    verify_address_pattern("/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~/foo").expect("Should be valid");

    // No '/' at beginning
    verify_address_pattern("test").expect_err("Should not be valid");
    // '/' at the end
    verify_address_pattern("/test/").expect_err("Should not be valid");

    // Different address pattern elements
    verify_address_pattern("/test*").expect("Should be valid");
    verify_address_pattern("/test?").expect("Should be valid");
    verify_address_pattern("/test{foo,bar}").expect("Should be valid");
    verify_address_pattern("/test[a-z]").expect("Should be valid");
    verify_address_pattern("/test[a-z]*??/{foo,bar,baz}[!a-z0-9]/*").expect("Should be valid");
    verify_address_pattern("/test{foo}").expect("Should be valid");

    // Empty element in choice
    verify_address_pattern("/{asd,}/").expect_err("Should not be valid");
    // Illegal character in range
    verify_address_pattern("/[a-b*]/").expect_err("Should not be valid");

    // Empty
    verify_address_pattern("").expect_err("Should not be valid");
    // Empty part
    verify_address_pattern("//empty/part").expect_err("Should not be valid");
    // Unclosed range
    verify_address_pattern("/[a-/foo").expect_err("Should not be valid");
    verify_address_pattern("/[a-").expect_err("Should not be valid");
    // Unclosed alternative
    verify_address_pattern("/{foo,bar/foo").expect_err("Should not be valid");
    verify_address_pattern("/{foo,/bar").expect_err("Should not be valid");
    verify_address_pattern("/{foo").expect_err("Should not be valid");
    verify_address_pattern("/foo{,").expect_err("Should not be valid");
}

#[cfg(feature = "std")]
#[test]
fn test_bad_address_pattern() {
    let expected_err = "bad OSC address pattern: bad address pattern";
    assert_eq!(Matcher::new("").unwrap_err().to_string(), expected_err);
    assert_eq!(Matcher::new("/").unwrap_err().to_string(), expected_err);
    assert_eq!(Matcher::new("//empty/parts/").unwrap_err().to_string(), expected_err);
    assert_eq!(Matcher::new("////").unwrap_err().to_string(), expected_err);
    assert_eq!(Matcher::new("/{unclosed,alternative").unwrap_err().to_string(), expected_err);
    assert_eq!(Matcher::new("/unclosed/[range-").unwrap_err().to_string(), expected_err);
}

#[cfg(feature = "std")]
#[test]
fn test_bad_address() {
    let matcher = Matcher::new("/does-not-matter").expect("Matcher::new");
    let expected_err = "bad OSC address: bad address";
    assert_eq!(matcher.match_address("").unwrap_err().to_string(), expected_err);
    assert_eq!(matcher.match_address("/").unwrap_err().to_string(), expected_err);
    assert_eq!(matcher.match_address("/contains/wildcards?").unwrap_err().to_string(), expected_err);
    assert_eq!(matcher.match_address("/contains/wildcards*").unwrap_err().to_string(), expected_err);
    assert_eq!(matcher.match_address("/contains/ranges[a-z]").unwrap_err().to_string(), expected_err);
    assert_eq!(matcher.match_address("/contains/ranges[!a-z]").unwrap_err().to_string(), expected_err);
    assert_eq!(matcher.match_address("/{contains,alternative}").unwrap_err().to_string(), expected_err);
}
<commit_msg>Remove obsolete tests<commit_after>extern crate rosc;

#[cfg(feature = "std")]
use rosc::address::{Matcher, verify_address};
use rosc::address::verify_address_pattern;

#[cfg(feature = "std")]
#[test]
fn test_matcher() {
    let mut matcher;

    // Regular address using only alphanumeric parts
    matcher = Matcher::new("/oscillator/1/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/1/frequency").expect("Should match");
    matcher.match_address("/oscillator/1/phase").expect_err("Should not match");
    matcher.match_address("/oscillator/1/frequencyfoo").expect_err("Should not match");
    matcher.match_address("/prefix/oscillator/1/frequency").expect_err("Should not match");

    // Choice
    matcher = Matcher::new("/foo{bar,baz}").expect("Should be valid");
    matcher.match_address("/foobar").expect("Should match");
    matcher.match_address("/foobaz").expect("Should match");

    matcher = Matcher::new("/foo{bar,baz,tron}").expect("Should be valid");
    matcher.match_address("/footron").expect("Should match");

    // Character class
    // Character classes are sets or ranges of characters to match.
    // e.g. [a-z] will match any lower case alphabetic character. [abcd] will match the characters abcd.
    // They can be negated with '!', e.g. [!0-9] will match all characters except 0-9
    // Basic example
    matcher = Matcher::new("/oscillator/[0-9]").expect("Should be valid");
    matcher.match_address("/oscillator/0").expect("Should match");  // Beginning of range included
    matcher.match_address("/oscillator/6").expect("Should match");  // Middle of range
    matcher.match_address("/oscillator/9").expect("Should match");  // Last member of range included

    // Inverted order should work too
    matcher = Matcher::new("/oscillator/[9-0]").expect("Should be valid");
    matcher.match_address("/oscillator/0").expect("Should match");
    matcher.match_address("/oscillator/6").expect("Should match");
    matcher.match_address("/oscillator/9").expect("Should match");

    // Multiple ranges
    matcher = Matcher::new("/oscillator/[a-zA-Z0-9]").expect("Should be valid");
    matcher.match_address("/oscillator/0").expect("Should match");
    matcher.match_address("/oscillator/a").expect("Should match");
    matcher.match_address("/oscillator/A").expect("Should match");

    // Negated range
    matcher = Matcher::new("/oscillator/[!0-9]").expect("Should be valid");
    matcher.match_address("/oscillator/1").expect_err("Should not match");
    matcher.match_address("/oscillator/a").expect("Should match");

    // Extra exclamation points must be entirely ignored
    matcher = Matcher::new("/oscillator/[!0-9!a-z!]").expect("Should be valid");
    matcher.match_address("/oscillator/A").expect("Should match");

    // Trailing dash has no special meaning
    matcher = Matcher::new("/oscillator/[abcd-]").expect("Should be valid");
    matcher.match_address("/oscillator/a").expect("Should match");
    matcher.match_address("/oscillator/-").expect("Should match");

    // Single wildcard
    // A single wildcard '?' matches excatly one alphanumeric character
    matcher = Matcher::new("/oscillator/?/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/1/frequency").expect("Should match");
    matcher.match_address("/oscillator/F/frequency").expect("Should match");
    matcher.match_address("/oscillator//frequency").expect_err("Should not match");
    matcher.match_address("/oscillator/10/frequency").expect_err("Should not match");

    // Test if two consecutive wildcards match
    matcher = Matcher::new("/oscillator/??/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/10/frequency").expect("Should match");
    matcher.match_address("/oscillator/1/frequency").expect_err("Should not match");

    // Test if it works if it is surrounded by non-wildcards
    matcher = Matcher::new("/oscillator/prefixed?postfixed/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/prefixed1postfixed/frequency").expect("Should match");
    matcher.match_address("/oscillator/prefixedpostfixed/frequency").expect_err("Should not match");

    // Wildcard
    // Wildcards '*' match zero or more alphanumeric characters. The implementation is greedy,
    // meaning it will match the longest possible sequence
    matcher = Matcher::new("/oscillator/*/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/anything123/frequency").expect("Should match");
    matcher.match_address("/oscillator/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~/frequency").expect("Should match");
    // Test that wildcard doesn't cross part boundary
    matcher.match_address("/oscillator/extra/part/frequency").expect_err("Should not match");
    matcher.match_address("/oscillator//frequency").expect_err("Should not match");

    // Test greediness
    matcher = Matcher::new("/oscillator/*bar/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/foobar/frequency").expect("Should match");
    matcher.match_address("/oscillator/foobarbar/frequency").expect("Should match");

    // Minimum length of 2
    matcher = Matcher::new("/oscillator/*??/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/foobar/frequency").expect("Should match");
    matcher.match_address("/oscillator/f/frequency").expect_err("Should not match");

    // Minimum length of 2 and another component follows
    matcher = Matcher::new("/oscillator/*??baz/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/foobarbaz/frequency").expect("Should match");
    matcher.match_address("/oscillator/fbaz/frequency").expect_err("Should not match");

    // Mix with character class
    matcher = Matcher::new("/oscillator/*[a-d]/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/a/frequency").expect("Should match");
    matcher.match_address("/oscillator/fooa/frequency").expect("Should match");
    matcher.match_address("/oscillator/foox/frequency").expect_err("Should not match");

    // Mix with choice
    matcher = Matcher::new("/oscillator/*{bar,baz}/frequency").expect("Should be valid");
    matcher.match_address("/oscillator/foobar/frequency").expect("Should match");
    matcher.match_address("/oscillator/baz/frequency").expect("Should match");
    matcher.match_address("/oscillator/something/frequency").expect_err("Should not match");

    // Check for allowed literal characters
    matcher = Matcher::new("/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~").expect("Should be valid");
    matcher.match_address("/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~").expect("Should match");

    // Check that single wildcard matches all legal characters
    matcher = Matcher::new("/?").expect("Should be valid");
    let legal = "!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~";
    for c in legal.chars() {
        matcher.match_address(format!("/{}", c).as_str()).expect("Should match");
    }
}

#[cfg(feature = "std")]
#[test]
fn test_verify_address() {
    verify_address("/test").expect("Should be valid");
    verify_address("/oscillator/1/frequency").expect("Should be valid");
    verify_address("/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~/foo").expect("Should be valid");

    // No '/' at beginning
    verify_address("test").expect_err("Should not be valid");
    // '/' at the end
    verify_address("/test/").expect_err("Should not be valid");
    // Different address pattern elements that are not allowed in regular addresses
    verify_address("/test*").expect_err("Should not be valid");
    verify_address("/test?").expect_err("Should not be valid");
    verify_address("/test{foo,bar}").expect_err("Should not be valid");
    verify_address("/test[a-z]").expect_err("Should not be valid");
}

#[cfg(feature = "std")]
#[test]
fn test_verify_address_pattern() {
    verify_address_pattern("/test").expect("Should be valid");
    verify_address_pattern("/oscillator/1/frequency").expect("Should be valid");
    verify_address_pattern("/!\"$%&'()+-.0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~/foo").expect("Should be valid");

    // No '/' at beginning
    verify_address_pattern("test").expect_err("Should not be valid");
    // '/' at the end
    verify_address_pattern("/test/").expect_err("Should not be valid");

    // Different address pattern elements
    verify_address_pattern("/test*").expect("Should be valid");
    verify_address_pattern("/test?").expect("Should be valid");
    verify_address_pattern("/test{foo,bar}").expect("Should be valid");
    verify_address_pattern("/test[a-z]").expect("Should be valid");
    verify_address_pattern("/test[a-z]*??/{foo,bar,baz}[!a-z0-9]/*").expect("Should be valid");
    verify_address_pattern("/test{foo}").expect("Should be valid");

    // Empty element in choice
    verify_address_pattern("/{asd,}/").expect_err("Should not be valid");
    // Illegal character in range
    verify_address_pattern("/[a-b*]/").expect_err("Should not be valid");

    // Empty
    verify_address_pattern("").expect_err("Should not be valid");
    // Empty part
    verify_address_pattern("//empty/part").expect_err("Should not be valid");
    // Unclosed range
    verify_address_pattern("/[a-/foo").expect_err("Should not be valid");
    verify_address_pattern("/[a-").expect_err("Should not be valid");
    // Unclosed alternative
    verify_address_pattern("/{foo,bar/foo").expect_err("Should not be valid");
    verify_address_pattern("/{foo,/bar").expect_err("Should not be valid");
    verify_address_pattern("/{foo").expect_err("Should not be valid");
    verify_address_pattern("/foo{,").expect_err("Should not be valid");
}
<|endoftext|>


<commit_before>#![cfg(not(windows))]

use clap::{arg, error::ErrorKind, value_parser, Arg, ArgAction, Command};
use std::ffi::OsString;
use std::os::unix::ffi::OsStringExt;

#[test]
fn invalid_utf8_strict_positional() {
    let m = Command::new("bad_utf8")
        .arg(Arg::new("arg"))
        .try_get_matches_from(vec![OsString::from(""), OsString::from_vec(vec![0xe9])]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_option_short_space() {
    let m = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("-a"),
            OsString::from_vec(vec![0xe9]),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_option_short_equals() {
    let m = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x61, 0x3d, 0xe9]),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_option_short_no_space() {
    let m = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x61, 0xe9]),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_option_long_space() {
    let m = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("--arg"),
            OsString::from_vec(vec![0xe9]),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_option_long_equals() {
    let m = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x2d, 0x61, 0x72, 0x67, 0x3d, 0xe9]),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_positional() {
    let r = Command::new("bad_utf8")
        .arg(Arg::new("arg").value_parser(value_parser!(OsString)))
        .try_get_matches_from(vec![OsString::from(""), OsString::from_vec(vec![0xe9])]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn invalid_utf8_option_short_space() {
    let r = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set)
                .value_parser(value_parser!(OsString)),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("-a"),
            OsString::from_vec(vec![0xe9]),
        ]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn invalid_utf8_option_short_equals() {
    let r = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set)
                .value_parser(value_parser!(OsString)),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x61, 0x3d, 0xe9]),
        ]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn invalid_utf8_option_short_no_space() {
    let r = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set)
                .value_parser(value_parser!(OsString)),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x61, 0xe9]),
        ]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn invalid_utf8_option_long_space() {
    let r = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set)
                .value_parser(value_parser!(OsString)),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("--arg"),
            OsString::from_vec(vec![0xe9]),
        ]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn invalid_utf8_option_long_equals() {
    let r = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set)
                .value_parser(value_parser!(OsString)),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x2d, 0x61, 0x72, 0x67, 0x3d, 0xe9]),
        ]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn refuse_invalid_utf8_subcommand_with_allow_external_subcommands() {
    let m = Command::new("bad_utf8")
        .allow_external_subcommands(true)
        .external_subcommand_value_parser(value_parser!(String))
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0xe9]),
            OsString::from("normal"),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn refuse_invalid_utf8_subcommand_when_args_are_allowed_with_allow_external_subcommands() {
    let m = Command::new("bad_utf8")
        .allow_external_subcommands(true)
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0xe9]),
            OsString::from("normal"),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn refuse_invalid_utf8_subcommand_args_with_allow_external_subcommands() {
    let m = Command::new("bad_utf8")
        .allow_external_subcommands(true)
        .external_subcommand_value_parser(value_parser!(String))
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("subcommand"),
            OsString::from("normal"),
            OsString::from_vec(vec![0xe9]),
            OsString::from("--another_normal"),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn allow_invalid_utf8_subcommand_args_with_allow_external_subcommands() {
    let m = Command::new("bad_utf8")
        .allow_external_subcommands(true)
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("subcommand"),
            OsString::from("normal"),
            OsString::from_vec(vec![0xe9]),
            OsString::from("--another_normal"),
        ]);
    assert!(m.is_ok(), "{}", m.unwrap_err());
    let m = m.unwrap();
    let (subcommand, args) = m.subcommand().unwrap();
    let args = args
        .get_many::<OsString>("")
        .unwrap()
        .cloned()
        .collect::<Vec<_>>();
    assert_eq!(subcommand, OsString::from("subcommand"));
    assert_eq!(
        args,
        vec![
            OsString::from("normal"),
            OsString::from_vec(vec![0xe9]),
            OsString::from("--another_normal"),
        ]
    );
}

#[test]
fn allow_validated_utf8_value_of() {
    let a = Command::new("test").arg(arg!(--name <NAME>));
    let m = a.try_get_matches_from(["test", "--name", "me"]).unwrap();
    let _ = m.get_one::<String>("name").map(|v| v.as_str());
}

#[test]
fn allow_validated_utf8_external_subcommand_values_of() {
    let a = Command::new("test")
        .allow_external_subcommands(true)
        .external_subcommand_value_parser(value_parser!(String));
    let m = a.try_get_matches_from(vec!["test", "cmd", "arg"]).unwrap();
    let (_ext, args) = m.subcommand().unwrap();
    args.get_many::<String>("").unwrap_or_default().count();
}

#[test]
#[should_panic = "Mismatch between definition and access of ``. Could not downcast to std::ffi::os_str::OsString, need to downcast to alloc::string::String"]
fn panic_validated_utf8_external_subcommand_values_of_os() {
    let a = Command::new("test")
        .allow_external_subcommands(true)
        .external_subcommand_value_parser(value_parser!(String));
    let m = a.try_get_matches_from(vec!["test", "cmd", "arg"]).unwrap();
    let (_ext, args) = m.subcommand().unwrap();
    args.get_many::<OsString>("").unwrap_or_default().count();
}

#[test]
fn allow_invalid_utf8_external_subcommand_values_of_os() {
    let a = Command::new("test").allow_external_subcommands(true);
    let m = a.try_get_matches_from(vec!["test", "cmd", "arg"]).unwrap();
    let (_ext, args) = m.subcommand().unwrap();
    args.get_many::<OsString>("").unwrap_or_default().count();
}

#[test]
#[should_panic = "Mismatch between definition and access of ``. Could not downcast to alloc::string::String, need to downcast to std::ffi::os_str::OsString"]
fn panic_invalid_utf8_external_subcommand_values_of() {
    let a = Command::new("test").allow_external_subcommands(true);
    let m = a.try_get_matches_from(vec!["test", "cmd", "arg"]).unwrap();
    let (_ext, args) = m.subcommand().unwrap();
    args.get_many::<String>("").unwrap_or_default().count();
}
<commit_msg>test(parser): Show UTF8 bug<commit_after>#![cfg(not(windows))]

use clap::{arg, error::ErrorKind, value_parser, Arg, ArgAction, Command};
use std::ffi::OsString;
use std::os::unix::ffi::OsStringExt;

#[test]
fn invalid_utf8_strict_positional() {
    let m = Command::new("bad_utf8")
        .arg(Arg::new("arg"))
        .try_get_matches_from(vec![OsString::from(""), OsString::from_vec(vec![0xe9])]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_option_short_space() {
    let m = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("-a"),
            OsString::from_vec(vec![0xe9]),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_option_short_equals() {
    let m = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x61, 0x3d, 0xe9]),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_option_short_no_space() {
    let m = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x61, 0xe9]),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_option_long_space() {
    let m = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("--arg"),
            OsString::from_vec(vec![0xe9]),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_option_long_equals() {
    let m = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x2d, 0x61, 0x72, 0x67, 0x3d, 0xe9]),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn invalid_utf8_strict_invalid_short() {
    let m = Command::new("bad_utf8").try_get_matches_from(vec![
        OsString::from(""),
        OsString::from("-a"),
        OsString::from_vec(vec![0xe9]),
    ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument);
}

#[test]
#[should_panic]
fn invalid_utf8_strict_invalid_long() {
    let m = Command::new("bad_utf8").try_get_matches_from(vec![
        OsString::from(""),
        OsString::from("--arg"),
        OsString::from_vec(vec![0xe9]),
    ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument);
}

#[test]
fn invalid_utf8_positional() {
    let r = Command::new("bad_utf8")
        .arg(Arg::new("arg").value_parser(value_parser!(OsString)))
        .try_get_matches_from(vec![OsString::from(""), OsString::from_vec(vec![0xe9])]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn invalid_utf8_option_short_space() {
    let r = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set)
                .value_parser(value_parser!(OsString)),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("-a"),
            OsString::from_vec(vec![0xe9]),
        ]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn invalid_utf8_option_short_equals() {
    let r = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set)
                .value_parser(value_parser!(OsString)),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x61, 0x3d, 0xe9]),
        ]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn invalid_utf8_option_short_no_space() {
    let r = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set)
                .value_parser(value_parser!(OsString)),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x61, 0xe9]),
        ]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn invalid_utf8_option_long_space() {
    let r = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set)
                .value_parser(value_parser!(OsString)),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("--arg"),
            OsString::from_vec(vec![0xe9]),
        ]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn invalid_utf8_option_long_equals() {
    let r = Command::new("bad_utf8")
        .arg(
            Arg::new("arg")
                .short('a')
                .long("arg")
                .action(ArgAction::Set)
                .value_parser(value_parser!(OsString)),
        )
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0x2d, 0x2d, 0x61, 0x72, 0x67, 0x3d, 0xe9]),
        ]);
    assert!(r.is_ok(), "{}", r.unwrap_err());
    let m = r.unwrap();
    assert!(m.contains_id("arg"));
    assert_eq!(
        m.get_one::<OsString>("arg").unwrap(),
        &*OsString::from_vec(vec![0xe9])
    );
}

#[test]
fn refuse_invalid_utf8_subcommand_with_allow_external_subcommands() {
    let m = Command::new("bad_utf8")
        .allow_external_subcommands(true)
        .external_subcommand_value_parser(value_parser!(String))
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0xe9]),
            OsString::from("normal"),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn refuse_invalid_utf8_subcommand_when_args_are_allowed_with_allow_external_subcommands() {
    let m = Command::new("bad_utf8")
        .allow_external_subcommands(true)
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from_vec(vec![0xe9]),
            OsString::from("normal"),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn refuse_invalid_utf8_subcommand_args_with_allow_external_subcommands() {
    let m = Command::new("bad_utf8")
        .allow_external_subcommands(true)
        .external_subcommand_value_parser(value_parser!(String))
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("subcommand"),
            OsString::from("normal"),
            OsString::from_vec(vec![0xe9]),
            OsString::from("--another_normal"),
        ]);
    assert!(m.is_err());
    assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidUtf8);
}

#[test]
fn allow_invalid_utf8_subcommand_args_with_allow_external_subcommands() {
    let m = Command::new("bad_utf8")
        .allow_external_subcommands(true)
        .try_get_matches_from(vec![
            OsString::from(""),
            OsString::from("subcommand"),
            OsString::from("normal"),
            OsString::from_vec(vec![0xe9]),
            OsString::from("--another_normal"),
        ]);
    assert!(m.is_ok(), "{}", m.unwrap_err());
    let m = m.unwrap();
    let (subcommand, args) = m.subcommand().unwrap();
    let args = args
        .get_many::<OsString>("")
        .unwrap()
        .cloned()
        .collect::<Vec<_>>();
    assert_eq!(subcommand, OsString::from("subcommand"));
    assert_eq!(
        args,
        vec![
            OsString::from("normal"),
            OsString::from_vec(vec![0xe9]),
            OsString::from("--another_normal"),
        ]
    );
}

#[test]
fn allow_validated_utf8_value_of() {
    let a = Command::new("test").arg(arg!(--name <NAME>));
    let m = a.try_get_matches_from(["test", "--name", "me"]).unwrap();
    let _ = m.get_one::<String>("name").map(|v| v.as_str());
}

#[test]
fn allow_validated_utf8_external_subcommand_values_of() {
    let a = Command::new("test")
        .allow_external_subcommands(true)
        .external_subcommand_value_parser(value_parser!(String));
    let m = a.try_get_matches_from(vec!["test", "cmd", "arg"]).unwrap();
    let (_ext, args) = m.subcommand().unwrap();
    args.get_many::<String>("").unwrap_or_default().count();
}

#[test]
#[should_panic = "Mismatch between definition and access of ``. Could not downcast to std::ffi::os_str::OsString, need to downcast to alloc::string::String"]
fn panic_validated_utf8_external_subcommand_values_of_os() {
    let a = Command::new("test")
        .allow_external_subcommands(true)
        .external_subcommand_value_parser(value_parser!(String));
    let m = a.try_get_matches_from(vec!["test", "cmd", "arg"]).unwrap();
    let (_ext, args) = m.subcommand().unwrap();
    args.get_many::<OsString>("").unwrap_or_default().count();
}

#[test]
fn allow_invalid_utf8_external_subcommand_values_of_os() {
    let a = Command::new("test").allow_external_subcommands(true);
    let m = a.try_get_matches_from(vec!["test", "cmd", "arg"]).unwrap();
    let (_ext, args) = m.subcommand().unwrap();
    args.get_many::<OsString>("").unwrap_or_default().count();
}

#[test]
#[should_panic = "Mismatch between definition and access of ``. Could not downcast to alloc::string::String, need to downcast to std::ffi::os_str::OsString"]
fn panic_invalid_utf8_external_subcommand_values_of() {
    let a = Command::new("test").allow_external_subcommands(true);
    let m = a.try_get_matches_from(vec!["test", "cmd", "arg"]).unwrap();
    let (_ext, args) = m.subcommand().unwrap();
    args.get_many::<String>("").unwrap_or_default().count();
}
<|endoftext|>