chromium/mojo/public/rust/system/test_support/lib.rs

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Utilities to support testing Mojo clients and the Mojo system implementation
//! itself.

chromium::import! {
    "//mojo/public/rust:mojo_system" as system;
}

macro_rules! gen_panic_stub {
    ($name:ident $(, $arg:ident : $arg_ty:ty)*) => {
        pub extern "C" fn $name ($($arg : $arg_ty),*) -> MojoResultCode {
            unimplemented!(concat!("test stub not implemented for ", stringify!($name)))
        }
    }
}

/// Define safe but non-functional stubs for Mojo calls. These provide safe
/// behavior for tests that hold Mojo handles but don't use them for anything
/// real. Currently, only freeing handles does anything, and all other stubs
/// simply panic. Tests are free to use any integer for a handle when using this
/// implementation.
mod stubs {
    #![allow(non_snake_case)]
    #![allow(unused_variables)]

    use system::ffi_for_testing::raw_ffi::*;
    use system::ffi_for_testing::{c_void, MojoResultCode};

    gen_panic_stub!(
        AddTrigger,
        trap_handle: MojoHandle,
        handle: MojoHandle,
        signals: MojoHandleSignals,
        condition: MojoTriggerCondition,
        context: usize,
        options: *const MojoAddTriggerOptions
    );
    gen_panic_stub!(
        AppendMessageData,
        message: MojoMessageHandle,
        additional_payload_size: u32,
        handles: *const MojoHandle,
        num_handles: u32,
        options: *const MojoAppendMessageDataOptions,
        buffer: *mut *mut c_void,
        buffer_size: *mut u32
    );
    gen_panic_stub!(
        ArmTrap,
        trap_handle: MojoHandle,
        options: *const MojoArmTrapOptions,
        num_blocking_events: *mut u32,
        blocking_events: *mut MojoTrapEvent
    );
    gen_panic_stub!(
        BeginReadData,
        handle: MojoHandle,
        options: *const MojoBeginReadDataOptions,
        buffer: *mut *const c_void,
        elements: *mut u32
    );
    gen_panic_stub!(
        BeginWriteData,
        handle: MojoHandle,
        options: *const MojoBeginWriteDataOptions,
        buffer: *mut *mut c_void,
        elements: *mut u32
    );
    gen_panic_stub!(Close, handle: MojoHandle);
    gen_panic_stub!(
        CreateDataPipe,
        options: *const MojoCreateDataPipeOptions,
        handle1: *mut MojoHandle,
        handle2: *mut MojoHandle
    );
    gen_panic_stub!(
        CreateMessage,
        options: *const MojoCreateMessageOptions,
        message: *mut MojoMessageHandle
    );
    gen_panic_stub!(
        CreateMessagePipe,
        options: *const MojoCreateMessagePipeOptions,
        handle1: *mut MojoHandle,
        handle2: *mut MojoHandle
    );
    gen_panic_stub!(
        CreateSharedBuffer,
        num_bytes: u64,
        options: *const MojoCreateSharedBufferOptions,
        handle: *mut MojoHandle
    );
    gen_panic_stub!(
        CreateTrap,
        handler: MojoTrapEventHandler,
        options: *const MojoCreateTrapOptions,
        handle: *mut MojoHandle
    );
    gen_panic_stub!(DestroyMessage, handle: MojoMessageHandle);
    gen_panic_stub!(
        DuplicateBufferHandle,
        handle: MojoHandle,
        options: *const MojoDuplicateBufferHandleOptions,
        new_handle: *mut MojoHandle
    );
    gen_panic_stub!(
        EndReadData,
        handle: MojoHandle,
        elements: u32,
        options: *const MojoEndReadDataOptions
    );
    gen_panic_stub!(
        EndWriteData,
        handle: MojoHandle,
        elements: u32,
        options: *const MojoEndWriteDataOptions
    );
    gen_panic_stub!(
        GetBufferInfo,
        handle: MojoHandle,
        options: *const MojoGetBufferInfoOptions,
        info: *mut MojoSharedBufferInfo
    );
    gen_panic_stub!(
        GetMessageData,
        handle: MojoMessageHandle,
        options: *const MojoGetMessageDataOptions,
        buffer: *mut *mut c_void,
        num_bytes: *mut u32,
        handles: *mut MojoHandle,
        num_handles: *mut u32
    );
    gen_panic_stub!(
        MapBuffer,
        handle: MojoHandle,
        offset: u64,
        bytes: u64,
        options: *const MojoMapBufferOptions,
        buffer: *mut *mut c_void
    );
    gen_panic_stub!(
        QueryHandleSignalsState,
        handle: MojoHandle,
        signals_state: *mut MojoHandleSignalsState
    );
    gen_panic_stub!(
        ReadData,
        handle: MojoHandle,
        options: *const MojoReadDataOptions,
        elements: *mut c_void,
        num_elements: *mut u32
    );
    gen_panic_stub!(
        ReadMessage,
        handle: MojoHandle,
        options: *const MojoReadMessageOptions,
        message: *mut MojoMessageHandle
    );
    gen_panic_stub!(
        RemoveTrigger,
        handle: MojoHandle,
        context: usize,
        options: *const MojoRemoveTriggerOptions
    );
    gen_panic_stub!(UnmapBuffer, buffer: *mut c_void);
    gen_panic_stub!(
        WriteData,
        handle: MojoHandle,
        elements: *const c_void,
        num_elements: *mut u32,
        options: *const MojoWriteDataOptions
    );
    gen_panic_stub!(
        WriteMessage,
        handle: MojoHandle,
        message: MojoMessageHandle,
        options: *const MojoWriteMessageOptions
    );

