Skip to content

Commit e038d67

Browse files
committed
ctest: Update TestCStr and TestConst tests
Apply the following changes: * Separate `id` (a valid identifier) from `rust_const` (potentially an invalid indentifier with `::`) since for the future we can't assume we will always have top-level items. Rename a few other fields to be consistent. * Change the `CStr` tests to be single-indirection, which is possible because we know the type. * Split up some `unsafe` blocks and add safety comments. * Rename test functions to start with `ctest_` rather than `__`. * Rename `TestCstr` to `TestCStr` (casing).
1 parent 1ba1005 commit e038d67

File tree

9 files changed

+154
-106
lines changed

9 files changed

+154
-106
lines changed

ctest-next/src/template.rs

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl CTestTemplate {
4646
/// Stores all information necessary for generation of tests for all items.
4747
#[derive(Clone, Debug, Default)]
4848
pub(crate) struct TestTemplate {
49-
pub const_cstr_tests: Vec<TestCstr>,
49+
pub const_cstr_tests: Vec<TestCStr>,
5050
pub const_tests: Vec<TestConst>,
5151
pub test_idents: Vec<BoxStr>,
5252
}
@@ -76,28 +76,29 @@ impl TestTemplate {
7676
&& path.path.segments.last().unwrap().ident == "c_char"
7777
&& ptr.mutability.is_none()
7878
{
79-
let item = TestCstr {
80-
test_ident: cstr_test_ident(constant.ident()),
81-
rust_ident: constant.ident().into(),
82-
c_ident: helper.c_ident(constant).into(),
83-
c_type: helper.c_type(constant)?.into(),
79+
let item = TestCStr {
80+
id: constant.ident().into(),
81+
test_name: cstr_test_ident(constant.ident()),
82+
rust_val: constant.ident().into(),
83+
c_val: helper.c_ident(constant).into(),
8484
};
8585
const_cstr_tests.push(item)
8686
} else {
8787
let item = TestConst {
88-
test_ident: const_test_ident(constant.ident()),
89-
rust_ident: constant.ident.clone(),
90-
rust_type: constant.ty.to_token_stream().to_string().into_boxed_str(),
91-
c_ident: helper.c_ident(constant).into(),
92-
c_type: helper.c_type(constant)?.into(),
88+
id: constant.ident().into(),
89+
test_name: const_test_ident(constant.ident()),
90+
rust_val: constant.ident.clone(),
91+
rust_ty: constant.ty.to_token_stream().to_string().into_boxed_str(),
92+
c_val: helper.c_ident(constant).into(),
93+
c_ty: helper.c_type(constant)?.into(),
9394
};
9495
const_tests.push(item)
9596
}
9697
}
9798

9899
let mut test_idents = vec![];
99-
test_idents.extend(const_cstr_tests.iter().map(|test| test.test_ident.clone()));
100-
test_idents.extend(const_tests.iter().map(|test| test.test_ident.clone()));
100+
test_idents.extend(const_cstr_tests.iter().map(|test| test.test_name.clone()));
101+
test_idents.extend(const_tests.iter().map(|test| test.test_name.clone()));
101102

102103
Ok(Self {
103104
const_cstr_tests,
@@ -107,23 +108,35 @@ impl TestTemplate {
107108
}
108109
}
109110

111+
/* Many test structures have the following fields:
112+
*
113+
* - `test_name`: The function name.
114+
* - `id`: An identifier that can be used to create functions related to this type without conflict,
115+
* usually also part of `test_name`.
116+
* - `rust_val`: Identifier for a Rust value, with path qualifications if needed.
117+
* - `rust_ty`: The Rust type of the relevant item, with path qualifications if needed.
118+
* - `c_val`: Identifier for a C value (e.g. `#define`)
119+
* - `c_ty`: The C type of the constant, qualified with `struct` or `union` if needed.
120+
*/
121+
110122
/// Information required to test a constant CStr.
111123
#[derive(Clone, Debug)]
112-
pub(crate) struct TestCstr {
113-
pub(crate) test_ident: BoxStr,
114-
pub(crate) rust_ident: BoxStr,
115-
pub(crate) c_ident: BoxStr,
116-
pub(crate) c_type: BoxStr,
124+
pub(crate) struct TestCStr {
125+
pub test_name: BoxStr,
126+
pub id: BoxStr,
127+
pub rust_val: BoxStr,
128+
pub c_val: BoxStr,
117129
}
118130

119131
/// Information required to test a constant.
120132
#[derive(Clone, Debug)]
121133
pub(crate) struct TestConst {
122-
pub(crate) test_ident: BoxStr,
123-
pub(crate) rust_ident: BoxStr,
124-
pub(crate) rust_type: BoxStr,
125-
pub(crate) c_ident: BoxStr,
126-
pub(crate) c_type: BoxStr,
134+
pub test_name: BoxStr,
135+
pub id: BoxStr,
136+
pub rust_val: BoxStr,
137+
pub c_val: BoxStr,
138+
pub rust_ty: BoxStr,
139+
pub c_ty: BoxStr,
127140
}
128141

129142
/// The Rust name of the cstr test.

ctest-next/templates/test.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,22 @@
1515

1616
{%- for const_cstr in ctx.const_cstr_tests +%}
1717

18-
static {{ const_cstr.c_type }} ctest_const_{{ const_cstr.rust_ident }}_val_static = {{ const_cstr.c_ident }};
18+
static char *ctest_const_{{ const_cstr.id }}_val_static = {{ const_cstr.c_val }};
1919

2020
// Define a function that returns a pointer to the value of the constant to test.
2121
// This will later be called on the Rust side via FFI.
22-
{{ const_cstr.c_type }}* __{{ const_cstr.test_ident }}(void) {
23-
return &ctest_const_{{ const_cstr.rust_ident }}_val_static;
22+
char *ctest_const_cstr__{{ const_cstr.id }}(void) {
23+
return ctest_const_{{ const_cstr.id }}_val_static;
2424
}
2525
{%- endfor +%}
2626

2727
{%- for constant in ctx.const_tests +%}
2828

29-
static {{ constant.c_type }} ctest_const_{{ constant.rust_ident }}_val_static = {{ constant.c_ident }};
29+
static {{ constant.c_ty }} ctest_const_{{ constant.id }}_val_static = {{ constant.c_val }};
3030

3131
// Define a function that returns a pointer to the value of the constant to test.
3232
// This will later be called on the Rust side via FFI.
33-
{{ constant.c_type }}* __{{ constant.test_ident }}(void) {
34-
return &ctest_const_{{ constant.rust_ident }}_val_static;
33+
{{ constant.c_ty }} *ctest_const__{{ constant.id }}(void) {
34+
return &ctest_const_{{ constant.id }}_val_static;
3535
}
3636
{%- endfor +%}
37-

ctest-next/templates/test.rs

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -48,41 +48,53 @@ mod generated_tests {
4848

4949
// Test that the string constant is the same in both Rust and C.
5050
// While fat pointers can't be translated, we instead use * const c_char.
51-
pub fn {{ const_cstr.test_ident }}() {
51+
pub fn {{ const_cstr.test_name }}() {
5252
extern "C" {
53-
fn __{{ const_cstr.test_ident }}() -> *const *const u8;
54-
}
55-
let val = {{ const_cstr.rust_ident }};
56-
unsafe {
57-
let ptr = *__{{ const_cstr.test_ident }}();
58-
let val = CStr::from_ptr(ptr.cast::<c_char>());
59-
let val = val.to_str().expect("const {{ const_cstr.rust_ident }} not utf8");
60-
let c = ::std::ffi::CStr::from_ptr(ptr as *const _);
61-
let c = c.to_str().expect("const {{ const_cstr.rust_ident }} not utf8");
62-
check_same(val, c, "{{ const_cstr.rust_ident }} string");
53+
fn ctest_const_cstr__{{ const_cstr.id }}() -> *const c_char;
6354
}
55+
56+
// SAFETY: we assume that `c_char` pointer consts are for C strings.
57+
let r_val = unsafe {
58+
let r_ptr: *const c_char = {{ const_cstr.rust_val }};
59+
assert!(!r_ptr.is_null(), "const {{ const_cstr.rust_val }} is null");
60+
CStr::from_ptr(r_ptr)
61+
};
62+
63+
// SAFETY: FFI call returns a valid C string.
64+
let c_val = unsafe {
65+
let c_ptr: *const c_char = unsafe { ctest_const_cstr__{{ const_cstr.id }}() };
66+
CStr::from_ptr(c_ptr)
67+
};
68+
69+
check_same(r_val, c_val, "const {{ const_cstr.rust_val }} string");
6470
}
6571
{%- endfor +%}
6672

6773
{%- for constant in ctx.const_tests +%}
6874

6975
// Test that the value of the constant is the same in both Rust and C.
7076
// This performs a byte by byte comparision of the constant value.
71-
pub fn {{ constant.test_ident }}() {
77+
pub fn {{ constant.test_name }}() {
78+
type T = {{ constant.rust_ty }};
7279
extern "C" {
73-
fn __{{ constant.test_ident }}() -> *const {{ constant.rust_type }};
80+
fn ctest_const__{{ constant.id }}() -> *const T;
7481
}
75-
let val = {{ constant.rust_ident }};
76-
unsafe {
77-
let ptr1 = ptr::from_ref(&val).cast::<u8>();
78-
let ptr2 = __{{ constant.test_ident }}().cast::<u8>();
79-
let ptr1_bytes = slice::from_raw_parts(ptr1, mem::size_of::<{{ constant.rust_type }}>());
80-
let ptr2_bytes = slice::from_raw_parts(ptr2, mem::size_of::<{{ constant.rust_type }}>());
81-
for (i, (&b1, &b2)) in ptr1_bytes.iter().zip(ptr2_bytes.iter()).enumerate() {
82-
// HACK: This may read uninitialized data! We do this because
83-
// there isn't a good way to recursively iterate all fields.
84-
check_same_hex(b1, b2, &format!("{{ constant.rust_ident }} value at byte {}", i));
85-
}
82+
83+
/* HACK: The slices may contian uninitialized data! We do this because
84+
* there isn't a good way to recursively iterate all fields. */
85+
86+
let r_val: T = {{ constant.rust_val }};
87+
let r_bytes = unsafe {
88+
slice::from_raw_parts(ptr::from_ref(&r_val).cast::<u8>(), size_of::<T>())
89+
};
90+
91+
let c_bytes = unsafe {
92+
let c_ptr: *const T = unsafe { ctest_const__{{ constant.id }}() };
93+
slice::from_raw_parts(c_ptr.cast::<u8>(), size_of::<T>())
94+
};
95+
96+
for (i, (&b1, &b2)) in r_bytes.iter().zip(c_bytes.iter()).enumerate() {
97+
check_same_hex(b1, b2, &format!("{{ constant.rust_val }} value at byte {}", i));
8698
}
8799
}
88100
{%- endfor +%}

ctest-next/tests/input/hierarchy.out.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ static bool ctest_const_ON_val_static = ON;
1111

1212
// Define a function that returns a pointer to the value of the constant to test.
1313
// This will later be called on the Rust side via FFI.
14-
bool* __ctest_const_ON(void) {
14+
bool *ctest_const__ON(void) {
1515
return &ctest_const_ON_val_static;
1616
}

ctest-next/tests/input/hierarchy.out.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,26 @@ mod generated_tests {
4444
// Test that the value of the constant is the same in both Rust and C.
4545
// This performs a byte by byte comparision of the constant value.
4646
pub fn ctest_const_ON() {
47+
type T = bool;
4748
extern "C" {
48-
fn __ctest_const_ON() -> *const bool;
49+
fn ctest_const__ON() -> *const T;
4950
}
50-
let val = ON;
51-
unsafe {
52-
let ptr1 = ptr::from_ref(&val).cast::<u8>();
53-
let ptr2 = __ctest_const_ON().cast::<u8>();
54-
let ptr1_bytes = slice::from_raw_parts(ptr1, mem::size_of::<bool>());
55-
let ptr2_bytes = slice::from_raw_parts(ptr2, mem::size_of::<bool>());
56-
for (i, (&b1, &b2)) in ptr1_bytes.iter().zip(ptr2_bytes.iter()).enumerate() {
57-
// HACK: This may read uninitialized data! We do this because
58-
// there isn't a good way to recursively iterate all fields.
59-
check_same_hex(b1, b2, &format!("ON value at byte {}", i));
60-
}
51+
52+
/* HACK: The slices may contian uninitialized data! We do this because
53+
* there isn't a good way to recursively iterate all fields. */
54+
55+
let r_val: T = ON;
56+
let r_bytes = unsafe {
57+
slice::from_raw_parts(ptr::from_ref(&r_val).cast::<u8>(), size_of::<T>())
58+
};
59+
60+
let c_bytes = unsafe {
61+
let c_ptr: *const T = unsafe { ctest_const__ON() };
62+
slice::from_raw_parts(c_ptr.cast::<u8>(), size_of::<T>())
63+
};
64+
65+
for (i, (&b1, &b2)) in r_bytes.iter().zip(c_bytes.iter()).enumerate() {
66+
check_same_hex(b1, b2, &format!("ON value at byte {}", i));
6167
}
6268
}
6369
}

ctest-next/tests/input/simple.out.with-renames.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@
77

88
#include <simple.h>
99

10-
static char const* ctest_const_A_val_static = A;
10+
static char *ctest_const_A_val_static = A;
1111

1212
// Define a function that returns a pointer to the value of the constant to test.
1313
// This will later be called on the Rust side via FFI.
14-
char const** __ctest_const_cstr_A(void) {
15-
return &ctest_const_A_val_static;
14+
char *ctest_const_cstr__A(void) {
15+
return ctest_const_A_val_static;
1616
}
1717

18-
static char const* ctest_const_B_val_static = C_B;
18+
static char *ctest_const_B_val_static = C_B;
1919

2020
// Define a function that returns a pointer to the value of the constant to test.
2121
// This will later be called on the Rust side via FFI.
22-
char const** __ctest_const_cstr_B(void) {
23-
return &ctest_const_B_val_static;
22+
char *ctest_const_cstr__B(void) {
23+
return ctest_const_B_val_static;
2424
}

ctest-next/tests/input/simple.out.with-renames.rs

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,34 +45,46 @@ mod generated_tests {
4545
// While fat pointers can't be translated, we instead use * const c_char.
4646
pub fn ctest_const_cstr_A() {
4747
extern "C" {
48-
fn __ctest_const_cstr_A() -> *const *const u8;
49-
}
50-
let val = A;
51-
unsafe {
52-
let ptr = *__ctest_const_cstr_A();
53-
let val = CStr::from_ptr(ptr.cast::<c_char>());
54-
let val = val.to_str().expect("const A not utf8");
55-
let c = ::std::ffi::CStr::from_ptr(ptr as *const _);
56-
let c = c.to_str().expect("const A not utf8");
57-
check_same(val, c, "A string");
48+
fn ctest_const_cstr__A() -> *const c_char;
5849
}
50+
51+
// SAFETY: we assume that `c_char` pointer consts are for C strings.
52+
let r_val = unsafe {
53+
let r_ptr: *const c_char = A;
54+
assert!(!r_ptr.is_null(), "const A is null");
55+
CStr::from_ptr(r_ptr)
56+
};
57+
58+
// SAFETY: FFI call returns a valid C string.
59+
let c_val = unsafe {
60+
let c_ptr: *const c_char = unsafe { ctest_const_cstr__A() };
61+
CStr::from_ptr(c_ptr)
62+
};
63+
64+
check_same(r_val, c_val, "const A string");
5965
}
6066

6167
// Test that the string constant is the same in both Rust and C.
6268
// While fat pointers can't be translated, we instead use * const c_char.
6369
pub fn ctest_const_cstr_B() {
6470
extern "C" {
65-
fn __ctest_const_cstr_B() -> *const *const u8;
66-
}
67-
let val = B;
68-
unsafe {
69-
let ptr = *__ctest_const_cstr_B();
70-
let val = CStr::from_ptr(ptr.cast::<c_char>());
71-
let val = val.to_str().expect("const B not utf8");
72-
let c = ::std::ffi::CStr::from_ptr(ptr as *const _);
73-
let c = c.to_str().expect("const B not utf8");
74-
check_same(val, c, "B string");
71+
fn ctest_const_cstr__B() -> *const c_char;
7572
}
73+
74+
// SAFETY: we assume that `c_char` pointer consts are for C strings.
75+
let r_val = unsafe {
76+
let r_ptr: *const c_char = B;
77+
assert!(!r_ptr.is_null(), "const B is null");
78+
CStr::from_ptr(r_ptr)
79+
};
80+
81+
// SAFETY: FFI call returns a valid C string.
82+
let c_val = unsafe {
83+
let c_ptr: *const c_char = unsafe { ctest_const_cstr__B() };
84+
CStr::from_ptr(c_ptr)
85+
};
86+
87+
check_same(r_val, c_val, "const B string");
7688
}
7789
}
7890

ctest-next/tests/input/simple.out.with-skips.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77

88
#include <simple.h>
99

10-
static char const* ctest_const_A_val_static = A;
10+
static char *ctest_const_A_val_static = A;
1111

1212
// Define a function that returns a pointer to the value of the constant to test.
1313
// This will later be called on the Rust side via FFI.
14-
char const** __ctest_const_cstr_A(void) {
15-
return &ctest_const_A_val_static;
14+
char *ctest_const_cstr__A(void) {
15+
return ctest_const_A_val_static;
1616
}

ctest-next/tests/input/simple.out.with-skips.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,23 @@ mod generated_tests {
4545
// While fat pointers can't be translated, we instead use * const c_char.
4646
pub fn ctest_const_cstr_A() {
4747
extern "C" {
48-
fn __ctest_const_cstr_A() -> *const *const u8;
49-
}
50-
let val = A;
51-
unsafe {
52-
let ptr = *__ctest_const_cstr_A();
53-
let val = CStr::from_ptr(ptr.cast::<c_char>());
54-
let val = val.to_str().expect("const A not utf8");
55-
let c = ::std::ffi::CStr::from_ptr(ptr as *const _);
56-
let c = c.to_str().expect("const A not utf8");
57-
check_same(val, c, "A string");
48+
fn ctest_const_cstr__A() -> *const c_char;
5849
}
50+
51+
// SAFETY: we assume that `c_char` pointer consts are for C strings.
52+
let r_val = unsafe {
53+
let r_ptr: *const c_char = A;
54+
assert!(!r_ptr.is_null(), "const A is null");
55+
CStr::from_ptr(r_ptr)
56+
};
57+
58+
// SAFETY: FFI call returns a valid C string.
59+
let c_val = unsafe {
60+
let c_ptr: *const c_char = unsafe { ctest_const_cstr__A() };
61+
CStr::from_ptr(c_ptr)
62+
};
63+
64+
check_same(r_val, c_val, "const A string");
5965
}
6066
}
6167

0 commit comments

Comments
 (0)