Skip to content

Commit 7528dde

Browse files
committed
Test: Add tests for custom exception and exception forwarding
1 parent c45ce05 commit 7528dde

File tree

4 files changed

+115
-1
lines changed

4 files changed

+115
-1
lines changed

tests/ffi/lib.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,30 @@
1515
pub mod cast;
1616
pub mod module;
1717

18-
use cxx::{type_id, CxxString, CxxVector, ExternType, SharedPtr, UniquePtr};
18+
use cxx::{
19+
type_id, CxxException, CxxString, CxxVector, ExternType, SharedPtr, ToCxxException, UniquePtr,
20+
};
1921
use std::fmt::{self, Display};
2022
use std::mem::MaybeUninit;
2123
use std::os::raw::c_char;
2224

25+
/// A custom error with special exception handling.
26+
pub struct CustomError {
27+
pub data: i32,
28+
}
29+
30+
impl CustomError {
31+
pub fn get_data(&self) -> i32 {
32+
self.data
33+
}
34+
}
35+
36+
impl ToCxxException for CustomError {
37+
fn to_cxx_exception(&self) -> CxxException {
38+
ffi::make_custom_exception(self)
39+
}
40+
}
41+
2342
#[cxx::bridge(namespace = "tests")]
2443
pub mod ffi {
2544
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
@@ -218,6 +237,21 @@ pub mod ffi {
218237
fn ns_c_take_ns_shared(shared: AShared);
219238
}
220239

240+
extern "Rust" {
241+
type CustomError;
242+
fn get_data(&self) -> i32;
243+
}
244+
245+
unsafe extern "C++" {
246+
include!("tests/ffi/tests.h");
247+
248+
fn make_custom_exception(error: &CustomError) -> CxxException;
249+
fn catch_custom_exception() -> Result<()>;
250+
251+
fn forward_exception_inner() -> Result<()>;
252+
fn forward_exception_outer() -> Result<()>;
253+
}
254+
221255
extern "C++" {
222256
include!("tests/ffi/module.rs.h");
223257

@@ -312,6 +346,9 @@ pub mod ffi {
312346

313347
#[cxx_name = "rAliasedFunction"]
314348
fn r_aliased_function(x: i32) -> String;
349+
350+
fn throw_custom_exception() -> Result<()>;
351+
fn forward_exception_middle() -> Result<()>;
315352
}
316353

317354
struct Dag0 {
@@ -646,3 +683,14 @@ fn r_try_return_mutsliceu8(slice: &mut [u8]) -> Result<&mut [u8], Error> {
646683
fn r_aliased_function(x: i32) -> String {
647684
x.to_string()
648685
}
686+
687+
fn throw_custom_exception() -> Result<(), CustomError> {
688+
Err(CustomError { data: 4711 })
689+
}
690+
691+
fn forward_exception_middle() -> Result<(), CxxException> {
692+
ffi::forward_exception_inner().map_err(|e| {
693+
assert_eq!(e.what(), "forward test exc");
694+
e.into()
695+
})
696+
}

tests/ffi/tests.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,49 @@ extern "C" const char *cxx_run_test() noexcept {
902902
return nullptr;
903903
}
904904

905+
std::exception_ptr make_custom_exception(const CustomError &error) {
906+
auto data = error.get_data();
907+
std::string msg("custom ");
908+
msg += std::to_string(data);
909+
return std::make_exception_ptr(std::logic_error(msg));
910+
}
911+
912+
void catch_custom_exception() {
913+
// call a Rust function throwing a custom error
914+
try {
915+
throw_custom_exception();
916+
} catch (std::logic_error &ex) {
917+
// yes, we caught the expected exception, just rethrow it to the outer
918+
// call from Rust to evaluate
919+
throw;
920+
} catch (...) {
921+
// something wrong, throw an exception with a different message, so the
922+
// test will fail
923+
throw std::logic_error("unexpected exception caught in the outer layer");
924+
}
925+
}
926+
927+
void forward_exception_inner() {
928+
// throw a special exception in the inner call
929+
throw std::logic_error("forward test exc");
930+
}
931+
932+
void forward_exception_outer() {
933+
try {
934+
// call Rust function, which calls `forward_exception_inner()` to generate
935+
// the exception, which is passed 1:1 through the Rust layer
936+
forward_exception_middle();
937+
} catch (std::logic_error &) {
938+
// yes, we caught the expected exception, just rethrow it to the outer
939+
// call from Rust to evaluate
940+
throw;
941+
} catch (...) {
942+
// something wrong, throw an exception with a different message, so the
943+
// test will fail
944+
throw std::logic_error("unexpected exception caught in the outer layer");
945+
}
946+
}
947+
905948
} // namespace tests
906949

907950
namespace other {

tests/ffi/tests.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,13 @@ std::unique_ptr<::F::F> c_return_ns_opaque_ptr();
215215
rust::String cOverloadedFunction(int32_t x);
216216
rust::String cOverloadedFunction(rust::Str x);
217217

218+
struct CustomError;
219+
std::exception_ptr make_custom_exception(const CustomError &error);
220+
void catch_custom_exception();
221+
222+
void forward_exception_inner();
223+
void forward_exception_outer();
224+
218225
} // namespace tests
219226

220227
namespace other {

tests/test.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,3 +378,19 @@ fn test_raw_ptr() {
378378
assert_eq!(2025, unsafe { ffi::c_take_const_ptr(c3) });
379379
assert_eq!(2025, unsafe { ffi::c_take_mut_ptr(c3 as *mut ffi::C) }); // deletes c3
380380
}
381+
382+
/// Test throwing a custom exception from a Rust call via `ToCxxException`
383+
/// trait.
384+
#[test]
385+
fn test_custom_exception() {
386+
let err = ffi::catch_custom_exception().expect_err("Error expected");
387+
assert_eq!(err.what(), "custom 4711");
388+
}
389+
390+
/// Test forwarding the exception from the inner C++ function via a middle Rust
391+
/// function into the outer C++ function unmodified.
392+
#[test]
393+
fn test_forward_exception() {
394+
let err = ffi::forward_exception_outer().expect_err("Error expected");
395+
assert_eq!(err.what(), "forward test exc");
396+
}

0 commit comments

Comments
 (0)