@@ -359,6 +359,66 @@ void testProgressListener(@TempDir Path tempDir) throws Exception {
359359 }
360360 }
361361
362+ @ Test
363+ void testResumeUpload (@ TempDir Path tempDir ) throws Exception {
364+ int totalBytes = 600 * 1024 ;
365+ int unalignedChunkSize = 300 * 1024 ;
366+ Path file = createTempFile (tempDir , "it-resume.txt" , totalBytes );
367+ UploadMediaRequest request = UploadMediaRequest .newBuilder ().setName ("it-resume.txt" ).build ();
368+ ApiCallContext failSecondChunkContext = faultInjectionContext (403 , 1 , SHOWCASE_CHUNK_SIZE );
369+
370+ ResumableUploadFuture <UploadMediaResponse > firstAttemptFuture =
371+ client
372+ .uploadMediaCallable ()
373+ .futureCall (
374+ request ,
375+ () -> Files .newInputStream (file ),
376+ failSecondChunkContext ,
377+ ResumableUploadOptions .newBuilder ().setChunkSize (unalignedChunkSize ).build ());
378+
379+ assertThrows (ExecutionException .class , () -> firstAttemptFuture .get (15 , TimeUnit .SECONDS ));
380+ String sessionUrl = firstAttemptFuture .getUploadSessionUrl ();
381+ Integer effectiveChunkSize = firstAttemptFuture .getEffectiveChunkSize ();
382+ assertThat (sessionUrl ).isNotNull ();
383+ assertThat (effectiveChunkSize ).isEqualTo (SHOWCASE_CHUNK_SIZE );
384+
385+ List <ResumableUploadProgress > resumeProgress = new CopyOnWriteArrayList <>();
386+ CountDownLatch finalizedLatch = new CountDownLatch (1 );
387+ ResumableUploadOptions resumeOptions =
388+ ResumableUploadOptions .newBuilder ()
389+ .setChunkSize (effectiveChunkSize )
390+ .setProgressListener (
391+ status -> {
392+ resumeProgress .add (status );
393+ if ("FINALIZED" .equals (status .getState ())) {
394+ finalizedLatch .countDown ();
395+ }
396+ })
397+ .build ();
398+
399+ ResumableUploadFuture <UploadMediaResponse > secondAttemptFuture =
400+ client
401+ .uploadMediaCallable ()
402+ .resumeCall (sessionUrl , () -> Files .newInputStream (file ), resumeOptions );
403+ UploadMediaResponse response = secondAttemptFuture .get (30 , TimeUnit .SECONDS );
404+ assertThat (finalizedLatch .await (10 , TimeUnit .SECONDS )).isTrue ();
405+
406+ assertThat (response .getName ()).isEqualTo ("it-resume.txt" );
407+ assertThat (response .getSize ()).isEqualTo (totalBytes );
408+ assertThat (
409+ resumeProgress .stream ()
410+ .map (ResumableUploadProgress ::getState )
411+ .collect (Collectors .toList ()))
412+ .containsExactly ("RECOVERING" , "OFFSET_RECEIVED" , "UPLOADING" , "FINALIZED" )
413+ .inOrder ();
414+ assertThat (
415+ resumeProgress .stream ()
416+ .map (ResumableUploadProgress ::getBytesUploaded )
417+ .collect (Collectors .toList ()))
418+ .containsExactly (0L , 262144L , 524288L , 614400L )
419+ .inOrder ();
420+ }
421+
362422 private static Path createTempFile (Path dir , String fileName , byte [] data ) throws IOException {
363423 Path path = dir .resolve (fileName );
364424 Files .write (path , data );
0 commit comments