-
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
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)
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 |
1bf45ce
to
c92e941
Compare
/// | ||
/// Arrays and Function types in C have different rules for placement, such as array lengths | ||
/// being placed after the parameter list. | ||
pub(crate) fn c_signature( |
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.
Do you happen to have a small example?
c92e941
to
d2b1425
Compare
@@ -5,12 +5,13 @@ | |||
/// are not allowed at the top-level, so we hack around this by keeping it | |||
/// inside of a module. | |||
mod generated_tests { | |||
#![allow(non_snake_case)] | |||
#![allow(non_snake_case, unused_imports)] |
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.
Put #[allow(unused_imports)]
on the specific imports that are expected to possibly be unused, to avoid accidentally importing something within a function that won't be used.
check_same((all_ones < all_zeros) as u32, | ||
ctest_{{ ident }}_is_signed(), "{{ ident }} signed"); |
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.
Formatting nit
check_same((all_ones < all_zeros) as u32, | |
ctest_{{ ident }}_is_signed(), "{{ ident }} signed"); | |
check_same( | |
(all_ones < all_zeros) as u32, | |
ctest_{{ ident }}_is_signed(), | |
"{{ ident }} signed", | |
); |
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.
Bind the C call as let c_is_signed = unsafe { ctest_{{ ident }}_is_signed() };
, that way the rest of this doesn't need to be unsafe
check_same(offset_of!({{ ident }}, {{ field.ident() }}), | ||
ctest_offset_of__{{ ident }}__{{ field.ident() }}() as usize, | ||
"field offset {{ field.ident() }} of {{ ident }}"); | ||
check_same(size_of_val(&val) as u64, | ||
ctest_field_size__{{ ident }}__{{ field.ident() }}(), | ||
"field size {{ field.ident() }} 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.
check_same(offset_of!({{ ident }}, {{ field.ident() }}), | |
ctest_offset_of__{{ ident }}__{{ field.ident() }}() as usize, | |
"field offset {{ field.ident() }} of {{ ident }}"); | |
check_same(size_of_val(&val) as u64, | |
ctest_field_size__{{ ident }}__{{ field.ident() }}(), | |
"field size {{ field.ident() }} of {{ ident }}"); | |
check_same( | |
offset_of!(ident, {{ field.ident() }}) as u64, | |
ctest_offset_of__{{ ident }}__{{ field.ident() }}(), | |
"field offset {{ field.ident() }} of {{ ident }}", | |
); | |
check_same( | |
size_of_val(&val) as u64, | |
ctest_field_size__{{ ident }}__{{ field.ident() }}(), | |
"field size {{ field.ident() }} of {{ ident }}", | |
); |
Changing the first offset_of
to cast to u64
rather than turning the C call into usize
, and formatting
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 read_unaligned
also isn't sound because it's possible that the type doesn't allow all zeros.
I don't want to delay this PR further but this does need to get changed in a followup, so please add a // FIXME(soundness): cast to a byte array before reading
} | ||
|
||
{%- if !self::should_skip_struct_field_type(generator, structure, field) +%} | ||
{%- let signature = self.c_signature(field.ty, &format!("__test_field_type_{ident}_{rust_field_name}({c_type}* b)")).unwrap() +%} |
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 this function could be ctest_get_pointer_to__{ident}__{rust_field_name}
let uninit_ty = MaybeUninit::<{{ ident }}>::zeroed(); | ||
let ty_ptr = uninit_ty.as_ptr(); | ||
let field_ptr = &raw const ((*ty_ptr).{{ field.ident() }}); | ||
check_same(field_ptr as *mut _, | ||
__test_field_type_{{ ident }}_{{ field.ident() }}(ty_ptr), | ||
"field type {{ field.ident() }} 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.
There are some things I don't like about this __test_field_type_
test but I don't want to block the PR on it. Could you just comment this test (both here and in C) with a FIXME? This applies to the later check of unions as well, and should also be able to comment out c_signature
which I think should look a bit different.
extern const int16_t* T1_sref; | ||
|
||
extern const int32_t* T1_mut_opt_ref; | ||
extern int32_t* T1_mut_opt_mut_ref; | ||
extern const int32_t* T1_const_opt_const_ref; | ||
|
||
extern void (*const T1_opt_fn1)(void); |
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.
Why are these removed?
If something isn't yet working, comment it with a FIXME
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 was before I learnt that Option
was FFI safe in some cases, so I removed them. I'll add them back.
@@ -88,7 +89,8 @@ extern "C" { | |||
} | |||
|
|||
pub fn foo() { | |||
assert_eq!(1, 1); | |||
let x = 1; | |||
assert_eq!(x, 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.
Why is this needed?
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.
It triggered a clippy lint about identical args used in assert_eq
, it's the only lint left that triggers so I thought it would be better to fix it. Although the CI currently skips checking ctest-test I think.
/* FIXME(#4365): duplicate symbol errors when enabled | ||
// uint32_t (*(*T1_opt_fn2)(uint8_t))(uint16_t); | ||
// uint32_t (*(*T1_opt_fn3)(uint8_t(*)(uint8_t), uint16_t(*)(uint16_t)))(uint16_t); |
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 this is no longer accurate, just uncomment the code so we can verify the test passes
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.
Functions and statics aren't tested right now, so for now uncommenting only confirms that they parse correctly. ctest
would still fail if they're uncommented.
@@ -195,6 +179,7 @@ struct timeval { | |||
tv_usec: c_int, | |||
} | |||
|
|||
#[expect(unused)] | |||
#[repr(C)] | |||
struct log_record_t { |
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.
Make them pub
instead of expect(unused)
let (o, status) = output(&mut cmd("t2")); | ||
assert!(!status.success(), "output: {o}"); | ||
let errors = [ | ||
"bad T2Foo signed", | ||
"bad T2TypedefFoo signed", | ||
"bad T2TypedefInt signed", | ||
"bad T2Bar size", | ||
"bad T2Bar align", | ||
"bad T2Bar signed", | ||
"bad T2Baz size", | ||
"bad field offset a of T2Baz", | ||
"bad field type a of T2Baz", | ||
"bad field offset b of T2Baz", | ||
"bad field type b of T2Baz", | ||
"bad T2a function pointer", | ||
"bad T2C value at byte 0", | ||
"bad T2S string", | ||
"bad T2Union size", | ||
"bad field type b of T2Union", | ||
"bad field offset b of T2Union", | ||
]; | ||
let mut errors = errors.iter().cloned().collect::<HashSet<_>>(); | ||
|
||
let mut bad = false; | ||
for line in o.lines().filter(|l| l.starts_with("bad ")) { | ||
let msg = &line[..line.find(":").unwrap()]; | ||
if !errors.remove(&msg) { | ||
println!("unknown error: {msg}"); | ||
bad = true; | ||
} | ||
} | ||
|
||
for error in errors { | ||
println!("didn't find error: {error}"); | ||
bad = true; | ||
} | ||
if bad { | ||
println!("output was:\n\n{o}"); | ||
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.
What happened to this block?
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.
It was duplicated across the C and C++ test, nothing has been changed here.
|
||
#ifdef _MSC_VER | ||
# pragma warning(default:4365) | ||
#endif |
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.
Missing newline at end of file
Same for the other files in this directory
/// Determine whether a Rust alias/struct/union should have a round trip test. | ||
/// | ||
/// By default all alias/struct/unions are roundtripped. Aliases or fields with arrays should | ||
/// not be part of the roundtrip. | ||
pub(crate) fn should_roundtrip(gen: &TestGenerator, ident: &str) -> 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 you define what "round trip" means here?
I think that should hopefully be the last round of review with a better explanation for the changes to the tests, please rebase+squash once that is done. |
@@ -1,107 +1,62 @@ | |||
use std::process::Command; |
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 ctest_next
be added to the tests without removing ctest
? Or do the updates to tests make something fail?
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.
t2
would fail because ctest
would fail on more errors than ctest-next
since statics and functions haven't been implemented yet.
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.
Can't you use the skips?
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.
Unfortunately it's not even able to parse pub const T1S: *const c_char = c"foo".as_ptr();
so a skip wouldn't work.
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.
Ah, I guess skip_function
and skip_static
haven't been added yet. If you add them then you should be able to cfg.skip_function(|_f| true); cfg.skip_static(|_s| true)
in the tests until it works, without blocking on everything else.
The same is true for libc-test
This PR is probably getting a bit too big, I think I need to look at some of these tests closer. Nothing related to what you are adding, it just seems like the existing Would you be willing to split up this PR? It would be easier if we could land tests one at a time without delaying everything here because specific tests need more updates. Specifically, I'd suggest splitting up into something like one PR for each of the following:
This way I don't think any work is blocked after 1 and 2 are merged, the rest of the tests can be improved before they get in. |
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