@@ -525,6 +525,82 @@ func TestIntegration_MRDCallbackReturnsDataLength(t *testing.T) {
525525 }
526526 })
527527}
528+ func TestIntegration_MRDWithReadHandle (t * testing.T ) {
529+ multiTransportTest (skipAllButZonal (context .Background (), "Bidi Read API test" ), t , func (t * testing.T , ctx context.Context , bucket string , _ string , client * Client ) {
530+ const (
531+ dataSize = 1000
532+ offset = 0
533+ limit = 100
534+ )
535+
536+ // Generate random content for testing.
537+ content := make ([]byte , dataSize )
538+ rand .New (rand .NewSource (0 )).Read (content )
539+ objName := "MRDWithReadHandle"
540+
541+ // Upload test data.
542+ obj := client .Bucket (bucket ).Object (objName )
543+ if err := writeObject (ctx , obj , "text/plain" , content ); err != nil {
544+ t .Fatalf ("Failed to upload test object %q: %v" , objName , err )
545+ }
546+ // Ensure cleanup after the test.
547+ defer func () {
548+ if err := obj .Delete (ctx ); err != nil {
549+ t .Logf ("Failed to delete test object %q: %v" , objName , err )
550+ }
551+ }()
552+
553+ mrd , err := obj .NewMultiRangeDownloader (ctx )
554+ if err != nil {
555+ t .Fatalf ("Failed to create MultiRangeDownloader: %v" , err )
556+ }
557+ readHandle := mrd .GetHandle ()
558+ obj = obj .ReadHandle (readHandle )
559+ mrd2 , err := obj .NewMultiRangeDownloader (ctx )
560+ if err != nil {
561+ t .Fatalf ("Failed to create MultiRangeDownloader with read handle: %v" , err )
562+ }
563+
564+ // Perform the read operation.
565+ var res1 , res2 multiRangeDownloaderOutput
566+ mrd .Add (& res1 .buf , offset , limit , func (x , y int64 , err error ) {
567+ res1 .offset = x
568+ res1 .limit = y
569+ res1 .err = err
570+ })
571+ mrd2 .Add (& res2 .buf , offset , limit , func (x , y int64 , err error ) {
572+ res2 .offset = x
573+ res2 .limit = y
574+ res2 .err = err
575+ })
576+
577+ mrd .Wait ()
578+ mrd2 .Wait ()
579+ if res1 .err != nil {
580+ t .Fatalf ("mrd.Add callback returned error: %v" , res1 .err )
581+ }
582+ if res2 .err != nil {
583+ t .Fatalf ("mrd2.Add callback returned error: %v" , res2 .err )
584+ }
585+
586+ // Validate results for mrd with read handle.
587+ want := content [offset : offset + limit ]
588+
589+ if res2 .offset != offset || res2 .limit != limit {
590+ t .Errorf ("mrd2.Add callback offset/limit got %d/%d, want %d/%d" , res2 .offset , res2 .limit , offset , limit )
591+ }
592+ if got := res2 .buf .Bytes (); ! bytes .Equal (got , want ) {
593+ t .Errorf ("mrd2 downloaded content mismatch. got %d bytes, want %d bytes" , len (got ), len (want ))
594+ }
595+
596+ if err := mrd .Close (); err != nil {
597+ t .Fatalf ("Error while closing reader: %v" , err )
598+ }
599+ if err := mrd2 .Close (); err != nil {
600+ t .Fatalf ("Error while closing reader created with read handle: %v" , err )
601+ }
602+ })
603+ }
528604
529605// TestIntegration_ReadSameFileConcurrentlyUsingMultiRangeDownloader tests for potential deadlocks
530606// or race conditions when multiple goroutines call Add() concurrently on the same MRD multiple times.
0 commit comments