Skip to content

Commit

Permalink
Use stubbed Mojo API in several Rust tests
Browse files Browse the repository at this point in the history
Generally, it may be useful to mock out the Mojo API. Often tests
don't need to perform real IPC, instead they just want to check that
abstractions call the right Mojo calls.

Specifically, a large chunk of the tests in this suite are deeply
unsound. They construct the Rust Mojo handle abstraction from integers
and pass them to other abstractions that require the handle be valid
and non-nil. Fortunately these tests do not perform any real Mojo
calls: they simply check that encoded Mojo messages parse into structs
correctly and vice versa.

Ideally we'd leverage the generic system and refactor the encoding
implementation to not actually require real Mojo handles. This is
difficult with the existing unsoundness so this CL stubs out the Mojo
API to make calls do nothing.

Bug: 1274864
Change-Id: I0c4bbfe449dcd13c63b0053da8d4b7258c248399

Cq-Include-Trybots: luci.chromium.try:android-rust-arm64-dbg,android-rust-arm32-rel,android-rust-arm64-rel,linux-rust-x64-dbg,linux-rust-x64-rel
Change-Id: I0c4bbfe449dcd13c63b0053da8d4b7258c248399
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4335903
Reviewed-by: Ken Rockot <rockot@google.com>
Reviewed-by: danakj <danakj@chromium.org>
Commit-Queue: Collin Baker <collinbaker@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1117735}
  • Loading branch information
chbaker0 authored and Chromium LUCI CQ committed Mar 15, 2023
1 parent e9957c9 commit 6aa09ce
Show file tree
Hide file tree
Showing 15 changed files with 417 additions and 82 deletions.
4 changes: 2 additions & 2 deletions mojo/public/c/system/thunks.cc
Expand Up @@ -858,8 +858,6 @@ MojoSystemThunks32 g_thunks_32 = {
MojoSetDefaultProcessErrorHandler,
};

} // extern "C"

const MojoSystemThunks2* MojoEmbedderGetSystemThunks2() {
return &g_thunks;
}
Expand All @@ -881,3 +879,5 @@ void MojoEmbedderSetSystemThunks(const MojoSystemThunks2* thunks) {

g_thunks = *thunks;
}

} // extern "C"
8 changes: 8 additions & 0 deletions mojo/public/c/system/thunks.h
Expand Up @@ -441,6 +441,10 @@ struct MojoSystemThunks {

typedef struct MojoSystemThunks MojoSystemThunks32;

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

MOJO_SYSTEM_EXPORT const struct MojoSystemThunks2*
MojoEmbedderGetSystemThunks2();

Expand All @@ -449,4 +453,8 @@ MOJO_SYSTEM_EXPORT const MojoSystemThunks32* MojoEmbedderGetSystemThunks32();
MOJO_SYSTEM_EXPORT void MojoEmbedderSetSystemThunks(
const struct MojoSystemThunks2* system_thunks);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

#endif // MOJO_PUBLIC_C_SYSTEM_THUNKS_H_
17 changes: 17 additions & 0 deletions mojo/public/rust/BUILD.gn
Expand Up @@ -18,6 +18,7 @@ rust_unit_tests_group("mojo_rust_tests") {
deps = []
if (can_build_rust_unit_tests) {
deps += [
"//mojo/public/rust/tests:mojo_rust_encoding_tests",
"//mojo/public/rust/tests:mojo_rust_integration_tests",
"//mojo/public/rust/tests:mojo_rust_system_tests",
]
Expand Down Expand Up @@ -68,6 +69,22 @@ rust_static_library("mojo_rust_system") {
rustenv = [ "BINDGEN_RS_FILE=" + rebase_path(bindgen_output[0]) ]
}

rust_static_library("mojo_rust_system_test_support") {
testonly = true
crate_root = "system/test_support/lib.rs"
crate_name = "mojo_system_test_support"

# Calls Mojo FFI functions.
allow_unsafe = true

sources = [ "system/test_support/lib.rs" ]

deps = [
":mojo_rust_system",
"//mojo/public/c/system",
]
}

rust_static_library("mojo_rust_bindings") {
crate_root = "bindings/lib.rs"
crate_name = "mojo_bindings"
Expand Down
9 changes: 8 additions & 1 deletion mojo/public/rust/system/ffi.rs
Expand Up @@ -21,6 +21,7 @@ pub mod raw_ffi {
#![allow(dead_code)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(env!("BINDGEN_RS_FILE"));
}

Expand Down Expand Up @@ -61,7 +62,8 @@ pub mod types {
pub type MojoResultCode = raw_ffi::MojoResult;
}

use crate::ffi::types::*;
pub use types::MojoResultCode;
use types::*;

#[allow(non_camel_case_types)]
pub type c_void = std::ffi::c_void;
Expand Down Expand Up @@ -178,3 +180,8 @@ pub use raw_ffi::MojoRemoveTrigger;
pub use raw_ffi::MojoUnmapBuffer;
pub use raw_ffi::MojoWriteData;
pub use raw_ffi::MojoWriteMessage;

/// Exposed for tests only. Note that calling this function means the Mojo
/// embedder target must be linked in.
pub use raw_ffi::MojoEmbedderSetSystemThunks;
pub use raw_ffi::MojoSystemThunks2;
8 changes: 7 additions & 1 deletion mojo/public/rust/system/lib.rs
Expand Up @@ -6,7 +6,6 @@
// Require unsafe blocks for unsafe operations even in an unsafe fn.
#![deny(unsafe_op_in_unsafe_fn)]

mod ffi;
mod handle;
mod mojo_types;

Expand All @@ -18,6 +17,13 @@ pub mod trap;
pub mod wait;
pub mod wait_set;

/// Export publicly for tests, but use a different name. This is awkward since
/// we can't have private `mod ffi` then re-export it publicly under a different
/// name.
#[path = "ffi.rs"]
pub mod ffi_for_testing;
use ffi_for_testing as ffi;

/// Provides extra utilities that don't directly wrap Mojo APIs, but build on
/// top of them and may be generally useful.
pub mod util {
Expand Down
252 changes: 252 additions & 0 deletions mojo/public/rust/system/test_support/lib.rs
@@ -0,0 +1,252 @@
// 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.

extern crate 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 _);
}
}
1 change: 1 addition & 0 deletions mojo/public/rust/system/wrapper.h
Expand Up @@ -6,5 +6,6 @@
#define MOJO_PUBLIC_RUST_SYSTEM_WRAPPER_H_

#include "mojo/public/c/system/core.h"
#include "mojo/public/c/system/thunks.h"

#endif // MOJO_PUBLIC_RUST_SYSTEM_WRAPPER_H_

0 comments on commit 6aa09ce

Please sign in to comment.