-
Notifications
You must be signed in to change notification settings - Fork 1.1k
ctest: add tests for aliases, structs, unions, as well as a test crate. #4543
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
d787bc1
to
6376d9e
Compare
6ac55b3
to
66cffca
Compare
ctest-next-test/Cargo.toml
Outdated
@@ -0,0 +1,33 @@ | |||
[package] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there anything different between ctest-next-test
and the existing ctest-test
? If it's nothing significant, I think it would be better to reuse ctest-test
and just add the invocations to test ctest-next
in its build.rs
, rather than duplicating everything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a bunch of removals, deleting the cpp side of the tests and modifying all.rs
, other than that the actual test files have minor changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to remove the c++ tests and make whatever changes are needed, the current ctest
won't be getting any updates so we don't really have anything to keep testing there.
I'd prefer modifying the existing ctest-test
anyway to see what the actual diff is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(first set, more in progress)
ctest-next/src/ast/structure.rs
Outdated
@@ -15,4 +13,9 @@ impl Struct { | |||
pub fn ident(&self) -> &str { | |||
&self.ident | |||
} | |||
|
|||
/// Return the public fields of the struct. | |||
pub fn public_fields(&self) -> Vec<&Field> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pub fn public_fields(&self) -> Vec<&Field> { | |
pub fn public_fields(&self) -> impl Iterator<Item = &Field> { |
That way you don't need to .collect()
ctest-next/src/ast/union.rs
Outdated
@@ -15,4 +14,9 @@ impl Union { | |||
pub fn ident(&self) -> &str { | |||
&self.ident | |||
} | |||
|
|||
/// Return the public fields of the union. | |||
pub(crate) fn public_fields(&self) -> Vec<&Field> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use impl Iterator<Item = &Field>
here too
ctest-next/src/ffi_items.rs
Outdated
let link_name = i | ||
.attrs | ||
.iter() | ||
.find(|attr| attr.path().is_ident("link_name")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably error here if there are >1 link_name
attrs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Visit
trait doesn't return anything, so it would have to be a panic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's fine, shouldn't happen anyway
ctest-next/src/lib.rs
Outdated
Field(Either<&'a Struct, &'a Union>, &'a Field), | ||
Alias(&'a Type), | ||
Const(&'a Const), | ||
Static(&'a Static), | ||
Type(&'a str), | ||
/// This variant is used for renaming the struct type. | ||
StructType(&'a str), | ||
UnionType(&'a str), | ||
FieldType(Either<&'a Struct, &'a Union>, &'a Field), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could these be split to StructField
StructFieldType
UnionField
and UnionFieldType
rather than using Either
? Seems slightly more straightforward since you have to match
somewhere anyway.
ctest-next/templates/test.rs
Outdated
use std::ffi::c_int; | ||
|
||
type U = {{ ident }}; | ||
extern "C" { | ||
fn __test_roundtrip_{{ ident }}( | ||
size: i32, x: U, e: *mut c_int, pad: *const u8 | ||
) -> U; | ||
} | ||
let pad = roundtrip_padding_{{ ident }}(); | ||
unsafe { | ||
use std::mem::{MaybeUninit, size_of}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Applies multiple places) put these imports at the top of the file, since they'll be used everywhere.
Also you don't need to import size_of
, it's in the prelude since 1.80
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The imports that are within the test functions are there because they aren't used everywhere, moving them to the top level would cause unused import errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine, just #[allow(unused_imports)]
ctest-next/templates/test.rs
Outdated
/// | ||
/// For type aliases, the padding map is all zeroes. | ||
fn roundtrip_padding_{{ ident }}() -> Vec<u8> { | ||
vec![0; mem::size_of::<{{ ident }}>()] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May as well use the prelude
vec![0; mem::size_of::<{{ ident }}>()] | |
vec![0; size_of::<{{ ident }}>()] |
ctest-next/templates/test.c
Outdated
uint64_t __test_align_{{ ident }}(void) { | ||
typedef struct { | ||
unsigned char c; | ||
{{ c_type }} v; | ||
} type; | ||
type t; | ||
size_t t_addr = (size_t)(unsigned char*)(&t); | ||
size_t v_addr = (size_t)(unsigned char*)(&t.v); | ||
return t_addr >= v_addr? t_addr - v_addr : v_addr - t_addr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can just use _Alignof
ctest-next/templates/test.c
Outdated
{%- let c_type = self.c_type(*alias).unwrap() +%} | ||
|
||
// Return the size of a type. | ||
uint64_t __test_size_{{ ident }}(void) { return sizeof({{ c_type }}); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these names could be adjusted a bi since they aren't really testing anything in these functions. Maybe ctest_get_{{ident}}_size
, ctest_get_{{ident}}_align
.
The __
prefix shouldn't be needed if ctest_
is used instead (I assume dunder was to avoid conflicts?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slightly changing my mind here; could you use ctest_size_of__{{ ident }}
and ctest_align_of__{{ ident }}
instead?
Using __
to separate "fields" is somewhat common. E.g. for struct val_tree
, ctest_size_of__val_tree
is a bit easier to tell that you're getting the ctest_size_of
for val_tree
, rather than ctest_size_of_val_tree
which could be read as ctest_size_of_val
for a struct named tree
.
ctest-next/src/translator.rs
Outdated
} | ||
|
||
/// Determine whether a C type is a signed type. | ||
pub(crate) fn has_sign(&self, ffi_items: &FfiItems, ty: &syn::Type) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be named is_signed
?
ctest-next-test/Cargo.toml
Outdated
@@ -0,0 +1,33 @@ | |||
[package] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to remove the c++ tests and make whatever changes are needed, the current ctest
won't be getting any updates so we don't really have anything to keep testing there.
I'd prefer modifying the existing ctest-test
anyway to see what the actual diff is.
ctest-next/templates/test.c
Outdated
|
||
// Return `1` if the type is signed, otherwise return `0`. | ||
uint32_t __test_signed_{{ ident }}(void) { | ||
return ((({{ c_type }}) -1) < 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return ((({{ c_type }}) -1) < 0); | |
return (({{ c_type }}) -1) < 0; |
Slightly unnest parens. Also rename this one ctest_{{ ident }}_is_signed
ctest-next/templates/test.c
Outdated
// Tests whether the type alias `x` when passed to C and back to Rust remains unchanged. | ||
// It checks if the size is the same as well as if the padding bytes are all in the correct place. | ||
{{ c_type }} __test_roundtrip_{{ ident }}( | ||
int32_t rust_size, {{ c_type }} value, int* error, unsigned char* pad |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
int32_t rust_size, {{ c_type }} value, int* error, unsigned char* pad | |
int32_t rust_size, {{ c_type }} value, int* error, unsigned char* pad |
Indentation nit
ctest-next/templates/test.rs
Outdated
y_ptr.add(i).write_volatile(d); | ||
} | ||
let r: U = __test_roundtrip_{{ ident }}(sz as i32, x.assume_init(), &mut error, pad.as_ptr()); | ||
if error == 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't really matter here but error != 0
is usually good practice (robust against added error codes)
ctest-next/templates/test.rs
Outdated
let mut error: c_int = 0; | ||
let mut y = MaybeUninit::<U>::uninit(); | ||
let mut x = MaybeUninit::<U>::uninit(); | ||
let x_ptr = x.as_mut_ptr().cast::<u8>(); | ||
let y_ptr = y.as_mut_ptr().cast::<u8>(); | ||
let sz = size_of::<U>(); | ||
for i in 0..sz { | ||
let c: u8 = (i % 256) as u8; | ||
let c = if c == 0 { 42 } else { c }; | ||
let d: u8 = 255_u8 - (i % 256) as u8; | ||
let d = if d == 0 { 42 } else { d }; | ||
x_ptr.add(i).write_volatile(c); | ||
y_ptr.add(i).write_volatile(d); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you comment what is going on here?
Also prefer MaybeUninit::<U>::zeroed()
(applies more places)
ctest-next/templates/test.rs
Outdated
x_ptr.add(i).write_volatile(c); | ||
y_ptr.add(i).write_volatile(d); | ||
} | ||
let r: U = __test_roundtrip_{{ ident }}(sz as i32, x.assume_init(), &mut error, pad.as_ptr()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assume_init
isn't sound, it's unlikely but the compiler can do some wonky stuff here. You should replace the U
in the function signature with MaybeUninit<U>
and then don't assume_init
.
I think this applies a few places.
ctest-next/templates/test.rs
Outdated
x_ptr.add(i).write_volatile(c); | ||
y_ptr.add(i).write_volatile(d); | ||
} | ||
let r: U = __test_roundtrip_{{ ident }}(sz as i32, x.assume_init(), &mut error, pad.as_ptr()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as earlier, this shouldn't assume_init
ctest-next/templates/test.rs
Outdated
} | ||
let pad = roundtrip_padding_{{ ident }}(); | ||
unsafe { | ||
use std::mem::{MaybeUninit, size_of}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a module-level import for MaybeUninit
ctest-next/templates/test.rs
Outdated
/// | ||
/// It checks if the size is the same as well as if the padding bytes are all in the | ||
/// correct place. | ||
pub fn roundtrip_{{ ident }}() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add some comments within the function about what is happening?
ctest-next/templates/test.rs
Outdated
y_ptr.add(i).write_volatile(d); | ||
} | ||
let r: U = __test_roundtrip_{{ ident }}(sz as i32, x.assume_init(), &mut error, pad.as_ptr()); | ||
if error == 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if error == 1 { | |
if error != 0 { |
ctest-next/templates/test.rs
Outdated
FAILED.store(true, Ordering::Relaxed); | ||
return; | ||
} | ||
for (i, elem) in pad.iter().enumerate().take(size_of::<U>()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't pad
already size_of::<U>()
? Maybe this is something worth asserting, then you can remove the .take()
The There might be some other things that can be combined in a similar way |
Why is the style check failing for |
Sometimes we get new lints that that suggest changes to existing code |
Description
This PR adds support for testing aliases, structs, and unions. It additionally ports
ctest-test
to usectest-next
.Sources
Checklist
libc-test/semver
have been updated*LAST
or*MAX
areincluded (see #3131)
cd libc-test && cargo test --target mytarget
);especially relevant for platforms that may not be checked in CI