@@ -564,3 +564,88 @@ fn virtq_multi_descriptor_h2g_repeated_calls() {
564564 }
565565 } ) ;
566566}
567+
568+ /// Helper to create a sandbox with a "GetLargeResponse" host function
569+ /// that returns `size` bytes filled with 0xAB.
570+ fn sandbox_with_large_response ( cfg : SandboxConfiguration ) -> MultiUseSandbox {
571+ let mut sandbox = UninitializedSandbox :: new (
572+ GuestBinary :: FilePath ( simple_guest_as_string ( ) . unwrap ( ) ) ,
573+ Some ( cfg) ,
574+ )
575+ . unwrap ( ) ;
576+ sandbox
577+ . register ( "GetLargeResponse" , |size : i32 | -> Result < Vec < u8 > > {
578+ Ok ( vec ! [ 0xABu8 ; size as usize ] )
579+ } )
580+ . unwrap ( ) ;
581+ sandbox. evolve ( ) . unwrap ( )
582+ }
583+
584+ #[ test]
585+ fn virtq_large_g2h_response_with_hint ( ) {
586+ // Host function returns >4096 bytes. Guest uses a sized completion
587+ // hint so the pool allocates multiple adjacent slots.
588+ let mut cfg = SandboxConfiguration :: default ( ) ;
589+ cfg. set_g2h_pool_pages ( 16 ) ;
590+ let mut sandbox = sandbox_with_large_response ( cfg) ;
591+
592+ // 8000 bytes of response payload. With FlatBuffer + header overhead,
593+ // the wire size is ~8100 bytes. A hint of 3*4096 = 12288 is enough.
594+ let hint = 3 * 4096i32 ;
595+ let res: Vec < u8 > = sandbox
596+ . call ( "CallGetLargeResponseWithHint" , ( 8000i32 , hint) )
597+ . unwrap ( ) ;
598+ assert_eq ! ( res. len( ) , 8000 ) ;
599+ assert ! ( res. iter( ) . all( |& b| b == 0xAB ) ) ;
600+ }
601+
602+ #[ test]
603+ fn virtq_large_g2h_response_too_large_without_hint ( ) {
604+ // Without a hint, the default 4096-byte completion buffer is used.
605+ // A response >4096 bytes should trigger the host's "response too
606+ // large" fallback error instead of a transport crash.
607+ let mut cfg = SandboxConfiguration :: default ( ) ;
608+ cfg. set_g2h_pool_pages ( 16 ) ;
609+ let mut sandbox = sandbox_with_large_response ( cfg) ;
610+
611+ let res = sandbox. call :: < Vec < u8 > > ( "CallGetLargeResponseDefault" , 8000i32 ) ;
612+ assert ! (
613+ res. is_err( ) ,
614+ "expected error for oversized response without hint"
615+ ) ;
616+ }
617+
618+ #[ test]
619+ fn virtq_large_g2h_response_boundary ( ) {
620+ // Response that fits exactly in one page (with overhead) should work
621+ // without needing a hint.
622+ let mut cfg = SandboxConfiguration :: default ( ) ;
623+ cfg. set_g2h_pool_pages ( 16 ) ;
624+ let mut sandbox = sandbox_with_large_response ( cfg) ;
625+
626+ // Small response that fits in default 4096 buffer
627+ let res: Vec < u8 > = sandbox
628+ . call ( "CallGetLargeResponseDefault" , 1000i32 )
629+ . unwrap ( ) ;
630+ assert_eq ! ( res. len( ) , 1000 ) ;
631+ assert ! ( res. iter( ) . all( |& b| b == 0xAB ) ) ;
632+ }
633+
634+ #[ test]
635+ fn virtq_large_g2h_response_after_log_backpressure ( ) {
636+ // Logs fill the G2H pool, then a large host response (with hint)
637+ // needs multi-slot allocation. The backpressure path must drain
638+ // completed log entries to free pool slots for the large completion.
639+ let mut cfg = SandboxConfiguration :: default ( ) ;
640+ cfg. set_g2h_pool_pages ( 16 ) ;
641+ let mut sandbox = sandbox_with_large_response ( cfg) ;
642+
643+ // Emit 20 log entries to consume pool slots, then request 8KB
644+ // response with a 12KB hint (3 upper-slab slots).
645+ let hint = 3 * 4096i32 ;
646+ let res: Vec < u8 > = sandbox
647+ . call ( "LogThenLargeResponse" , ( 20i32 , 8000i32 , hint) )
648+ . unwrap ( ) ;
649+ assert_eq ! ( res. len( ) , 8000 ) ;
650+ assert ! ( res. iter( ) . all( |& b| b == 0xAB ) ) ;
651+ }
0 commit comments