Enable rustfmt comment wrapping and reformat Rust code
Bug: None Change-Id: I10995152a7098eb9c13c0e7aa05f05b580bf513f Cq-Include-Trybots: luci.chromium.try:android-rust-arm-dbg,android-rust-arm-rel,linux-rust-x64-dbg,linux-rust-x64-rel Change-Id: I10995152a7098eb9c13c0e7aa05f05b580bf513f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4060461 Auto-Submit: Collin Baker <collinbaker@chromium.org> Commit-Queue: danakj <danakj@chromium.org> Commit-Queue: Collin Baker <collinbaker@chromium.org> Reviewed-by: danakj <danakj@chromium.org> Cr-Commit-Position: refs/heads/main@{#1077040}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
1db504024d
commit
dbf27b48b8
.rustfmt.toml
build/rust/tests/test_rlib_crate/crate/src
mojo/public/rust
bindings
system
tests
testing/rust_gtest_interop
third_party/rust/serde_json_lenient/v0_1/wrapper
tools/crates/gnrt
@ -10,6 +10,8 @@ version = "Two"
|
||||
# Line endings will be converted to \n.
|
||||
newline_style = "Unix"
|
||||
|
||||
wrap_comments = true
|
||||
|
||||
# The "Default" setting has a heuristic which splits lines too aggresively.
|
||||
# We are willing to revisit this setting in future versions of rustfmt.
|
||||
# Bugs:
|
||||
|
@ -28,8 +28,8 @@ pub fn say_hello_from_crate() {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
/// Test features are passed through from BUILD.gn correctly. This test is the target1
|
||||
/// configuration.
|
||||
/// Test features are passed through from BUILD.gn correctly. This test is
|
||||
/// the target1 configuration.
|
||||
#[test]
|
||||
#[cfg(test_a_and_b)]
|
||||
fn test_features_passed_target1() {
|
||||
|
@ -195,9 +195,10 @@ impl<'slice> DecodingState<'slice> {
|
||||
|
||||
/// Decode a pointer from the buffer as a global offset into the buffer.
|
||||
///
|
||||
/// The pointer in the buffer is an offset relative to the pointer to another
|
||||
/// location in the buffer. We convert that to an absolute offset with respect
|
||||
/// to the buffer before returning. This is our defintion of a pointer.
|
||||
/// The pointer in the buffer is an offset relative to the pointer to
|
||||
/// another location in the buffer. We convert that to an absolute
|
||||
/// offset with respect to the buffer before returning. This is our
|
||||
/// defintion of a pointer.
|
||||
pub fn decode_pointer(&mut self) -> Option<u64> {
|
||||
self.align_to_byte();
|
||||
self.align_to_bytes(8);
|
||||
@ -279,8 +280,9 @@ impl<'slice> DecodingState<'slice> {
|
||||
let len = versions.len();
|
||||
let (latest_version, _) = versions[len - 1];
|
||||
let (_, size) = versions[idx - 1];
|
||||
// If this is higher than any version we know, its okay for the size to be bigger,
|
||||
// but if its a version we know about, it must match the size.
|
||||
// If this is higher than any version we know, its okay for the size to be
|
||||
// bigger, but if its a version we know about, it must match the
|
||||
// size.
|
||||
if (version > latest_version && bytes < size)
|
||||
|| (version <= latest_version && bytes != size)
|
||||
{
|
||||
@ -374,7 +376,8 @@ impl<'slice> Decoder<'slice> {
|
||||
} else {
|
||||
return Err(ValidationError::UnexpectedInvalidHandle);
|
||||
};
|
||||
// If the index exceeds our number of handles or if we have already claimed that handle
|
||||
// If the index exceeds our number of handles or if we have already claimed that
|
||||
// handle
|
||||
if real_index >= self.handles.len() || real_index < self.handles_claimed {
|
||||
return Err(ValidationError::IllegalHandle);
|
||||
}
|
||||
|
@ -88,7 +88,8 @@ pub trait MojomNumeric:
|
||||
+ PartialEq<Self>
|
||||
+ Default
|
||||
{
|
||||
/// Converts the primitive to a little-endian representation (the mojom endianness).
|
||||
/// Converts the primitive to a little-endian representation (the mojom
|
||||
/// endianness).
|
||||
fn to_mojom_endian(self) -> Self;
|
||||
}
|
||||
|
||||
@ -389,8 +390,8 @@ impl<'slice> Encoder<'slice> {
|
||||
&mut self.states[context.id()]
|
||||
}
|
||||
|
||||
/// Signal to finish encoding by destroying the Encoder and returning the final
|
||||
/// handle vector.
|
||||
/// Signal to finish encoding by destroying the Encoder and returning the
|
||||
/// final handle vector.
|
||||
///
|
||||
/// Note: No byte buffer is returned as that is pre-allocated.
|
||||
pub fn unwrap(self) -> Vec<UntypedHandle> {
|
||||
|
@ -162,7 +162,8 @@ pub trait MojomUnion: MojomEncodable {
|
||||
Bits(8 * (UNION_SIZE as usize))
|
||||
}
|
||||
|
||||
/// The encoding routine for when the union is inlined into the current context.
|
||||
/// The encoding routine for when the union is inlined into the current
|
||||
/// context.
|
||||
fn inline_encode(self, encoder: &mut Encoder, context: Context) {
|
||||
{
|
||||
let state = encoder.get_mut(&context);
|
||||
@ -178,7 +179,8 @@ pub trait MojomUnion: MojomEncodable {
|
||||
}
|
||||
}
|
||||
|
||||
/// The decoding routine for when the union is inlined into the current context.
|
||||
/// The decoding routine for when the union is inlined into the current
|
||||
/// context.
|
||||
fn inline_decode(decoder: &mut Decoder, context: Context) -> Result<Self, ValidationError> {
|
||||
{
|
||||
let state = decoder.get_mut(&context);
|
||||
@ -341,9 +343,9 @@ pub trait MojomMessage: MojomStruct {
|
||||
|
||||
/// The trait for a "container" type intended to be used in MojomInterfaceRecv.
|
||||
///
|
||||
/// This trait contains the decode logic which decodes based on the message header
|
||||
/// and returns itself: a union type which may contain any of the possible messages
|
||||
/// that may be sent across this interface.
|
||||
/// This trait contains the decode logic which decodes based on the message
|
||||
/// header and returns itself: a union type which may contain any of the
|
||||
/// possible messages that may be sent across this interface.
|
||||
pub trait MojomMessageOption: Sized {
|
||||
/// Decodes the actual payload of the message.
|
||||
///
|
||||
|
@ -34,8 +34,8 @@ use crate::system::{Handle, MojoResult, MOJO_INDEFINITE};
|
||||
/// Define the equivalent of MOJO_INDEFINITE for absolute deadlines
|
||||
const MOJO_INDEFINITE_ABSOLUTE: system::MojoTimeTicks = 0;
|
||||
|
||||
// TODO(mknyszek): The numbers below are arbitrary and come from the C++ bindings,
|
||||
// and should probably be changed at some point
|
||||
// TODO(mknyszek): The numbers below are arbitrary and come from the C++
|
||||
// bindings, and should probably be changed at some point
|
||||
|
||||
/// Initial size of the result buffer.
|
||||
const INITIAL_WAIT_SET_NUM_RESULTS: usize = 16;
|
||||
@ -358,9 +358,9 @@ impl<'h, 't> RunLoop<'h, 't> {
|
||||
|
||||
/// Removes an entry from the runloop.
|
||||
///
|
||||
/// Since we cannot remove from the deadlines heap, we just leave the deadline
|
||||
/// in there as "stale", and we handle those when trying to find the next closest
|
||||
/// deadline.
|
||||
/// Since we cannot remove from the deadlines heap, we just leave the
|
||||
/// deadline in there as "stale", and we handle those when trying to
|
||||
/// find the next closest deadline.
|
||||
pub fn deregister(&mut self, token: Token) -> bool {
|
||||
match self.handlers.remove(&token) {
|
||||
Some(_) => {
|
||||
@ -576,7 +576,8 @@ impl<'h, 't> RunLoop<'h, 't> {
|
||||
// Clear the buffer since we don't need the results anymore.
|
||||
// Helps prevent a copy if we resize the buffer.
|
||||
results_buffer.clear();
|
||||
// If we reached the capcity of `results_buffer` there's a chance there were more handles available. Grow the buffer.
|
||||
// If we reached the capcity of `results_buffer` there's a chance there were
|
||||
// more handles available. Grow the buffer.
|
||||
let capacity = results_buffer.capacity();
|
||||
if capacity < MAXIMUM_WAIT_SET_NUM_RESULTS && capacity == num_results {
|
||||
results_buffer.reserve(capacity);
|
||||
|
@ -87,8 +87,8 @@ impl<'b, 'p, T> ReadDataBuffer<'b, 'p, T> {
|
||||
|
||||
if result != MojoResult::Okay {
|
||||
// The Mojo function can return two possible errors:
|
||||
// * MojoResult::InvalidArgument indicating either the handle is
|
||||
// invalid or elems_read is larger than self.buffer.
|
||||
// * MojoResult::InvalidArgument indicating either the handle is invalid or
|
||||
// elems_read is larger than self.buffer.
|
||||
// * MojoResult::FailedPrecondition if not in a two-phase read.
|
||||
//
|
||||
// Either indicates a bug in the wrapper code and other errors are
|
||||
@ -171,8 +171,8 @@ impl<'b, 'p, T> WriteDataBuffer<'b, 'p, T> {
|
||||
});
|
||||
if result != MojoResult::Okay {
|
||||
// The Mojo function can return two possible errors:
|
||||
// * MojoResult::InvalidArgument indicating either the handle is
|
||||
// invalid or elems_written is larger than self.buffer.
|
||||
// * MojoResult::InvalidArgument indicating either the handle is invalid or
|
||||
// elems_written is larger than self.buffer.
|
||||
// * MojoResult::FailedPrecondition if not in a two-phase read.
|
||||
//
|
||||
// Either indicates a bug in the wrapper code and other errors are
|
||||
|
@ -16,7 +16,8 @@
|
||||
use crate::system::ffi;
|
||||
use crate::system::wait::*;
|
||||
|
||||
// This full import is intentional; nearly every type in mojo_types needs to be used.
|
||||
// This full import is intentional; nearly every type in mojo_types needs to be
|
||||
// used.
|
||||
use crate::system::mojo_types::*;
|
||||
|
||||
/// The CastHandle trait defines an interface to convert between
|
||||
@ -52,7 +53,8 @@ pub trait Handle {
|
||||
wait(self.get_native_handle(), signals)
|
||||
}
|
||||
|
||||
/// Gets the last known signals state of the handle. The state may change at any time during or after this call.
|
||||
/// Gets the last known signals state of the handle. The state may change at
|
||||
/// any time during or after this call.
|
||||
fn query_signals_state(&self) -> Result<SignalsState, MojoResult> {
|
||||
let mut state: SignalsState = Default::default();
|
||||
let r = MojoResult::from_code(unsafe {
|
||||
|
@ -10,7 +10,8 @@ use std::convert::TryInto;
|
||||
use crate::system::ffi;
|
||||
use crate::system::handle;
|
||||
use crate::system::handle::{CastHandle, Handle};
|
||||
// This full import is intentional; nearly every type in mojo_types needs to be used.
|
||||
// This full import is intentional; nearly every type in mojo_types needs to be
|
||||
// used.
|
||||
use crate::system::mojo_types::*;
|
||||
|
||||
use ffi::c_void;
|
||||
@ -137,9 +138,10 @@ impl MessageEndpoint {
|
||||
assert_ne!(buffer, ptr::null_mut());
|
||||
// Will not panic if usize has at least 32 bits, which is true for our targets
|
||||
let buffer_size: usize = num_bytes.try_into().unwrap();
|
||||
// MojoGetMessageData points us to the data with a c_void pointer and a length. This
|
||||
// is only available until we destroy the message. We want to copy this into our own
|
||||
// Vec. Read the buffer as a slice, which is safe.
|
||||
// MojoGetMessageData points us to the data with a c_void pointer and a length.
|
||||
// This is only available until we destroy the message. We want to
|
||||
// copy this into our own Vec. Read the buffer as a slice, which is
|
||||
// safe.
|
||||
unsafe {
|
||||
let buffer_slice = std::slice::from_raw_parts(buffer.cast(), buffer_size);
|
||||
buffer_slice.to_vec()
|
||||
@ -211,9 +213,10 @@ impl MessageEndpoint {
|
||||
let buffer_size: usize = buffer_size.try_into().unwrap();
|
||||
assert!(bytes.len() <= buffer_size);
|
||||
assert_ne!(buffer_ptr, ptr::null_mut());
|
||||
// MojoAppendMessageData tells us where to write with a c_void pointer and a length.
|
||||
// This is only available until we destroy or send the message. We can view this
|
||||
// through a slice and copy our `bytes` into it.
|
||||
// MojoAppendMessageData tells us where to write with a c_void pointer and a
|
||||
// length. This is only available until we destroy or send the
|
||||
// message. We can view this through a slice and copy our `bytes`
|
||||
// into it.
|
||||
unsafe {
|
||||
// We know `bytes.len() <= buffer_size`, and `buffer_size` is the limit of the
|
||||
// provided buffer.
|
||||
|
@ -6,7 +6,8 @@ use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use crate::system::ffi;
|
||||
// This full import is intentional; nearly every type in mojo_types needs to be used.
|
||||
// This full import is intentional; nearly every type in mojo_types needs to be
|
||||
// used.
|
||||
use crate::system::handle;
|
||||
use crate::system::handle::{CastHandle, Handle};
|
||||
use crate::system::mojo_types::*;
|
||||
|
@ -102,10 +102,10 @@ impl UnsafeTrap {
|
||||
let mut handle = UntypedHandle::invalid();
|
||||
let result = unsafe {
|
||||
// SAFETY:
|
||||
// * MojoCreateTrap is given a valid function pointer (type checked
|
||||
// thanks to bindgen)
|
||||
// * `handle`'s pointer cast is OK since `UntypedHandle` is
|
||||
// repr(transparent) for MojoHandle
|
||||
// * MojoCreateTrap is given a valid function pointer (type checked thanks to
|
||||
// bindgen)
|
||||
// * `handle`'s pointer cast is OK since `UntypedHandle` is repr(transparent)
|
||||
// for MojoHandle
|
||||
MojoResult::from_code(ffi::MojoCreateTrap(
|
||||
Some(handler_ptr),
|
||||
ffi::MojoCreateTrapOptions::new(0).inner_ptr(),
|
||||
@ -336,8 +336,10 @@ mod asserts {
|
||||
|
||||
impl<Context, EventHandler> Trap<Context, EventHandler>
|
||||
where
|
||||
// Context: Sync because it is shared by reference through `raw_handler`
|
||||
// calls (from any thread) and Send because it is transitively owned by a
|
||||
// Context: Sync because it is shared by
|
||||
// reference through `raw_handler`
|
||||
// calls (from any thread) and Send because
|
||||
// it is transitively owned by a
|
||||
// Mutex that must be Sync.
|
||||
Context: Send + Sync,
|
||||
// EventHandler: Send because it is shared by value through `raw_handler`
|
||||
|
@ -33,10 +33,10 @@ use crate::util::mojom_validation::*;
|
||||
/// 6. Decode this re-encoded payload.
|
||||
/// 7. Verify the re-decoded payload is what we expect.
|
||||
///
|
||||
/// Each test should sufficiently verify the operation of the encoding and decoding
|
||||
/// frameworks as we first verify the decoder works correctly on the "golden files",
|
||||
/// then verify that the encoder works by encoding the decoded output, and decoding
|
||||
/// that once again.
|
||||
/// Each test should sufficiently verify the operation of the encoding and
|
||||
/// decoding frameworks as we first verify the decoder works correctly on the
|
||||
/// "golden files", then verify that the encoder works by encoding the decoded
|
||||
/// output, and decoding that once again.
|
||||
macro_rules! encoding_tests {
|
||||
($($name:ident { MessageHeader => $header_cls:expr, $req_type:ident => $cls:expr } )*) => {
|
||||
tests! {
|
||||
|
@ -22,7 +22,6 @@ use crate::util::mojom_validation::*;
|
||||
/// 1. Decode the header of the validation input.
|
||||
/// 2. Decode the payload of the validation input, expecting a validation
|
||||
/// error.
|
||||
///
|
||||
macro_rules! validation_tests {
|
||||
($($name:ident => $req_type:ident;)*) => {
|
||||
tests! {
|
||||
|
@ -91,8 +91,9 @@ macro_rules! expect_false {
|
||||
};
|
||||
}
|
||||
|
||||
/// Evaluates and compares the two given expressions. If not equal, a failure is reported to Gtest.
|
||||
/// The expressions must evaluate to the same type, which must be PartialEq.
|
||||
/// Evaluates and compares the two given expressions. If not equal, a failure is
|
||||
/// reported to Gtest. The expressions must evaluate to the same type, which
|
||||
/// must be PartialEq.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@ -120,8 +121,9 @@ macro_rules! expect_eq {
|
||||
};
|
||||
}
|
||||
|
||||
/// Evaluates and compares the two given expressions. If equal, a failure is reported to Gtest.
|
||||
/// The expressions must evaluate to the same type, which must be PartialEq.
|
||||
/// Evaluates and compares the two given expressions. If equal, a failure is
|
||||
/// reported to Gtest. The expressions must evaluate to the same type, which
|
||||
/// must be PartialEq.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@ -149,9 +151,10 @@ macro_rules! expect_ne {
|
||||
};
|
||||
}
|
||||
|
||||
/// Evaluates and compares the two given expressions. If the first is not greater than the second, a
|
||||
/// failure is reported to Gtest. The expressions must evaluate to the same type, which must be
|
||||
/// PartialOrd. If a PartialOrd comparison fails, the result is false.
|
||||
/// Evaluates and compares the two given expressions. If the first is not
|
||||
/// greater than the second, a failure is reported to Gtest. The expressions
|
||||
/// must evaluate to the same type, which must be PartialOrd. If a PartialOrd
|
||||
/// comparison fails, the result is false.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@ -179,9 +182,10 @@ macro_rules! expect_gt {
|
||||
};
|
||||
}
|
||||
|
||||
/// Evaluates and compares the two given expressions. If the first is not less than the second, a
|
||||
/// failure is reported to Gtest. The expressions must evaluate to the same type, which must be
|
||||
/// PartialOrd. If a PartialOrd comparison fails, the result is false.
|
||||
/// Evaluates and compares the two given expressions. If the first is not less
|
||||
/// than the second, a failure is reported to Gtest. The expressions must
|
||||
/// evaluate to the same type, which must be PartialOrd. If a PartialOrd
|
||||
/// comparison fails, the result is false.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@ -209,9 +213,10 @@ macro_rules! expect_lt {
|
||||
};
|
||||
}
|
||||
|
||||
/// Evaluates and compares the two given expressions. If the first is not greater than or equal to
|
||||
/// the second, a failure is reported to Gtest. The expressions must evaluate to the same type,
|
||||
/// which must be PartialOrd. If a PartialOrd comparison fails, the result is false.
|
||||
/// Evaluates and compares the two given expressions. If the first is not
|
||||
/// greater than or equal to the second, a failure is reported to Gtest. The
|
||||
/// expressions must evaluate to the same type, which must be PartialOrd. If a
|
||||
/// PartialOrd comparison fails, the result is false.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@ -238,9 +243,10 @@ macro_rules! expect_ge {
|
||||
};
|
||||
}
|
||||
|
||||
/// Evaluates and compares the two given expressions. If the first is not less than or equal to the
|
||||
/// second, a failure is reported to Gtest. The expressions must evaluate to the same type, /which
|
||||
/// must be PartialOrd. If a PartialOrd comparison fails, the result is false.
|
||||
/// Evaluates and compares the two given expressions. If the first is not less
|
||||
/// than or equal to the second, a failure is reported to Gtest. The expressions
|
||||
/// must evaluate to the same type, /which must be PartialOrd. If a PartialOrd
|
||||
/// comparison fails, the result is false.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@ -267,16 +273,18 @@ macro_rules! expect_le {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(danakj): There's a bunch more useful macros to write, even ignoring gmock:
|
||||
// TODO(danakj): There's a bunch more useful macros to write, even ignoring
|
||||
// gmock:
|
||||
// - EXPECT_NONFATAL_FAILURE
|
||||
// - SCOPED_TRACE
|
||||
// - EXPECT_DEATH
|
||||
// - FAIL (fatal failure, would this work?)
|
||||
// - ADD_FAILURE
|
||||
// - SUCCEED
|
||||
// - EXPECT_PANIC (catch_unwind() with an error if no panic, but this depends on us using a stdlib
|
||||
// with -Cpanic=unwind in tests, currently we use -Cpanic=abort.)
|
||||
// - EXPECT_NO_PANIC (Like above, depende on -Cpanic=unwind, or else all panics just abort the
|
||||
// process.)
|
||||
// - EXPECT_PANIC (catch_unwind() with an error if no panic, but this depends on
|
||||
// us using a stdlib with -Cpanic=unwind in tests, currently we use
|
||||
// -Cpanic=abort.)
|
||||
// - EXPECT_NO_PANIC (Like above, depende on -Cpanic=unwind, or else all panics
|
||||
// just abort the process.)
|
||||
// - EXPECT_FLOAT_EQ (Comparison for equality within a small range.)
|
||||
// - EXPECT_NEAR (Comparison for equality within a user-specified range.)
|
||||
|
@ -2,20 +2,23 @@ use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// The prefix attached to a Gtest factory function by the RUST_GTEST_TEST_SUITE_FACTORY() macro.
|
||||
/// The prefix attached to a Gtest factory function by the
|
||||
/// RUST_GTEST_TEST_SUITE_FACTORY() macro.
|
||||
const RUST_GTEST_FACTORY_PREFIX: &str = "RustGtestFactory_";
|
||||
|
||||
/// The `gtest` macro can be placed on a function to make it into a Gtest unit test, when linked
|
||||
/// into a C++ binary that invokes Gtest.
|
||||
/// The `gtest` macro can be placed on a function to make it into a Gtest unit
|
||||
/// test, when linked into a C++ binary that invokes Gtest.
|
||||
///
|
||||
/// The `gtest` macro takes two arguments, which are Rust identifiers. The first is the name of the
|
||||
/// test suite and the second is the name of the test, each of which are converted to a string and
|
||||
/// given to Gtest. The name of the test function itself does not matter, and need not be unique
|
||||
/// (it's placed into a unique module based on the Gtest suite + test names.
|
||||
/// The `gtest` macro takes two arguments, which are Rust identifiers. The first
|
||||
/// is the name of the test suite and the second is the name of the test, each
|
||||
/// of which are converted to a string and given to Gtest. The name of the test
|
||||
/// function itself does not matter, and need not be unique (it's placed into a
|
||||
/// unique module based on the Gtest suite + test names.
|
||||
///
|
||||
/// The test function must have no arguments. The return value must be either `()` or
|
||||
/// `std::result::Result<(), E>`. If another return type is found, the test will fail when run. If
|
||||
/// the return type is a `Result`, then an `Err` is treated as a test failure.
|
||||
/// The test function must have no arguments. The return value must be either
|
||||
/// `()` or `std::result::Result<(), E>`. If another return type is found, the
|
||||
/// test will fail when run. If the return type is a `Result`, then an `Err` is
|
||||
/// treated as a test failure.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@ -31,8 +34,8 @@ const RUST_GTEST_FACTORY_PREFIX: &str = "RustGtestFactory_";
|
||||
/// [ OK ] MathTest.Addition (0 ms)
|
||||
/// ```
|
||||
///
|
||||
/// A test with a Result return type, and which uses the `?` operator. It will fail if the test
|
||||
/// returns an `Err`, and print the resulting error string:
|
||||
/// A test with a Result return type, and which uses the `?` operator. It will
|
||||
/// fail if the test returns an `Err`, and print the resulting error string:
|
||||
/// ```
|
||||
/// #[gtest(ResultTest, CheckThingWithResult)]
|
||||
/// fn my_test() -> std::result::Result<(), String> {
|
||||
@ -48,10 +51,11 @@ pub fn gtest(
|
||||
TestSuite,
|
||||
TestName,
|
||||
}
|
||||
// Returns a string representation of an identifier argument to the attribute. For example, for
|
||||
// #[gtest(Foo, Bar)], this function would return "Foo" for position 0 and "Bar" for position 1.
|
||||
// If the argument is not a Rust identifier or not present, it returns a compiler error as a
|
||||
// TokenStream to be emitted.
|
||||
// Returns a string representation of an identifier argument to the attribute.
|
||||
// For example, for #[gtest(Foo, Bar)], this function would return "Foo" for
|
||||
// position 0 and "Bar" for position 1. If the argument is not a Rust
|
||||
// identifier or not present, it returns a compiler error as a TokenStream
|
||||
// to be emitted.
|
||||
fn get_arg_string(
|
||||
args: &syn::AttributeArgs,
|
||||
which: GtestAttributeArgument,
|
||||
@ -87,7 +91,8 @@ pub fn gtest(
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Parses `#[gtest_suite(path::to::RustType)]` and returns `path::to::RustType`.
|
||||
/// Parses `#[gtest_suite(path::to::RustType)]` and returns
|
||||
/// `path::to::RustType`.
|
||||
fn parse_gtest_suite(attr: &syn::Attribute) -> Result<TokenStream, TokenStream> {
|
||||
let parsed = match attr.parse_meta() {
|
||||
Ok(syn::Meta::List(list)) if list.nested.len() == 1 => match &list.nested[0] {
|
||||
@ -111,13 +116,14 @@ pub fn gtest(
|
||||
|
||||
// Populated data from the #[gtest_suite] macro arguments.
|
||||
//
|
||||
// The Rust type wrapping a C++ TestSuite (subclass of `testing::Test`), which is created and
|
||||
// returned by a C++ factory function. If no type is specified, then this is left as None, and
|
||||
// the default C++ factory function will be used to make a `testing::Test` directly.
|
||||
// The Rust type wrapping a C++ TestSuite (subclass of `testing::Test`), which
|
||||
// is created and returned by a C++ factory function. If no type is
|
||||
// specified, then this is left as None, and the default C++ factory
|
||||
// function will be used to make a `testing::Test` directly.
|
||||
let mut gtest_test_suite_wrapper_type: Option<TokenStream> = None;
|
||||
|
||||
// Look through other attributes on the test function, parse the ones related to Gtests, and put
|
||||
// the rest back into `attrs`.
|
||||
// Look through other attributes on the test function, parse the ones related to
|
||||
// Gtests, and put the rest back into `attrs`.
|
||||
input_fn.attrs = {
|
||||
let mut keep = Vec::new();
|
||||
for attr in std::mem::take(&mut input_fn.attrs) {
|
||||
@ -139,9 +145,10 @@ pub fn gtest(
|
||||
let gtest_test_suite_wrapper_type = gtest_test_suite_wrapper_type;
|
||||
|
||||
if let Some(asyncness) = input_fn.sig.asyncness {
|
||||
// TODO(crbug.com/1288947): We can support async functions once we have block_on() support
|
||||
// which will run a RunLoop until the async test completes. The run_test_fn just needs to be
|
||||
// generated to `block_on(|| #test_fn)` instead of calling `#test_fn` synchronously.
|
||||
// TODO(crbug.com/1288947): We can support async functions once we have
|
||||
// block_on() support which will run a RunLoop until the async test
|
||||
// completes. The run_test_fn just needs to be generated to `block_on(||
|
||||
// #test_fn)` instead of calling `#test_fn` synchronously.
|
||||
return quote_spanned! {
|
||||
asyncness.span =>
|
||||
compile_error!("async functions are not supported.");
|
||||
@ -178,20 +185,23 @@ pub fn gtest(
|
||||
}
|
||||
};
|
||||
|
||||
// We put the test function and all the code we generate around it into a submodule which is
|
||||
// uniquely named for the super module based on the Gtest suite and test names. A result of this
|
||||
// is that if two tests have the same test suite + name, a compiler error would report the
|
||||
// conflict.
|
||||
// We put the test function and all the code we generate around it into a
|
||||
// submodule which is uniquely named for the super module based on the Gtest
|
||||
// suite and test names. A result of this is that if two tests have the same
|
||||
// test suite + name, a compiler error would report the conflict.
|
||||
let test_mod = format_ident!("__test_{}_{}", test_suite_name, test_name);
|
||||
|
||||
// The run_test_fn identifier is marked #[no_mangle] to work around a codegen bug where the
|
||||
// function is seen as dead and the compiler omits it from the object files. Since it's
|
||||
// #[no_mangle], the identifier must be globally unique or we have an ODR violation. To produce
|
||||
// a unique identifier, we roll our own name mangling by combining the file name and path from
|
||||
// the source tree root with the Gtest suite and test names and the function itself.
|
||||
// The run_test_fn identifier is marked #[no_mangle] to work around a codegen
|
||||
// bug where the function is seen as dead and the compiler omits it from the
|
||||
// object files. Since it's #[no_mangle], the identifier must be globally
|
||||
// unique or we have an ODR violation. To produce a unique identifier, we
|
||||
// roll our own name mangling by combining the file name and path from
|
||||
// the source tree root with the Gtest suite and test names and the function
|
||||
// itself.
|
||||
//
|
||||
// Note that an adversary could still produce a bug here by placing two equal Gtest suite and
|
||||
// names in a single .rs file but in separate inline submodules.
|
||||
// Note that an adversary could still produce a bug here by placing two equal
|
||||
// Gtest suite and names in a single .rs file but in separate inline
|
||||
// submodules.
|
||||
let mangled_function_name = |f: &syn::ItemFn| -> syn::Ident {
|
||||
let file_name = file!().replace(|c: char| !c.is_ascii_alphanumeric(), "_");
|
||||
format_ident!("{}_{}_{}_{}", file_name, test_suite_name, test_name, f.sig.ident)
|
||||
@ -201,25 +211,27 @@ pub fn gtest(
|
||||
// The identifier of the function which contains the body of the test.
|
||||
let test_fn = &input_fn.sig.ident;
|
||||
|
||||
// Implements ToTokens to generate a reference to a static-lifetime, null-terminated, C-String
|
||||
// literal. It is represented as an array of type std::os::raw::c_char which can be either
|
||||
// signed or unsigned depending on the platform, and it can be passed directly to C++. This
|
||||
// differs from byte strings and CStr which work with `u8`.
|
||||
// Implements ToTokens to generate a reference to a static-lifetime,
|
||||
// null-terminated, C-String literal. It is represented as an array of type
|
||||
// std::os::raw::c_char which can be either signed or unsigned depending on
|
||||
// the platform, and it can be passed directly to C++. This differs from
|
||||
// byte strings and CStr which work with `u8`.
|
||||
//
|
||||
// TODO(crbug.com/1298175): Would it make sense to write a c_str_literal!() macro that takes a
|
||||
// Rust string literal and produces a null-terminated array of `c_char`? Then you could write
|
||||
// `c_str_literal!(file!())` for example, or implement a `file_c_str!()` in this way. Explore
|
||||
// using https://crates.io/crates/cstr.
|
||||
// TODO(crbug.com/1298175): Would it make sense to write a c_str_literal!()
|
||||
// macro that takes a Rust string literal and produces a null-terminated
|
||||
// array of `c_char`? Then you could write `c_str_literal!(file!())` for
|
||||
// example, or implement a `file_c_str!()` in this way. Explore using https://crates.io/crates/cstr.
|
||||
//
|
||||
// TODO(danakj): Write unit tests for this, and consider pulling this out into its own crate,
|
||||
// if we don't replace it with c_str_literal!() or the "cstr" crate.
|
||||
// TODO(danakj): Write unit tests for this, and consider pulling this out into
|
||||
// its own crate, if we don't replace it with c_str_literal!() or the "cstr"
|
||||
// crate.
|
||||
struct CStringLiteral<'a>(&'a str);
|
||||
impl quote::ToTokens for CStringLiteral<'_> {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
let mut c_chars = self.0.chars().map(|c| c as std::os::raw::c_char).collect::<Vec<_>>();
|
||||
c_chars.push(0);
|
||||
// Verify there's no embedded nulls as that would be invalid if the literal were put in
|
||||
// a std::ffi::CString.
|
||||
// Verify there's no embedded nulls as that would be invalid if the literal were
|
||||
// put in a std::ffi::CString.
|
||||
assert_eq!(c_chars.iter().filter(|x| **x == 0).count(), 1);
|
||||
let comment = format!("\"{}\" as [c_char]", self.0);
|
||||
tokens.extend(quote! {
|
||||
@ -243,15 +255,15 @@ pub fn gtest(
|
||||
}
|
||||
None => {
|
||||
// If the #[gtest] macros didn't specify a test suite, then we use
|
||||
// `rust_gtest_interop::rust_gtest_default_factory() which makes a TestSuite with
|
||||
// `testing::Test` directly.
|
||||
// `rust_gtest_interop::rust_gtest_default_factory() which makes a TestSuite
|
||||
// with `testing::Test` directly.
|
||||
quote! { ::rust_gtest_interop::__private::rust_gtest_default_factory }
|
||||
}
|
||||
};
|
||||
let test_fn_call = match >est_test_suite_wrapper_type {
|
||||
Some(_rust_type) => {
|
||||
// SAFETY: Our lambda casts the `suite` reference and does not move from it, and the
|
||||
// resulting type is not Unpin.
|
||||
// SAFETY: Our lambda casts the `suite` reference and does not move from it, and
|
||||
// the resulting type is not Unpin.
|
||||
quote! {
|
||||
let p = unsafe {
|
||||
suite.map_unchecked_mut(|suite: &mut ::rust_gtest_interop::OpaqueTestingTest| {
|
||||
@ -314,19 +326,21 @@ pub fn gtest(
|
||||
output.into()
|
||||
}
|
||||
|
||||
/// The `#[extern_test_suite()]` macro is used to implement the unsafe `TestSuite` trait.
|
||||
/// The `#[extern_test_suite()]` macro is used to implement the unsafe
|
||||
/// `TestSuite` trait.
|
||||
///
|
||||
/// The `TestSuite` trait is used to mark a Rust type as being a wrapper of a C++ subclass of
|
||||
/// `testing::Test`. This makes it valid to cast from a `*mut testing::Test` to a pointer of the
|
||||
/// marked Rust type.
|
||||
/// The `TestSuite` trait is used to mark a Rust type as being a wrapper of a
|
||||
/// C++ subclass of `testing::Test`. This makes it valid to cast from a `*mut
|
||||
/// testing::Test` to a pointer of the marked Rust type.
|
||||
///
|
||||
/// It also marks a promise that on the C++, there exists an instantiation of the
|
||||
/// RUST_GTEST_TEST_SUITE_FACTORY() macro for the C++ subclass type which will be linked with the
|
||||
/// Rust crate.
|
||||
/// It also marks a promise that on the C++, there exists an instantiation of
|
||||
/// the RUST_GTEST_TEST_SUITE_FACTORY() macro for the C++ subclass type which
|
||||
/// will be linked with the Rust crate.
|
||||
///
|
||||
/// The macro takes a single parameter which is the fully specified C++ typename of the C++ subclass
|
||||
/// for which the implementing Rust type is a wrapper. It expects the body of the trait
|
||||
/// implementation to be empty, as it will fill in the required implementation.
|
||||
/// The macro takes a single parameter which is the fully specified C++ typename
|
||||
/// of the C++ subclass for which the implementing Rust type is a wrapper. It
|
||||
/// expects the body of the trait implementation to be empty, as it will fill in
|
||||
/// the required implementation.
|
||||
///
|
||||
/// # Example
|
||||
/// If in C++ we have:
|
||||
@ -335,17 +349,18 @@ pub fn gtest(
|
||||
/// RUST_GTEST_TEST_SUITE_FACTORY(GoatTestSuite);
|
||||
/// ```
|
||||
///
|
||||
/// And in Rust we have a `ffi::GoatTestSuite` type generated to wrap the C++ type. The the type can
|
||||
/// be marked as a valid TestSuite with the `#[extern_test_suite]` macro:
|
||||
/// ```rs
|
||||
/// And in Rust we have a `ffi::GoatTestSuite` type generated to wrap the C++
|
||||
/// type. The the type can be marked as a valid TestSuite with the
|
||||
/// `#[extern_test_suite]` macro: ```rs
|
||||
/// #[extern_test_suite("GoatTestSuite")]
|
||||
/// unsafe impl rust_gtest_interop::TestSuite for ffi::GoatTestSuite {}
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// # Internals
|
||||
/// The #[cpp_prefix("STRING_")] attribute can follow `#[extern_test_suite()]` to control the
|
||||
/// path to the C++ Gtest factory function. This is used for connecting to different C++ macros
|
||||
/// than the usual RUST_GTEST_TEST_SUITE_FACTORY().
|
||||
/// The #[cpp_prefix("STRING_")] attribute can follow `#[extern_test_suite()]`
|
||||
/// to control the path to the C++ Gtest factory function. This is used for
|
||||
/// connecting to different C++ macros than the usual
|
||||
/// RUST_GTEST_TEST_SUITE_FACTORY().
|
||||
#[proc_macro_attribute]
|
||||
pub fn extern_test_suite(
|
||||
arg_stream: proc_macro::TokenStream,
|
||||
@ -353,15 +368,16 @@ pub fn extern_test_suite(
|
||||
) -> proc_macro::TokenStream {
|
||||
let args = syn::parse_macro_input!(arg_stream as syn::AttributeArgs);
|
||||
|
||||
// TODO(b/229791967): With CXX it is not possible to get the C++ typename and path from the Rust
|
||||
// wrapper type, so we require specifying it by hand in the macro. It would be nice to remove
|
||||
// this opportunity for mistakes.
|
||||
// TODO(b/229791967): With CXX it is not possible to get the C++ typename and
|
||||
// path from the Rust wrapper type, so we require specifying it by hand in
|
||||
// the macro. It would be nice to remove this opportunity for mistakes.
|
||||
let cpp_type = match if args.len() == 1 { Some(&args[0]) } else { None } {
|
||||
Some(syn::NestedMeta::Lit(syn::Lit::Str(lit_str))) => {
|
||||
// TODO(danakj): This code drops the C++ namespaces, because we can't produce a mangled
|
||||
// name and can't generate bindings involving fn pointers, so we require the C++
|
||||
// function to be `extern "C"` which means it has no namespace. Eventually we should
|
||||
// drop the `extern "C"` on the C++ side and use the full path here.
|
||||
// TODO(danakj): This code drops the C++ namespaces, because we can't produce a
|
||||
// mangled name and can't generate bindings involving fn pointers,
|
||||
// so we require the C++ function to be `extern "C"` which means it
|
||||
// has no namespace. Eventually we should drop the `extern "C"` on
|
||||
// the C++ side and use the full path here.
|
||||
let string = lit_str.value();
|
||||
let class_name = string.split("::").last();
|
||||
match class_name {
|
||||
@ -411,8 +427,8 @@ pub fn extern_test_suite(
|
||||
|
||||
let mut cpp_prefix = RUST_GTEST_FACTORY_PREFIX.to_owned();
|
||||
|
||||
// Look through other attributes on `trait_impl`, parse the ones related to Gtests, and put
|
||||
// the rest back into `attrs`.
|
||||
// Look through other attributes on `trait_impl`, parse the ones related to
|
||||
// Gtests, and put the rest back into `attrs`.
|
||||
trait_impl.attrs = {
|
||||
let mut keep = Vec::new();
|
||||
for attr in std::mem::take(&mut trait_impl.attrs) {
|
||||
@ -451,9 +467,9 @@ pub fn extern_test_suite(
|
||||
}
|
||||
};
|
||||
|
||||
// TODO(danakj): We should generate a C++ mangled name here, then we don't require
|
||||
// the function to be `extern "C"` (or have the author write the mangled name
|
||||
// themselves).
|
||||
// TODO(danakj): We should generate a C++ mangled name here, then we don't
|
||||
// require the function to be `extern "C"` (or have the author write the
|
||||
// mangled name themselves).
|
||||
let cpp_fn_name = format_ident!("{}{}", cpp_prefix, cpp_type.into_token_stream().to_string());
|
||||
|
||||
let output = quote! {
|
||||
|
@ -10,8 +10,9 @@ pub mod prelude {
|
||||
pub use gtest_attribute::extern_test_suite;
|
||||
// The #[gtest(TestSuite, TestName)] macro.
|
||||
pub use gtest_attribute::gtest;
|
||||
// Gtest expectation macros, which should be used to verify test expectations. These replace the
|
||||
// standard practice of using assert/panic in Rust tests which would crash the test binary.
|
||||
// Gtest expectation macros, which should be used to verify test expectations.
|
||||
// These replace the standard practice of using assert/panic in Rust tests
|
||||
// which would crash the test binary.
|
||||
pub use crate::expect_eq;
|
||||
pub use crate::expect_false;
|
||||
pub use crate::expect_ge;
|
||||
@ -22,14 +23,15 @@ pub mod prelude {
|
||||
pub use crate::expect_true;
|
||||
}
|
||||
|
||||
// The gtest_attribute proc-macro crate makes use of small_ctor, with a path through this crate here
|
||||
// to ensure it's available.
|
||||
// The gtest_attribute proc-macro crate makes use of small_ctor, with a path
|
||||
// through this crate here to ensure it's available.
|
||||
#[doc(hidden)]
|
||||
pub extern crate small_ctor;
|
||||
|
||||
/// A marker trait that promises the Rust type is an FFI wrapper around a C++ class which subclasses
|
||||
/// `testing::Test`. In particular, casting a `testing::Test` pointer to the implementing class type
|
||||
/// is promised to be valid.
|
||||
/// A marker trait that promises the Rust type is an FFI wrapper around a C++
|
||||
/// class which subclasses `testing::Test`. In particular, casting a
|
||||
/// `testing::Test` pointer to the implementing class type is promised to be
|
||||
/// valid.
|
||||
///
|
||||
/// Implement this trait with the `#[extern_test_suite]` macro:
|
||||
/// ```rs
|
||||
@ -37,28 +39,30 @@ pub extern crate small_ctor;
|
||||
/// unsafe impl TestSuite for Foo {}
|
||||
/// ```
|
||||
pub unsafe trait TestSuite {
|
||||
/// Gives the Gtest factory function on the C++ side which constructs the C++ class for which
|
||||
/// the implementing Rust type is an FFI wrapper.
|
||||
/// Gives the Gtest factory function on the C++ side which constructs the
|
||||
/// C++ class for which the implementing Rust type is an FFI wrapper.
|
||||
#[doc(hidden)]
|
||||
fn gtest_factory_fn_ptr() -> GtestFactoryFunction;
|
||||
}
|
||||
|
||||
/// Matches the C++ type `rust_gtest_interop::GtestFactoryFunction`, with the `testing::Test` type
|
||||
/// erased to `OpaqueTestingTest`.
|
||||
/// Matches the C++ type `rust_gtest_interop::GtestFactoryFunction`, with the
|
||||
/// `testing::Test` type erased to `OpaqueTestingTest`.
|
||||
///
|
||||
/// We replace `testing::Test*` with `OpaqueTestingTest` because but we don't know that C++ type in
|
||||
/// Rust, as we don't have a Rust generator giving access to that type.
|
||||
/// We replace `testing::Test*` with `OpaqueTestingTest` because but we don't
|
||||
/// know that C++ type in Rust, as we don't have a Rust generator giving access
|
||||
/// to that type.
|
||||
#[doc(hidden)]
|
||||
pub type GtestFactoryFunction = unsafe extern "C" fn(
|
||||
f: extern "C" fn(Pin<&mut OpaqueTestingTest>),
|
||||
) -> Pin<&'static mut OpaqueTestingTest>;
|
||||
|
||||
/// Opaque replacement of a C++ `testing::Test` type, which can only be used as a pointer, since its
|
||||
/// size is incorrect. Only appears in the GtestFactoryFunction signature, which is a function
|
||||
/// pointer that passed to C++, and never run from within Rust.
|
||||
/// Opaque replacement of a C++ `testing::Test` type, which can only be used as
|
||||
/// a pointer, since its size is incorrect. Only appears in the
|
||||
/// GtestFactoryFunction signature, which is a function pointer that passed to
|
||||
/// C++, and never run from within Rust.
|
||||
///
|
||||
/// TODO(danakj): If there was a way, without making references to it into wide pointers, we should
|
||||
/// make this type be !Sized.
|
||||
/// TODO(danakj): If there was a way, without making references to it into wide
|
||||
/// pointers, we should make this type be !Sized.
|
||||
#[repr(C)]
|
||||
#[doc(hidden)]
|
||||
pub struct OpaqueTestingTest(u8);
|
||||
@ -72,8 +76,9 @@ impl TestResult for () {
|
||||
None
|
||||
}
|
||||
}
|
||||
// This impl requires an `Error` not just a `String` so that in the future we could print things
|
||||
// like the backtrace too (though that field is currently unstable).
|
||||
// This impl requires an `Error` not just a `String` so that in the future we
|
||||
// could print things like the backtrace too (though that field is currently
|
||||
// unstable).
|
||||
impl<E: Into<Box<dyn std::error::Error>>> TestResult for std::result::Result<(), E> {
|
||||
fn into_error_message(self) -> Option<String> {
|
||||
match self {
|
||||
@ -83,18 +88,19 @@ impl<E: Into<Box<dyn std::error::Error>>> TestResult for std::result::Result<(),
|
||||
}
|
||||
}
|
||||
|
||||
// Internals used by code generated from the gtest-attriute proc-macro. Should not be used by
|
||||
// human-written code.
|
||||
// Internals used by code generated from the gtest-attriute proc-macro. Should
|
||||
// not be used by human-written code.
|
||||
#[doc(hidden)]
|
||||
pub mod __private {
|
||||
use super::{GtestFactoryFunction, OpaqueTestingTest, Pin};
|
||||
|
||||
/// Rust wrapper around the same C++ method.
|
||||
///
|
||||
/// We have a wrapper to convert the file name into a C++-friendly string, and the line number
|
||||
/// into a C++-friendly signed int.
|
||||
/// We have a wrapper to convert the file name into a C++-friendly string,
|
||||
/// and the line number into a C++-friendly signed int.
|
||||
///
|
||||
/// TODO(crbug.com/1298175): We should be able to receive a C++-friendly file path.
|
||||
/// TODO(crbug.com/1298175): We should be able to receive a C++-friendly
|
||||
/// file path.
|
||||
///
|
||||
/// TODO(danakj): We should be able to pass a `c_int` directly to C++:
|
||||
/// https://github.com/dtolnay/cxx/issues/1015.
|
||||
@ -103,8 +109,8 @@ pub mod __private {
|
||||
let null_term_message = std::ffi::CString::new(message).unwrap();
|
||||
|
||||
extern "C" {
|
||||
// The C++ mangled name for rust_gtest_interop::rust_gtest_add_failure_at(). This comes
|
||||
// from `objdump -t` on the C++ object file.
|
||||
// The C++ mangled name for rust_gtest_interop::rust_gtest_add_failure_at().
|
||||
// This comes from `objdump -t` on the C++ object file.
|
||||
fn _ZN18rust_gtest_interop25rust_gtest_add_failure_atEPKciS1_(
|
||||
file: *const std::ffi::c_char,
|
||||
line: i32,
|
||||
@ -121,19 +127,22 @@ pub mod __private {
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn a file!() string for a source file into a path from the root of the source tree.
|
||||
/// Turn a file!() string for a source file into a path from the root of the
|
||||
/// source tree.
|
||||
pub fn make_canonical_file_path(file: &str) -> String {
|
||||
// The path of the file here is relative to and prefixed with the crate root's source file
|
||||
// with the current directory being the build's output directory. So for a generated crate
|
||||
// root at gen/foo/, the file path would look like `gen/foo/../../../../real/path.rs`. The
|
||||
// last two `../` move up from the build output directory to the source tree root. As such,
|
||||
// we need to strip pairs of `something/../` until there are none left, and remove the
|
||||
// remaining `../` path components up to the source tree root.
|
||||
// The path of the file here is relative to and prefixed with the crate root's
|
||||
// source file with the current directory being the build's output
|
||||
// directory. So for a generated crate root at gen/foo/, the file path
|
||||
// would look like `gen/foo/../../../../real/path.rs`. The last two `../
|
||||
// ` move up from the build output directory to the source tree root. As such,
|
||||
// we need to strip pairs of `something/../` until there are none left, and
|
||||
// remove the remaining `../` path components up to the source tree
|
||||
// root.
|
||||
//
|
||||
// Note that std::fs::canonicalize() does not work here since it requires the file to
|
||||
// exist, but we're working with a relative path that is rooted in the build directory, not
|
||||
// the current directory. We could try to get the path to the build directory.. but this is
|
||||
// simple enough.
|
||||
// Note that std::fs::canonicalize() does not work here since it requires the
|
||||
// file to exist, but we're working with a relative path that is rooted
|
||||
// in the build directory, not the current directory. We could try to
|
||||
// get the path to the build directory.. but this is simple enough.
|
||||
let (keep_rev, _) = std::path::Path::new(file).iter().rev().fold(
|
||||
(Vec::new(), 0),
|
||||
// Build the set of path components we want to keep, which we do by keeping a count of
|
||||
@ -152,7 +161,8 @@ pub mod __private {
|
||||
}
|
||||
},
|
||||
);
|
||||
// Reverse the path components, join them together, and write them into a string.
|
||||
// Reverse the path components, join them together, and write them into a
|
||||
// string.
|
||||
keep_rev
|
||||
.into_iter()
|
||||
.rev()
|
||||
@ -163,14 +173,14 @@ pub mod __private {
|
||||
|
||||
/// Wrapper that calls C++ rust_gtest_default_factory().
|
||||
///
|
||||
/// TODO(danakj): We do this by hand because cxx doesn't support passing raw function pointers:
|
||||
/// https://github.com/dtolnay/cxx/issues/1011.
|
||||
/// TODO(danakj): We do this by hand because cxx doesn't support passing raw
|
||||
/// function pointers: https://github.com/dtolnay/cxx/issues/1011.
|
||||
pub unsafe extern "C" fn rust_gtest_default_factory(
|
||||
f: extern "C" fn(Pin<&mut OpaqueTestingTest>),
|
||||
) -> Pin<&'static mut OpaqueTestingTest> {
|
||||
extern "C" {
|
||||
// The C++ mangled name for rust_gtest_interop::rust_gtest_default_factory(). This comes
|
||||
// from `objdump -t` on the C++ object file.
|
||||
// The C++ mangled name for rust_gtest_interop::rust_gtest_default_factory().
|
||||
// This comes from `objdump -t` on the C++ object file.
|
||||
fn _ZN18rust_gtest_interop26rust_gtest_default_factoryEPFvPN7testing4TestEE(
|
||||
f: extern "C" fn(Pin<&mut OpaqueTestingTest>),
|
||||
) -> Pin<&'static mut OpaqueTestingTest>;
|
||||
@ -183,8 +193,8 @@ pub mod __private {
|
||||
///
|
||||
/// Note that the `factory` parameter is actually a C++ function pointer.
|
||||
///
|
||||
/// TODO(danakj): We do this by hand because cxx doesn't support passing raw function pointers
|
||||
/// nor passing `*const c_char`: https://github.com/dtolnay/cxx/issues/1011 and
|
||||
/// TODO(danakj): We do this by hand because cxx doesn't support passing raw
|
||||
/// function pointers nor passing `*const c_char`: https://github.com/dtolnay/cxx/issues/1011 and
|
||||
/// https://github.com/dtolnay/cxx/issues/1015.
|
||||
unsafe fn rust_gtest_add_test(
|
||||
factory: GtestFactoryFunction,
|
||||
@ -195,7 +205,8 @@ pub mod __private {
|
||||
line: i32,
|
||||
) {
|
||||
extern "C" {
|
||||
/// The C++ mangled name for rust_gtest_interop::rust_gtest_add_test(). This comes from
|
||||
/// The C++ mangled name for
|
||||
/// rust_gtest_interop::rust_gtest_add_test(). This comes from
|
||||
/// `objdump -t` on the C++ object file.
|
||||
fn _ZN18rust_gtest_interop19rust_gtest_add_testEPFPN7testing4TestEPFvS2_EES4_PKcS8_S8_i(
|
||||
factory: GtestFactoryFunction,
|
||||
@ -217,10 +228,12 @@ pub mod __private {
|
||||
)
|
||||
}
|
||||
|
||||
/// Information used to register a function pointer as a test with the C++ Gtest framework.
|
||||
/// Information used to register a function pointer as a test with the C++
|
||||
/// Gtest framework.
|
||||
pub struct TestRegistration {
|
||||
pub func: extern "C" fn(suite: Pin<&mut OpaqueTestingTest>),
|
||||
// TODO(danakj): These a C-String-Literals. Maybe we should expose that as a type somewhere.
|
||||
// TODO(danakj): These a C-String-Literals. Maybe we should expose that as a type
|
||||
// somewhere.
|
||||
pub test_suite_name: &'static [std::os::raw::c_char],
|
||||
pub test_name: &'static [std::os::raw::c_char],
|
||||
pub file: &'static [std::os::raw::c_char],
|
||||
@ -230,12 +243,14 @@ pub mod __private {
|
||||
|
||||
/// Register a given test function with the C++ Gtest framework.
|
||||
///
|
||||
/// This function is called from static initializers. It may only be called from the main
|
||||
/// thread, before main() is run. It may not panic, or call anything that may panic.
|
||||
/// This function is called from static initializers. It may only be called
|
||||
/// from the main thread, before main() is run. It may not panic, or
|
||||
/// call anything that may panic.
|
||||
pub fn register_test(r: TestRegistration) {
|
||||
let line = r.line.try_into().unwrap_or(-1);
|
||||
// SAFETY: The `factory` parameter to rust_gtest_add_test() must be a C++ function that
|
||||
// returns a `testing::Test*` disguised as a `OpaqueTestingTest`. The #[gtest] macro will use
|
||||
// SAFETY: The `factory` parameter to rust_gtest_add_test() must be a C++
|
||||
// function that returns a `testing::Test*` disguised as a
|
||||
// `OpaqueTestingTest`. The #[gtest] macro will use
|
||||
// `rust_gtest_interop::rust_gtest_default_factory()` by default.
|
||||
unsafe {
|
||||
rust_gtest_add_test(
|
||||
|
@ -70,16 +70,17 @@ fn test() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// This test intentionally fails due to returning Err, and displays the message "uhoh."
|
||||
// This test intentionally fails due to returning Err, and displays the message
|
||||
// "uhoh."
|
||||
#[gtest(Test, DISABLED_WithError)]
|
||||
fn test() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||
expect_true!(true);
|
||||
Err("uhoh".into())
|
||||
}
|
||||
|
||||
// TODO(danakj): It would be nice to test expect macros, but we would need to hook up
|
||||
// EXPECT_NONFATAL_FAILURE to do so. There's no way to fail a test in a way that we accept, the bots
|
||||
// see the failure even if the process returns 0.
|
||||
// TODO(danakj): It would be nice to test expect macros, but we would need to
|
||||
// hook up EXPECT_NONFATAL_FAILURE to do so. There's no way to fail a test in a
|
||||
// way that we accept, the bots see the failure even if the process returns 0.
|
||||
// #[gtest(ExpectFailTest, Failures)]
|
||||
// fn test() {
|
||||
// expect_eq!(1 + 1, 1 + 2);
|
||||
|
@ -72,27 +72,29 @@ mod ffi {
|
||||
message: String,
|
||||
}
|
||||
|
||||
/// Options for parsing JSON inputs. A mirror of the C++ `base::JSONParserOptions` bitflags,
|
||||
/// represented as a friendlier struct-of-bools instead, and with additional fields
|
||||
/// Options for parsing JSON inputs. A mirror of the C++
|
||||
/// `base::JSONParserOptions` bitflags, represented as a friendlier
|
||||
/// struct-of-bools instead, and with additional fields
|
||||
struct JsonOptions {
|
||||
/// Allows commas to exist after the last element in structures.
|
||||
allow_trailing_commas: bool,
|
||||
/// If set the parser replaces invalid code points (i.e. lone surrogates) with the Unicode
|
||||
/// replacement character (U+FFFD). If not set, invalid code points trigger a hard error and
|
||||
/// If set the parser replaces invalid code points (i.e. lone
|
||||
/// surrogates) with the Unicode replacement character (U+FFFD).
|
||||
/// If not set, invalid code points trigger a hard error and
|
||||
/// parsing fails.
|
||||
replace_invalid_characters: bool,
|
||||
/// Allows both C (/* */) and C++ (//) style comments.
|
||||
allow_comments: bool,
|
||||
/// Permits unescaped ASCII control characters (such as unescaped \r and \n) in the range
|
||||
/// [0x00,0x1F].
|
||||
/// Permits unescaped ASCII control characters (such as unescaped \r and
|
||||
/// \n) in the range [0x00,0x1F].
|
||||
allow_control_chars: bool,
|
||||
/// Permits \\v vertical tab escapes.
|
||||
allow_vert_tab: bool,
|
||||
/// Permits \\xNN escapes as described above.
|
||||
allow_x_escapes: bool,
|
||||
|
||||
/// The maximum recursion depth to walk while parsing nested JSON objects. JSON beyond the
|
||||
/// specified depth will be ignored.
|
||||
/// The maximum recursion depth to walk while parsing nested JSON
|
||||
/// objects. JSON beyond the specified depth will be ignored.
|
||||
max_depth: usize,
|
||||
}
|
||||
}
|
||||
@ -102,14 +104,17 @@ pub type JsonOptions = ffi::JsonOptions;
|
||||
pub type Functions = ffi::Functions;
|
||||
pub type ContextPointer = ffi::ContextPointer;
|
||||
|
||||
/// Decode a JSON input from `json` and call back out to functions defined in `options` when
|
||||
/// visiting each node in order for the caller to construct an output.
|
||||
/// Decode a JSON input from `json` and call back out to functions defined in
|
||||
/// `options` when visiting each node in order for the caller to construct an
|
||||
/// output.
|
||||
///
|
||||
/// The first item visited will be appened to the `ctx` as if the `ctx` were a list. This means the
|
||||
/// `ContextPointer` in `ctx` must already be a list aggregate type, unless the caller has extra
|
||||
/// logic to handle the first element visited.
|
||||
/// The first item visited will be appened to the `ctx` as if the `ctx` were a
|
||||
/// list. This means the `ContextPointer` in `ctx` must already be a list
|
||||
/// aggregate type, unless the caller has extra logic to handle the first
|
||||
/// element visited.
|
||||
///
|
||||
/// The `error` is only written to when there is an error decoding and `false` is returned.
|
||||
/// The `error` is only written to when there is an error decoding and `false`
|
||||
/// is returned.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
@ -136,16 +141,17 @@ pub fn decode_json(
|
||||
// We track recursion depth ourselves to limit it to `max_depth` option.
|
||||
deserializer.disable_recursion_limit();
|
||||
|
||||
// The first element visited will be treated as if being appended to a list, as is specified in
|
||||
// the contract of `decode_json()`.
|
||||
// The first element visited will be treated as if being appended to a list, as
|
||||
// is specified in the contract of `decode_json()`.
|
||||
//
|
||||
// SAFETY: We have only a single ContextPointer around at a time, so this reference will not
|
||||
// alias. The lifetime of the ContextPointer exceeds this function's lifetime, so we are okay to
|
||||
// tie it to the `target`'s lifetime which is shorter.
|
||||
// SAFETY: We have only a single ContextPointer around at a time, so this
|
||||
// reference will not alias. The lifetime of the ContextPointer exceeds this
|
||||
// function's lifetime, so we are okay to tie it to the `target`'s lifetime
|
||||
// which is shorter.
|
||||
//
|
||||
// Dereferencing the ContextPointer in C++ would be Undefined Behaviour since it's not a similar
|
||||
// type to the actual type it's pointing to, but Rust allows us to make a reference to it
|
||||
// regardless.
|
||||
// Dereferencing the ContextPointer in C++ would be Undefined Behaviour since
|
||||
// it's not a similar type to the actual type it's pointing to, but Rust
|
||||
// allows us to make a reference to it regardless.
|
||||
let target = visitor::DeserializationTarget::List { ctx };
|
||||
|
||||
let result =
|
||||
@ -157,9 +163,9 @@ pub fn decode_json(
|
||||
error.as_mut().column = err.column().try_into().unwrap_or(-1);
|
||||
error.as_mut().message.clear();
|
||||
// The following line pulls in a lot of binary bloat, due to all the formatter
|
||||
// implementations required to stringify error messages. This error message is used in
|
||||
// only a couple of places outside unit tests so we could consider trying
|
||||
// to eliminate.
|
||||
// implementations required to stringify error messages. This error message is
|
||||
// used in only a couple of places outside unit tests so we could
|
||||
// consider trying to eliminate.
|
||||
error.as_mut().message.push_str(&err.to_string());
|
||||
false
|
||||
}
|
||||
|
@ -29,12 +29,12 @@ pub enum DeserializationTarget<'c> {
|
||||
Dict { ctx: Pin<&'c mut ContextPointer>, key: String },
|
||||
}
|
||||
|
||||
/// A deserializer and visitor type that is used to visit each value in the JSON input when it is
|
||||
/// deserialized.
|
||||
/// A deserializer and visitor type that is used to visit each value in the JSON
|
||||
/// input when it is deserialized.
|
||||
///
|
||||
/// Normally serde deserialization instantiates a new object, but this visitor is designed to call
|
||||
/// back into C++ for creating the deserialized objects. To achieve this we use a feature of serde
|
||||
/// called "stateful deserialization" (https://docs.serde.rs/serde/de/trait.DeserializeSeed.html).
|
||||
/// Normally serde deserialization instantiates a new object, but this visitor
|
||||
/// is designed to call back into C++ for creating the deserialized objects. To
|
||||
/// achieve this we use a feature of serde called "stateful deserialization" (https://docs.serde.rs/serde/de/trait.DeserializeSeed.html).
|
||||
pub struct ValueVisitor<'f, 'c> {
|
||||
fns: &'f Functions,
|
||||
aggregate: DeserializationTarget<'c>,
|
||||
@ -54,7 +54,8 @@ impl<'f, 'c> ValueVisitor<'f, 'c> {
|
||||
}
|
||||
|
||||
impl<'de, 'f, 'c> Visitor<'de> for ValueVisitor<'f, 'c> {
|
||||
// We call out to C++ to construct the deserialized type, so no output from the visitor.
|
||||
// We call out to C++ to construct the deserialized type, so no output from the
|
||||
// visitor.
|
||||
type Value = ();
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
@ -142,8 +143,9 @@ impl<'de, 'f, 'c> Visitor<'de> for ValueVisitor<'f, 'c> {
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
// TODO(danakj): base::Value::Dict doesn't expose a way to reserve space, so we don't bother
|
||||
// using `access.size_hint()` here, unlike when creating a list.
|
||||
// TODO(danakj): base::Value::Dict doesn't expose a way to reserve space, so we
|
||||
// don't bother using `access.size_hint()` here, unlike when creating a
|
||||
// list.
|
||||
let mut inner_ctx = match self.aggregate {
|
||||
DeserializationTarget::List { ctx } => self.fns.list_append_dict(ctx),
|
||||
DeserializationTarget::Dict { ctx, key } => self.fns.dict_set_dict(ctx, &key),
|
||||
@ -180,7 +182,8 @@ impl<'de, 'f, 'c> Visitor<'de> for ValueVisitor<'f, 'c> {
|
||||
}
|
||||
|
||||
impl<'de, 'f, 'c> DeserializeSeed<'de> for ValueVisitor<'f, 'c> {
|
||||
// We call out to C++ to construct the deserialized type, so no output from here.
|
||||
// We call out to C++ to construct the deserialized type, so no output from
|
||||
// here.
|
||||
type Value = ();
|
||||
|
||||
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||
|
@ -21,7 +21,8 @@ pub enum Visibility {
|
||||
Public,
|
||||
/// The crate can be used by only third-party crates.
|
||||
ThirdParty,
|
||||
/// The crate can be used by any test target, and in production by third-party crates.
|
||||
/// The crate can be used by any test target, and in production by
|
||||
/// third-party crates.
|
||||
TestOnlyAndThirdParty,
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,8 @@ use crate::crates;
|
||||
use crate::manifest::CargoManifest;
|
||||
use crate::paths;
|
||||
|
||||
/// Runs the download subcommand, which downloads a crate from crates.io and unpacks it into the
|
||||
/// Chromium tree.
|
||||
/// Runs the download subcommand, which downloads a crate from crates.io and
|
||||
/// unpacks it into the Chromium tree.
|
||||
pub fn download(
|
||||
name: &str,
|
||||
version: semver::Version,
|
||||
@ -40,12 +40,13 @@ pub fn download(
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
// Makes the directory where the build file will go. The crate's source code will go below it.
|
||||
// This directory and its parents are allowed to exist already.
|
||||
// Makes the directory where the build file will go. The crate's source code
|
||||
// will go below it. This directory and its parents are allowed to exist
|
||||
// already.
|
||||
std::fs::create_dir_all(&build_path)
|
||||
.expect("Could not make the third-party directory '{build_path}' for the crate");
|
||||
// Makes the directory where the source code will be unzipped. It should not exist or we'd be
|
||||
// clobbering existing files.
|
||||
// Makes the directory where the source code will be unzipped. It should not
|
||||
// exist or we'd be clobbering existing files.
|
||||
std::fs::create_dir(&crate_path).expect("Crate directory '{crate_path}' already exists");
|
||||
|
||||
let mut untar = process::Command::new("tar")
|
||||
|
@ -98,13 +98,15 @@ impl RuleDep {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate `BuildFile` descriptions for each third party crate in the dependency graph.
|
||||
/// Generate `BuildFile` descriptions for each third party crate in the
|
||||
/// dependency graph.
|
||||
///
|
||||
/// * `deps` is the result of dependency resolution from the `deps` module.
|
||||
/// * `metadata` contains the package metadata for each third party crate.
|
||||
/// * `build_script_outputs` is the list of files generated by the build.rs script for each package.
|
||||
/// * `deps_visibility` is the visibility for each package, defining if it can be used outside of
|
||||
/// third-party code and outside of tests.
|
||||
/// * `build_script_outputs` is the list of files generated by the build.rs
|
||||
/// script for each package.
|
||||
/// * `deps_visibility` is the visibility for each package, defining if it can
|
||||
/// be used outside of third-party code and outside of tests.
|
||||
pub fn build_files_from_deps<'a, 'b, Iter: IntoIterator<Item = &'a deps::Package>>(
|
||||
deps: Iter,
|
||||
paths: &'b paths::ChromiumPaths,
|
||||
@ -176,10 +178,10 @@ fn make_build_file_for_dep(
|
||||
|
||||
// Enumerate the dependencies of each kind for the package.
|
||||
//
|
||||
// TODO(crbug.com/1304772): If this target itself was a ":cargo_tests_support" then it should
|
||||
// only depend on other ":cargo_tests_support" targets. We should also define a
|
||||
// group("cargo_tests_support") that points to ":lib" if there is no Development library rule
|
||||
// definition.
|
||||
// TODO(crbug.com/1304772): If this target itself was a ":cargo_tests_support"
|
||||
// then it should only depend on other ":cargo_tests_support" targets. We
|
||||
// should also define a group("cargo_tests_support") that points to ":lib"
|
||||
// if there is no Development library rule definition.
|
||||
for (target_name, gn_deps, cargo_deps) in [
|
||||
("lib", &mut rule_template.deps, &dep.dependencies),
|
||||
("cargo_tests_support", &mut rule_template.dev_deps, &dep.dev_dependencies),
|
||||
@ -234,9 +236,10 @@ fn make_build_file_for_dep(
|
||||
|
||||
// Generate the rule for the main library target, if it exists.
|
||||
//
|
||||
// TODO(crbug.com/1304772): We should also define a group("cargo_tests_support") that points to
|
||||
// ":lib" if there is no Development library rule definition so that other
|
||||
// ":cargo_tests_support" rules are simpler and can always depend on that target name.
|
||||
// TODO(crbug.com/1304772): We should also define a group("cargo_tests_support")
|
||||
// that points to ":lib" if there is no Development library rule definition
|
||||
// so that other ":cargo_tests_support" rules are simpler and can always
|
||||
// depend on that target name.
|
||||
if let Some(lib_target) = &dep.lib_target {
|
||||
use deps::DependencyKind::*;
|
||||
// Generate the rules for each dependency kind. We use a stable
|
||||
@ -281,8 +284,9 @@ fn make_build_file_for_dep(
|
||||
|
||||
rules.push((lib_rule_name.clone(), lib_rule));
|
||||
|
||||
// If first-party tests should be able to use the dependency, but it's only visible to
|
||||
// third-party we need to provide a ":test_support" target for the tests to use.
|
||||
// If first-party tests should be able to use the dependency, but it's only
|
||||
// visible to third-party we need to provide a ":test_support"
|
||||
// target for the tests to use.
|
||||
if dep_kind == Normal && visibility == Visibility::TestOnlyAndThirdParty {
|
||||
let test_support_rule = Rule::Group {
|
||||
common: RuleCommon { testonly: true, public_visibility: true },
|
||||
|
@ -118,9 +118,10 @@ fn generate_for_third_party(args: &clap::ArgMatches, paths: &paths::ChromiumPath
|
||||
}
|
||||
// [build-dependencies] is not used in third_party.toml.
|
||||
|
||||
// For crates used in first-party tests, we do not build a separate library from production
|
||||
// (unlike standard Rust tests, and those found in third-party crates.) So we merge the
|
||||
// dev_dependencies from third_party.toml into the regular dependencies.
|
||||
// For crates used in first-party tests, we do not build a separate library from
|
||||
// production (unlike standard Rust tests, and those found in third-party
|
||||
// crates.) So we merge the dev_dependencies from third_party.toml into the
|
||||
// regular dependencies.
|
||||
third_party_manifest
|
||||
.dependency_spec
|
||||
.dependencies
|
||||
|
@ -100,7 +100,8 @@ pub struct FullDependency {
|
||||
/// List of files generated by build.rs script.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub build_script_outputs: Vec<String>,
|
||||
/// Extra variables to add to the lib GN rule. The text will be added verbatim.
|
||||
/// Extra variables to add to the lib GN rule. The text will be added
|
||||
/// verbatim.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub gn_variables_lib: Option<String>,
|
||||
}
|
||||
|
Reference in New Issue
Block a user