Skip to content

Commit d104bbd

Browse files
authored
ci: test on aarch64 macOS (#1280)
* Fixes #1279. This starts testing in CI on aarch64 macOS, our first aarch64 target to test. Varargs are broken on aarch64 (#1281), so this PR also disables varargs tests on aarch64. However, doing this didn't work with the current `test_translator.py`, which textually searches the Rust test file for `fn test_*`s. `#[cfg(not(target_arch = "aarch64"))]` is the simplest and most straightforward way to disable such tests on aarch64, but parsing this from Python and generating a test runner in `main.rs` that's platform-dependent is tricky. Since Rust's built-in test runner already does much of this, and does it better, I switched `test_translator.py` to simply run `cargo test`. This means marking all of the test functions with `#[test]` and generating a mostly empty `lib.rs` instead of a `main.rs`. We also lose the ability to distinguish between expected failures and successes, as `cargo test` treats them the same, but I think this is fine. It also means we can use the idiomatic `#[should_panic]` instead of parsing the non-standard `// xfail` ourselves. I can also break this part out into a separate PR if you think that'd be helpful.
2 parents 7999c5d + 4f6f8c7 commit d104bbd

File tree

67 files changed

+275
-147
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+275
-147
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ jobs:
2222
os: macOS
2323
arch: x86_64
2424
clang-version: 17
25+
- runner: macos-15
26+
os: macOS
27+
arch: aarch64
28+
clang-version: 17
2529
fail-fast: false
2630
name: "test (${{ matrix.runner }}: ${{ matrix.os }} ${{ matrix.arch}}, Clang ${{ matrix.clang-version }})"
2731
runs-on: ${{ matrix.runner }}
@@ -41,6 +45,8 @@ jobs:
4145
# cached, what gets used as part of the key, and what additional handling
4246
# happens to make the cache reliable and smaller.
4347
- uses: Swatinem/rust-cache@v2
48+
with:
49+
cache-workspace-crates: true
4450

4551
# Run after `rust-cache` so that this is cached.
4652
- run: rustup component add rustfmt rustc-dev

scripts/run_ci_checks.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ test() {
4848
# so changing `RUSTFLAGS` will not trigger a full rebuild.
4949
test-translator() {
5050
unset RUSTFLAGS
51-
./scripts/test_translator.py tests/
51+
uv venv
52+
uv pip install -r ./scripts/requirements.txt
53+
uv run ./scripts/test_translator.py tests/
5254
}
5355

5456
all() {

scripts/test_translator.py

Lines changed: 33 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ def __init__(self, path: str, test_functions: Optional[List[TestFunction]] = Non
216216

217217

218218
class TestDirectory:
219+
rs_test_files: list[TestFile]
220+
219221
def __init__(self, full_path: str, files: 're.Pattern', keep: List[str], log_level: str) -> None:
220222
self.c_files = []
221223
self.rs_test_files = []
@@ -272,7 +274,6 @@ def __init__(self, full_path: str, files: 're.Pattern', keep: List[str], log_lev
272274
elif (filename.startswith("test_") and ext == ".rs" and
273275
files.search(filename)):
274276
rs_test_file = self._read_rust_test_file(path)
275-
276277
self.rs_test_files.append(rs_test_file)
277278

278279
def _read_c_file(self, path: str) -> Optional[CFile]:
@@ -453,7 +454,6 @@ def run(self) -> List[TestOutcome]:
453454
rust_file_builder.add_mod(RustMod(extensionless_rust_file,
454455
RustVisibility.Public))
455456

456-
match_arms = []
457457
rustc_extra_args = ["-C", "target-cpu=native"]
458458

459459
# Build one binary that can call all the tests
@@ -464,6 +464,8 @@ def run(self) -> List[TestOutcome]:
464464

465465
_, file_name = os.path.split(test_file.path)
466466
extensionless_file_name, _ = os.path.splitext(file_name)
467+
if test_file.pass_expected:
468+
rust_file_builder.add_mod(RustMod(extensionless_file_name, RustVisibility.Private))
467469

468470
if not test_file.pass_expected:
469471
try:
@@ -486,32 +488,11 @@ def run(self) -> List[TestOutcome]:
486488

487489
continue
488490

489-
for test_function in test_file.test_functions:
490-
rust_file_builder.add_mod(RustMod(extensionless_file_name,
491-
RustVisibility.Public))
492-
left = "Some(\"{}::{}\")".format(extensionless_file_name,
493-
test_function.name)
494-
right = "{}::{}()".format(extensionless_file_name,
495-
test_function.name)
496-
match_arms.append((left, right))
497-
498-
match_arms.append(("e",
499-
"panic!(\"Tried to run unknown test: {:?}\", e)"))
500-
501-
test_main_body = [
502-
RustMatch("std::env::args().nth(1).as_ref().map(AsRef::<str>::as_ref)", match_arms),
503-
]
504-
test_main = RustFunction("main",
505-
visibility=RustVisibility.Public,
506-
body=[str(stmt) for stmt in test_main_body])
507-
508-
rust_file_builder.add_function(test_main)
491+
lib_file = rust_file_builder.build(self.full_path + "/src/lib.rs")
509492

510-
main_file = rust_file_builder.build(self.full_path + "/src/main.rs")
493+
self.generated_files["rust_src"].append(lib_file)
511494

512-
self.generated_files["rust_src"].append(main_file)
513-
514-
# Try and build test binary
495+
# Build
515496
with pb.local.cwd(self.full_path):
516497
args = ["build"]
517498

@@ -524,65 +505,45 @@ def run(self) -> List[TestOutcome]:
524505
retcode, stdout, stderr = cargo[args].run(retcode=None)
525506

526507
if retcode != 0:
527-
_, main_file_path_short = os.path.split(main_file.path)
508+
_, lib_file_path_short = os.path.split(lib_file.path)
528509

529-
self.print_status(Colors.FAIL, "FAILED", "compile {}".format(main_file_path_short))
510+
self.print_status(Colors.FAIL, "FAILED", "compile {}".format(lib_file_path_short))
530511
sys.stdout.write('\n')
531512
sys.stdout.write(stderr)
532513

533514
outcomes.append(TestOutcome.UnexpectedFailure)
534515

535516
return outcomes
536517

537-
for test_file in self.rs_test_files:
538-
if not test_file.pass_expected:
539-
continue
540-
541-
_, file_name = os.path.split(test_file.path)
542-
extensionless_file_name, _ = os.path.splitext(file_name)
543-
544-
for test_function in test_file.test_functions:
545-
args = ["run", "{}::{}".format(extensionless_file_name, test_function.name)]
546-
547-
if c.BUILD_TYPE == 'release':
548-
args.append('--release')
549-
550-
with pb.local.cwd(self.full_path):
551-
retcode, stdout, stderr = cargo[args].run(retcode=None)
552-
553-
logging.debug("stdout:%s\n", stdout)
554-
555-
test_str = file_name + ' - ' + test_function.name
556-
557-
if retcode == 0:
558-
if test_function.pass_expected:
559-
self.print_status(Colors.OKGREEN, "OK", " test " + test_str)
560-
sys.stdout.write('\n')
561-
562-
outcomes.append(TestOutcome.Success)
563-
else:
564-
self.print_status(Colors.FAIL, "FAILED", "test " + test_str)
565-
sys.stdout.write('\n')
518+
# Test
519+
with pb.local.cwd(self.full_path):
520+
args = ["test"]
566521

567-
outcomes.append(TestOutcome.UnexpectedSuccess)
522+
if c.BUILD_TYPE == 'release':
523+
args.append('--release')
568524

569-
elif retcode != 0:
570-
if test_function.pass_expected:
571-
self.print_status(Colors.FAIL, "FAILED", "test " + test_str)
572-
sys.stdout.write('\n')
573-
sys.stdout.write(stderr)
525+
if self.target:
526+
args += ["--target", self.target]
574527

575-
outcomes.append(TestOutcome.UnexpectedFailure)
576-
else:
577-
self.print_status(Colors.OKBLUE, "FAILED", "test " + test_str)
578-
sys.stdout.write('\n')
528+
retcode, stdout, stderr = cargo[args].run(retcode=None)
529+
530+
if retcode != 0:
531+
_, lib_file_path_short = os.path.split(lib_file.path)
579532

580-
outcomes.append(TestOutcome.Failure)
533+
self.print_status(Colors.FAIL, "FAILED", "test {}".format(lib_file_path_short))
534+
sys.stdout.write('\n')
535+
sys.stdout.write(stdout)
536+
else:
537+
sys.stdout.write("\n".join(line for line in stdout.split("\n") if "... ok" in line or "... FAILED" in line))
538+
539+
# Don't distinguish between expected and unexpected failures.
540+
# `#[should_panic]` is used for that instead of `// xfail` now.
541+
# Also, `cargo test -- --format json` is unstable, so it's easier to just parse very simply.
542+
for _ in range(stdout.count("... ok")):
543+
outcomes.append(TestOutcome.Success)
544+
for _ in range(stdout.count("... FAILED")):
545+
outcomes.append(TestOutcome.UnexpectedFailure)
581546

582-
if not outcomes:
583-
display_text = " No rust file(s) matching " + self.files.pattern
584-
display_text += " within this folder\n"
585-
self.print_status(Colors.OKBLUE, "N/A", display_text)
586547
return outcomes
587548

588549
def cleanup(self) -> None:

tests/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ extern "C" {
2323
// The length can be any value
2424
const BUFFER_SIZE: usize = 1024;
2525
26+
#[test]
2627
pub fn test_example() {
2728
let mut buffer = [0; BUFFER_SIZE];
2829
let mut rust_buffer = [0; BUFFER_SIZE];
@@ -42,7 +43,8 @@ The C code can do one of two things: modify some sort of buffer or return a valu
4243

4344
To completely skip the translation of a C file, you must add the comment `//! skip_translation` at the top of the file. That will prevent the case from showing up as red in the console output.
4445

45-
You can also mark a Rust file as unexpected to compile, by adding `//! xfail` to the top of the file, or just expect an individual test function to fail to run by adding `// xfail` prior to the function definition.
46+
You can also mark a Rust file as unexpected to compile by adding `//! xfail` to the top of the file.
47+
For an individual test function, use the normal Rust `#[should_panic]` for `#[test]`s.
4648

4749
Adding `//! extern_crate_X` to the top of a test file will ensure `extern crate X;` gets added to the main binary driver.
4850

tests/arrays/src/test_arrays.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,21 @@ const BUFFER_SIZE: usize = 49;
2727
const BUFFER_SIZE2: usize = 2;
2828
const BUFFER_SIZEV: usize = 88;
2929

30+
#[test]
3031
pub fn test_sized_array_impls() {
3132
unsafe {
3233
assert_eq!(rust_test_sized_array(), test_sized_array());
3334
}
3435
}
3536

37+
#[test]
3638
pub fn test_global_incomplete_array() {
3739
unsafe {
3840
assert_eq!(rust_check_some_ints(), check_some_ints());
3941
}
4042
}
4143

44+
#[test]
4245
pub fn test_buffer() {
4346
let mut buffer = [0; BUFFER_SIZE];
4447
let mut rust_buffer = [0; BUFFER_SIZE];
@@ -59,6 +62,7 @@ pub fn test_buffer() {
5962
}
6063
}
6164

65+
#[test]
6266
pub fn test_buffer2() {
6367
let mut buffer = [0; BUFFER_SIZE2];
6468
let mut rust_buffer = [0; BUFFER_SIZE2];
@@ -73,6 +77,7 @@ pub fn test_buffer2() {
7377
assert_eq!(buffer, expected_buffer);
7478
}
7579

80+
#[test]
7681
pub fn test_variable_arrays() {
7782
let mut buffer = [0; BUFFER_SIZEV];
7883
let mut rust_buffer = [0; BUFFER_SIZEV];
@@ -93,6 +98,7 @@ pub fn test_variable_arrays() {
9398
}
9499
}
95100

101+
#[test]
96102
pub fn test_alloca_arrays() {
97103
let mut buffer = [0; BUFFER_SIZEV];
98104
let mut rust_buffer = [0; BUFFER_SIZEV];

tests/asm.aarch64/src/test_asm.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern "C" {
1010

1111
const BUFFER_SIZE: usize = 6;
1212

13+
#[test]
1314
pub fn test_buffer() {
1415
let mut buffer = [0; BUFFER_SIZE];
1516
let mut rust_buffer = [0; BUFFER_SIZE];

tests/asm.arm/src/test_asm.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extern "C" {
88

99
const BUFFER_SIZE: usize = 1;
1010

11+
#[test]
1112
pub fn test_buffer() {
1213
let mut buffer = [0; BUFFER_SIZE];
1314
let mut rust_buffer = [0; BUFFER_SIZE];

tests/asm.x86_64/src/test_asm.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern "C" {
1010

1111
const BUFFER_SIZE: usize = 6;
1212

13+
#[test]
1314
pub fn test_buffer() {
1415
let mut buffer = [0; BUFFER_SIZE];
1516
let mut rust_buffer = [0; BUFFER_SIZE];

tests/builtins/src/test_builtins.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ extern "C" {
2121
const BUFFER_SIZE: usize = 1024;
2222
const BUFFER_SIZE2: usize = 10;
2323

24+
#[test]
2425
pub fn test_atomics() {
2526
let mut buffer = [0; BUFFER_SIZE];
2627
let mut rust_buffer = [0; BUFFER_SIZE];
@@ -35,6 +36,7 @@ pub fn test_atomics() {
3536
}
3637
}
3738

39+
#[test]
3840
pub fn test_new_atomics() {
3941
let mut buffer = [0; BUFFER_SIZE];
4042
let mut rust_buffer = [0; BUFFER_SIZE];
@@ -50,6 +52,7 @@ pub fn test_new_atomics() {
5052
}
5153
}
5254

55+
#[test]
5356
pub fn test_mem_fns() {
5457
let const_string = "I am ten!\0";
5558
let mut buffer = [0; BUFFER_SIZE2];
@@ -66,6 +69,7 @@ pub fn test_mem_fns() {
6669
}
6770
}
6871

72+
#[test]
6973
pub fn test_ffs() {
7074
for i in 0..256 {
7175
let ffs_ret = unsafe { ffs(i) };
@@ -85,6 +89,7 @@ pub fn test_ffs() {
8589
}
8690
}
8791

92+
#[test]
8893
pub fn test_clang9_intrinsics() {
8994
let pinf = 1.0 / 0.0;
9095
let ninf = -1.0 / 0.0;
@@ -121,6 +126,7 @@ pub fn test_clang9_intrinsics() {
121126
}
122127
}
123128

129+
#[test]
124130
pub fn test_assume_aligned() {
125131
let null = std::ptr::null_mut();
126132

tests/casts/src/test_casts.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ extern "C" {
1818

1919
const BUFFER_SIZE: usize = 1;
2020

21+
#[test]
2122
pub fn test_compiles() {
2223
unsafe {
2324
cast_stuff();
2425
rust_cast_stuff();
2526
}
2627
}
2728

29+
#[test]
2830
pub fn test_buffer() {
2931
let mut buffer = [0; BUFFER_SIZE];
3032
let mut rust_buffer = [0; BUFFER_SIZE];
@@ -37,6 +39,7 @@ pub fn test_buffer() {
3739
assert_eq!(buffer, rust_buffer);
3840
}
3941

42+
#[test]
4043
pub fn test_identity() {
4144
for i in 0..10 {
4245
let id = unsafe { identity(i) };

0 commit comments

Comments
 (0)