2727import com .google .api .gax .rpc .AbortedException ;
2828import com .google .api .gax .rpc .ApiExceptions ;
2929import com .google .api .gax .rpc .CancelledException ;
30+ import com .google .api .gax .rpc .OutOfRangeException ;
3031import com .google .api .gax .rpc .UnavailableException ;
3132import com .google .cloud .storage .Crc32cValue .Crc32cLengthKnown ;
3233import com .google .cloud .storage .Hasher .ChecksumMismatchException ;
@@ -628,7 +629,6 @@ public void onNext(BidiReadObjectRequest request) {
628629
629630 assertThat (cancelledException ).hasCauseThat ().isInstanceOf (ChecksumMismatchException .class );
630631 Throwable [] suppressed = cancelledException .getSuppressed ();
631- assertThat (suppressed ).asList ().hasSize (3 );
632632 List <String > suppressedMessages =
633633 Arrays .stream (suppressed ).map (Throwable ::getMessage ).collect (Collectors .toList ());
634634 assertThat (suppressedMessages )
@@ -640,6 +640,70 @@ public void onNext(BidiReadObjectRequest request) {
640640 }
641641 }
642642
643+ @ Test
644+ public void retrySettingsApplicable_objectRangeData_offset_notAligned_gt () throws Exception {
645+
646+ ChecksummedTestContent content2 = ChecksummedTestContent .of (ALL_OBJECT_BYTES , 11 , 20 );
647+ BidiReadObjectRequest req2 = read (1 , 10 , 20 );
648+ BidiReadObjectResponse res2 =
649+ BidiReadObjectResponse .newBuilder ()
650+ .addObjectDataRanges (
651+ ObjectRangeData .newBuilder ()
652+ .setChecksummedData (content2 .asChecksummedData ())
653+ .setReadRange (getReadRange (1 , 11 , content2 ))
654+ .setRangeEnd (true )
655+ .build ())
656+ .build ();
657+
658+ ChecksummedTestContent content3 = ChecksummedTestContent .of (ALL_OBJECT_BYTES , 12 , 20 );
659+ BidiReadObjectRequest req3 = read (2 , 10 , 20 );
660+ BidiReadObjectResponse res3 =
661+ BidiReadObjectResponse .newBuilder ()
662+ .addObjectDataRanges (
663+ ObjectRangeData .newBuilder ()
664+ .setChecksummedData (content3 .asChecksummedData ())
665+ .setReadRange (getReadRange (2 , 12 , content3 ))
666+ .setRangeEnd (true )
667+ .build ())
668+ .build ();
669+
670+ ImmutableMap <BidiReadObjectRequest , BidiReadObjectResponse > db =
671+ ImmutableMap .<BidiReadObjectRequest , BidiReadObjectResponse >builder ()
672+ .put (REQ_OPEN , RES_OPEN )
673+ .put (req2 , res2 )
674+ .put (req3 , res3 )
675+ .buildOrThrow ();
676+
677+ try (FakeServer fakeServer = FakeServer .of (FakeStorage .from (db ));
678+ Storage storage =
679+ fakeServer
680+ .getGrpcStorageOptions ()
681+ .toBuilder ()
682+ .setRetrySettings (RetrySettings .newBuilder ().setMaxAttempts (2 ).build ())
683+ .build ()
684+ .getService ()) {
685+
686+ BlobId id = BlobId .of ("b" , "o" );
687+ ApiFuture <BlobDescriptor > futureObjectDescriptor = storage .getBlobDescriptor (id );
688+
689+ try (BlobDescriptor bd = futureObjectDescriptor .get (5 , TimeUnit .SECONDS )) {
690+ ApiFuture <byte []> future = bd .readRangeAsBytes (RangeSpec .of (10L , 20L ));
691+
692+ CancelledException cancelledException =
693+ assertThrows (
694+ CancelledException .class , () -> ApiExceptions .callAndTranslateApiException (future ));
695+
696+ assertThat (cancelledException ).hasCauseThat ().isInstanceOf (OutOfRangeException .class );
697+ Throwable [] suppressed = cancelledException .getSuppressed ();
698+ List <String > suppressedMessages =
699+ Arrays .stream (suppressed ).map (Throwable ::getMessage ).collect (Collectors .toList ());
700+ assertThat (suppressedMessages )
701+ .containsExactly (
702+ "position = 10, readRange.read_offset = 11" , "Asynchronous task failed" );
703+ }
704+ }
705+ }
706+
643707 private static void runTestAgainstFakeServer (
644708 FakeStorage fakeStorage , RangeSpec range , ChecksummedTestContent expected ) throws Exception {
645709
0 commit comments