    pub extern "C" fn GetTimeTicksNow() -> MojoTimeTicks {
        0
    }
}

/// Instead of the Mojo core implementation, use non-functional stubs for API
/// calls.
///
/// # Safety
///
/// This may only be called once. Mojo cannot be initialized before or after in
/// the same process, ever.
pub unsafe fn set_stub_thunks() {
    let mut thunks: system::ffi_for_testing::MojoSystemThunks2 =
        unsafe { std::mem::MaybeUninit::zeroed().assume_init() };

    macro_rules! set_thunks {
        ($($name:ident),+ $(,)?) => { $(thunks.$name = Some(stubs::$name));* }
    }

    set_thunks!(
        AddTrigger,
        AppendMessageData,
        ArmTrap,
        BeginReadData,
        BeginWriteData,
        Close,
        CreateDataPipe,
        CreateMessage,
        CreateMessagePipe,
        CreateSharedBuffer,
        CreateTrap,
        DestroyMessage,
        DuplicateBufferHandle,
        EndReadData,
        EndWriteData,
        GetBufferInfo,
        GetMessageData,
        MapBuffer,
        QueryHandleSignalsState,
        ReadData,
        ReadMessage,
        RemoveTrigger,
        UnmapBuffer,
        WriteData,
        WriteMessage,
        GetTimeTicksNow,
    );

    thunks.size = std::mem::size_of_val(&thunks) as u32;

    unsafe {
        set_thunks(thunks);
    }
}

/// Set custom thunks to back Mojo API calls. Mojo core cannot be initialized
/// before or after this call, so it precludes use in any process that will at
/// some point need to use real Mojo functionality. Ideally, this would never be
/// used and downstream code would be able to write tests that don't need a fake
/// Mojo implementation, but here we are.
///
/// # Safety
///
/// Must be called no more than once, including the underlying functions from
/// outside Rust. Mojo cannot be initialized in the same process before or after
/// this call.
pub unsafe fn set_thunks(thunks: system::ffi_for_testing::MojoSystemThunks2) {
    unsafe {
        system::ffi_for_testing::MojoEmbedderSetSystemThunks(&thunks as *const _);
    }
}