0

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:
Collin Baker
2022-11-29 20:24:57 +00:00
committed by Chromium LUCI CQ
parent 1db504024d
commit dbf27b48b8
24 changed files with 336 additions and 263 deletions

@ -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 &gtest_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>,
}