@@ -618,6 +618,54 @@ TEST_P(FastpathIoParamWithFallback, IntegrationRunWithFallback)
618618 ASSERT_EQ (num_bytes, DEFAULT_IO_SIZE);
619619}
620620
621+ // If the fallback backend rejects the IO, the original exception from
622+ // Fastpath should be raised.
623+ TEST_P (FastpathIoParamWithFallback, IntegrationFallbackRejectsIO)
624+ {
625+ StrictMock<MHip> mhip;
626+ StrictMock<MSys> msys;
627+
628+ auto fallback_backend = std::make_shared<StrictMock<Fallback>>();
629+ auto fastpath_backend = std::make_shared<StrictMock<Fastpath>>();
630+ fastpath_backend->register_fallback_backend (fallback_backend);
631+
632+ // Called only by Fastpath
633+ EXPECT_CALL (*mbuffer, getBuffer).WillOnce (Return (DEFAULT_BUFFER_ADDR));
634+ EXPECT_CALL (*mbuffer, getLength).WillOnce (Return (DEFAULT_BUFFER_LENGTH));
635+ EXPECT_CALL (*mfile, getUnbufferedFd).WillOnce (Return (DEFAULT_UNBUFFERED_FD));
636+ // Called only by Fallback - should fail the score() check.
637+ EXPECT_CALL (*mbuffer, getType).WillOnce (Return (hipMemoryTypeHost));
638+ switch (_get_param_io_type ()) {
639+ case IoType::Read:
640+ // Called by Fastpath
641+ EXPECT_CALL (mhip, hipAmdFileRead).WillOnce (Rethrow (_get_param_exc_ptr ()));
642+ break ;
643+ case IoType::Write:
644+ // Called by Fastpath
645+ EXPECT_CALL (mhip, hipAmdFileWrite).WillOnce (Rethrow (_get_param_exc_ptr ()));
646+ break ;
647+ default :
648+ FAIL () << " Invalid IoType" ;
649+ }
650+
651+ // Have to rethrow the exception_ptr to be able to access the exception
652+ try {
653+ std::rethrow_exception (_get_param_exc_ptr ());
654+ } catch (const std::exception &expected_exc) {
655+ // Can't use EXPECT_THROW due to the thrown exception type only being known at runtime
656+ try {
657+ fastpath_backend->io (_get_param_io_type (), mfile, mbuffer, DEFAULT_IO_SIZE, 0 , 0 );
658+ } catch (const std::exception &actual_exc) {
659+ // Note: std::exception_ptr preserves the original thrown exception rather than
660+ // creating a new one. This allows us to verify that the expected and
661+ // actual exceptions refer to the same underlying exception object.
662+ ASSERT_EQ (&expected_exc, &actual_exc);
663+ } catch (...) {
664+ FAIL () << " io() threw something other than a std::exception" ;
665+ }
666+ }
667+ }
668+
621669// Using std::exception_ptr is more straightforward here than storing a pointer
622670// to a derived std::exception type, which would require careful handling when
623671// setting expectations. Note that Throw() does not accept std::exception_ptr,
0 commit comments