From 1685c18f1e95117dd542b23e74811de3873fedb4 Mon Sep 17 00:00:00 2001 From: mbyx Date: Wed, 30 Jul 2025 14:36:26 +0500 Subject: [PATCH] ctest: add foreign static test --- ctest-next/src/ffi_items.rs | 1 - ctest-next/src/template.rs | 36 +++++++++++++++++++ ctest-next/templates/test.c | 9 +++++ ctest-next/templates/test.rs | 17 ++++++++- ctest-next/tests/input/hierarchy.out.c | 6 ++++ ctest-next/tests/input/hierarchy.out.rs | 15 +++++++- ctest-next/tests/input/hierarchy/foo.rs | 2 +- ctest-next/tests/input/macro.out.rs | 2 +- .../tests/input/simple.out.with-renames.rs | 2 +- .../tests/input/simple.out.with-skips.rs | 2 +- ctest-test/build.rs | 1 + 11 files changed, 86 insertions(+), 7 deletions(-) diff --git a/ctest-next/src/ffi_items.rs b/ctest-next/src/ffi_items.rs index a3758c54b3a1d..ee82bccf21ebd 100644 --- a/ctest-next/src/ffi_items.rs +++ b/ctest-next/src/ffi_items.rs @@ -62,7 +62,6 @@ impl FfiItems { } /// Return a list of all foreign statics found mapped by their ABI. - #[cfg_attr(not(test), expect(unused))] pub(crate) fn foreign_statics(&self) -> &Vec { &self.foreign_statics } diff --git a/ctest-next/src/template.rs b/ctest-next/src/template.rs index 41091b3872bb2..be096faf6be61 100644 --- a/ctest-next/src/template.rs +++ b/ctest-next/src/template.rs @@ -50,6 +50,7 @@ impl CTestTemplate { /// Stores all information necessary for generation of tests for all items. #[derive(Clone, Debug, Default)] pub(crate) struct TestTemplate { + pub foreign_static_tests: Vec, pub field_ptr_tests: Vec, pub field_size_offset_tests: Vec, pub roundtrip_tests: Vec, @@ -77,6 +78,7 @@ impl TestTemplate { template.populate_field_ptr_tests(&helper)?; template.populate_roundtrip_tests(&helper)?; template.populate_foreign_fn_tests(&helper)?; + template.populate_foreign_static_tests(&helper)?; Ok(template) } @@ -414,6 +416,28 @@ impl TestTemplate { Ok(()) } + + /// Populates tests for foreign statics, keeping track of the names of each test. + fn populate_foreign_static_tests( + &mut self, + helper: &TranslateHelper, + ) -> Result<(), TranslationError> { + for static_ in helper.ffi_items.foreign_statics() { + let rust_ty = static_.ty.to_token_stream().to_string().into_boxed_str(); + + let item = TestForeignStatic { + test_name: static_test_ident(static_.ident()), + id: static_.ident().into(), + c_val: helper.c_ident(static_).into_boxed_str(), + rust_ty, + }; + + self.foreign_static_tests.push(item.clone()); + self.test_idents.push(item.test_name); + } + + Ok(()) + } } /* Many test structures have the following fields: @@ -498,6 +522,14 @@ pub(crate) struct TestForeignFn { pub id: BoxStr, } +#[derive(Clone, Debug)] +pub(crate) struct TestForeignStatic { + pub test_name: BoxStr, + pub id: BoxStr, + pub c_val: BoxStr, + pub rust_ty: BoxStr, +} + fn signededness_test_ident(ident: &str) -> BoxStr { format!("ctest_signededness_{ident}").into() } @@ -530,6 +562,10 @@ fn foreign_fn_test_ident(ident: &str) -> BoxStr { format!("ctest_foreign_fn_{ident}").into() } +fn static_test_ident(ident: &str) -> BoxStr { + format!("ctest_static_{ident}").into() +} + /// Wrap methods that depend on both ffi items and the generator. pub(crate) struct TranslateHelper<'a> { ffi_items: &'a FfiItems, diff --git a/ctest-next/templates/test.c b/ctest-next/templates/test.c index 3268b96f2ecbc..5ff39ed746955 100644 --- a/ctest-next/templates/test.c +++ b/ctest-next/templates/test.c @@ -137,3 +137,12 @@ ctest_void_func ctest_foreign_fn__{{ item.id }}(void) { #ifdef _MSC_VER # pragma warning(default:4191) #endif + +{%- for static_ in ctx.foreign_static_tests +%} + +// Return a pointer to the static variable content. +void *ctest_static__{{ static_.id }}(void) { + // FIXME(ctest): Not correct due to casting the function to a data pointer. + return (void *)&{{ static_.c_val }}; +} +{%- endfor +%} diff --git a/ctest-next/templates/test.rs b/ctest-next/templates/test.rs index fff33b2f51a6e..b6a9762a95ea4 100644 --- a/ctest-next/templates/test.rs +++ b/ctest-next/templates/test.rs @@ -10,7 +10,7 @@ mod generated_tests { #![allow(non_snake_case)] #![deny(improper_ctypes_definitions)] #[allow(unused_imports)] - use std::ffi::{CStr, c_int, c_char}; + use std::ffi::{CStr, c_int, c_char, c_uint}; use std::fmt::{Debug, LowerHex}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; #[allow(unused_imports)] @@ -327,6 +327,21 @@ mod generated_tests { check_same(actual, expected, "{{ item.id }} function pointer"); } {%- endfor +%} + +{%- for static_ in ctx.foreign_static_tests +%} + + // Tests if the pointer to the static variable matches in both Rust and C. + pub fn {{ static_.test_name }}() { + extern "C" { + fn ctest_static__{{ static_.id }}() -> *const {{ static_.rust_ty }}; + } + let actual = (&raw const {{ static_.id }}).addr(); + let expected = unsafe { + ctest_static__{{ static_.id }}().addr() + }; + check_same(actual, expected, "{{ static_.id }} static"); + } +{%- endfor +%} } use generated_tests::*; diff --git a/ctest-next/tests/input/hierarchy.out.c b/ctest-next/tests/input/hierarchy.out.c index f224bb7abe39c..47dbd18d45084 100644 --- a/ctest-next/tests/input/hierarchy.out.c +++ b/ctest-next/tests/input/hierarchy.out.c @@ -80,3 +80,9 @@ ctest_void_func ctest_foreign_fn__malloc(void) { #ifdef _MSC_VER # pragma warning(default:4191) #endif + +// Return a pointer to the static variable content. +void *ctest_static__in6addr_any(void) { + // FIXME(ctest): Not correct due to casting the function to a data pointer. + return (void *)&in6addr_any; +} diff --git a/ctest-next/tests/input/hierarchy.out.rs b/ctest-next/tests/input/hierarchy.out.rs index e228e9ef28690..6fdde40cdb833 100644 --- a/ctest-next/tests/input/hierarchy.out.rs +++ b/ctest-next/tests/input/hierarchy.out.rs @@ -7,7 +7,7 @@ mod generated_tests { #![allow(non_snake_case)] #![deny(improper_ctypes_definitions)] #[allow(unused_imports)] - use std::ffi::{CStr, c_int, c_char}; + use std::ffi::{CStr, c_int, c_char, c_uint}; use std::fmt::{Debug, LowerHex}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; #[allow(unused_imports)] @@ -223,6 +223,18 @@ mod generated_tests { let expected = malloc as u64; check_same(actual, expected, "malloc function pointer"); } + + // Tests if the pointer to the static variable matches in both Rust and C. + pub fn ctest_static_in6addr_any() { + extern "C" { + fn ctest_static__in6addr_any() -> *const in6_addr; + } + let actual = (&raw const in6addr_any).addr(); + let expected = unsafe { + ctest_static__in6addr_any().addr() + }; + check_same(actual, expected, "in6addr_any static"); + } } use generated_tests::*; @@ -247,4 +259,5 @@ fn run_all() { ctest_signededness_in6_addr(); ctest_roundtrip_in6_addr(); ctest_foreign_fn_malloc(); + ctest_static_in6addr_any(); } diff --git a/ctest-next/tests/input/hierarchy/foo.rs b/ctest-next/tests/input/hierarchy/foo.rs index 91727290cfdd3..cfcf8545402b6 100644 --- a/ctest-next/tests/input/hierarchy/foo.rs +++ b/ctest-next/tests/input/hierarchy/foo.rs @@ -6,5 +6,5 @@ pub const ON: bool = true; unsafe extern "C" { pub fn malloc(size: usize) -> *mut c_void; - static in6addr_any: in6_addr; + pub static in6addr_any: in6_addr; } diff --git a/ctest-next/tests/input/macro.out.rs b/ctest-next/tests/input/macro.out.rs index 95b52a2567d59..8ebfaf9468c36 100644 --- a/ctest-next/tests/input/macro.out.rs +++ b/ctest-next/tests/input/macro.out.rs @@ -7,7 +7,7 @@ mod generated_tests { #![allow(non_snake_case)] #![deny(improper_ctypes_definitions)] #[allow(unused_imports)] - use std::ffi::{CStr, c_int, c_char}; + use std::ffi::{CStr, c_int, c_char, c_uint}; use std::fmt::{Debug, LowerHex}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; #[allow(unused_imports)] diff --git a/ctest-next/tests/input/simple.out.with-renames.rs b/ctest-next/tests/input/simple.out.with-renames.rs index 8e857409e54e8..9937410043b74 100644 --- a/ctest-next/tests/input/simple.out.with-renames.rs +++ b/ctest-next/tests/input/simple.out.with-renames.rs @@ -7,7 +7,7 @@ mod generated_tests { #![allow(non_snake_case)] #![deny(improper_ctypes_definitions)] #[allow(unused_imports)] - use std::ffi::{CStr, c_int, c_char}; + use std::ffi::{CStr, c_int, c_char, c_uint}; use std::fmt::{Debug, LowerHex}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; #[allow(unused_imports)] diff --git a/ctest-next/tests/input/simple.out.with-skips.rs b/ctest-next/tests/input/simple.out.with-skips.rs index 16eca78827330..9bb6cd2fde359 100644 --- a/ctest-next/tests/input/simple.out.with-skips.rs +++ b/ctest-next/tests/input/simple.out.with-skips.rs @@ -7,7 +7,7 @@ mod generated_tests { #![allow(non_snake_case)] #![deny(improper_ctypes_definitions)] #[allow(unused_imports)] - use std::ffi::{CStr, c_int, c_char}; + use std::ffi::{CStr, c_int, c_char, c_uint}; use std::fmt::{Debug, LowerHex}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; #[allow(unused_imports)] diff --git a/ctest-test/build.rs b/ctest-test/build.rs index bb394873d5729..2161bb31850bc 100644 --- a/ctest-test/build.rs +++ b/ctest-test/build.rs @@ -99,6 +99,7 @@ fn test_ctest_next() { .include("src") .skip_private(true) .rename_fn(|f| f.link_name().unwrap_or(f.ident()).to_string().into()) + .rename_static(|s| s.link_name().unwrap_or(s.ident()).to_string().into()) .rename_union_ty(|ty| (ty == "T1Union").then_some(ty.to_string())) .rename_struct_ty(|ty| (ty == "Transparent").then_some(ty.to_string())) .volatile_struct_field(|s, f| s.ident() == "V" && f.ident() == "v")