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