Skip to content

ctest: add foreign static test #4601

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

Merged
merged 1 commit into from
Aug 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ctest-next/src/ffi_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Static> {
&self.foreign_statics
}
Expand Down
36 changes: 36 additions & 0 deletions ctest-next/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TestForeignStatic>,
pub field_ptr_tests: Vec<TestFieldPtr>,
pub field_size_offset_tests: Vec<TestFieldSizeOffset>,
pub roundtrip_tests: Vec<TestRoundtrip>,
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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()
}
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions ctest-next/templates/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 +%}
17 changes: 16 additions & 1 deletion ctest-next/templates/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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::*;
Expand Down
6 changes: 6 additions & 0 deletions ctest-next/tests/input/hierarchy.out.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
15 changes: 14 additions & 1 deletion ctest-next/tests/input/hierarchy.out.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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::*;
Expand All @@ -247,4 +259,5 @@ fn run_all() {
ctest_signededness_in6_addr();
ctest_roundtrip_in6_addr();
ctest_foreign_fn_malloc();
ctest_static_in6addr_any();
}
2 changes: 1 addition & 1 deletion ctest-next/tests/input/hierarchy/foo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 1 addition & 1 deletion ctest-next/tests/input/macro.out.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
2 changes: 1 addition & 1 deletion ctest-next/tests/input/simple.out.with-renames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
2 changes: 1 addition & 1 deletion ctest-next/tests/input/simple.out.with-skips.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
1 change: 1 addition & 0 deletions ctest-test/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down