Repository: googleapis/java-storage
Pull request #1646 (2022-09-22 18:19:26)

test: Stub tests for GRPC integration
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
- [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/java-storage/issues/new/choose) before writing your code!  That way we can discuss the change, evaluate designs, and agree on the general idea
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)

Fixes #<issue_number_goes_here> ☕️

If you write sample code, please follow the [samples format](
https://github.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md).



Patch:
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannel.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannel.java
index 442335b6..c4e15733 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannel.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannel.java
@@ -23,6 +23,7 @@ import com.google.cloud.storage.UnbufferedWritableByteChannelSession.UnbufferedW
 import com.google.cloud.storage.WriteCtx.WriteObjectRequestBuilderFactory;
 import com.google.cloud.storage.WriteFlushStrategy.Flusher;
 import com.google.cloud.storage.WriteFlushStrategy.FlusherFactory;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.protobuf.ByteString;
 import com.google.storage.v2.ChecksummedData;
 import com.google.storage.v2.ObjectChecksums;
@@ -58,9 +59,7 @@ final class GapicUnbufferedWritableByteChannel<
     this.writeCtx = new WriteCtx<>(requestFactory);
     this.flusher =
         flusherFactory.newFlusher(
-            requestFactory.bucketName(),
-            writeCtx.getConfirmedBytes()::addAndGet,
-            resultFuture::set);
+            requestFactory.bucketName(), writeCtx.getConfirmedBytes()::set, resultFuture::set);
   }
 
   @Override
@@ -157,4 +156,9 @@ final class GapicUnbufferedWritableByteChannel<
     }
     open = false;
   }
+
+  @VisibleForTesting
+  WriteCtx<RequestFactoryT> getWriteCtx() {
+    return writeCtx;
+  }
 }
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicWritableByteChannelSessionBuilder.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicWritableByteChannelSessionBuilder.java
index f34c1c67..a3b5b5ab 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicWritableByteChannelSessionBuilder.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GapicWritableByteChannelSessionBuilder.java
@@ -21,9 +21,11 @@ import static java.util.Objects.requireNonNull;
 import com.google.api.core.ApiFuture;
 import com.google.api.core.ApiFutures;
 import com.google.api.core.SettableApiFuture;
+import com.google.api.gax.retrying.ResultRetryAlgorithm;
 import com.google.api.gax.rpc.ClientStreamingCallable;
 import com.google.cloud.storage.ChannelSession.BufferedWriteSession;
 import com.google.cloud.storage.ChannelSession.UnbufferedWriteSession;
+import com.google.cloud.storage.Retrying.RetryingDependencies;
 import com.google.cloud.storage.UnbufferedWritableByteChannelSession.UnbufferedWritableByteChannel;
 import com.google.cloud.storage.WriteCtx.WriteObjectRequestBuilderFactory;
 import com.google.cloud.storage.WriteFlushStrategy.FlusherFactory;
@@ -165,10 +167,70 @@ final class GapicWritableByteChannelSessionBuilder {
     private BufferedDirectUploadBuilder buffered(BufferHandle bufferHandle) {
       return new BufferedDirectUploadBuilder(bufferHandle);
     }
+
+    final class UnbufferedDirectUploadBuilder {
+
+      private WriteObjectRequest req;
+
+      /** Specify the {@link WriteObjectRequest} which will be used to start the Write stream. */
+      UnbufferedDirectUploadBuilder setRequest(WriteObjectRequest req) {
+        this.req = requireNonNull(req, "req must be non null");
+        return this;
+      }
+
+      UnbufferedWritableByteChannelSession<WriteObjectResponse> build() {
+        return new UnbufferedWriteSession<>(
+            ApiFutures.immediateFuture(requireNonNull(req, "req must be non null")),
+            bindFunction(
+                    WriteFlushStrategy.fsyncOnClose(write),
+                    WriteObjectRequestBuilderFactory::simple)
+                .andThen(StorageByteChannels.writable()::createSynchronized));
+      }
+    }
+
+    final class BufferedDirectUploadBuilder {
+
+      private final BufferHandle bufferHandle;
+      private WriteObjectRequest req;
+
+      BufferedDirectUploadBuilder(BufferHandle bufferHandle) {
+        this.bufferHandle = bufferHandle;
+      }
+
+      /** Specify the {@link WriteObjectRequest} which will be used to start the Write stream. */
+      BufferedDirectUploadBuilder setRequest(WriteObjectRequest req) {
+        this.req = requireNonNull(req, "req must be non null");
+        return this;
+      }
+
+      BufferedWritableByteChannelSession<WriteObjectResponse> build() {
+        return new BufferedWriteSession<>(
+            ApiFutures.immediateFuture(requireNonNull(req, "req must be non null")),
+            bindFunction(
+                    WriteFlushStrategy.fsyncOnClose(write),
+                    WriteObjectRequestBuilderFactory::simple)
+                .andThen(c -> new DefaultBufferedWritableByteChannel(bufferHandle, c))
+                .andThen(StorageByteChannels.writable()::createSynchronized));
+      }
+    }
   }
 
   final class ResumableUploadBuilder {
 
+    private RetryingDependencies deps;
+    private ResultRetryAlgorithm<?> alg;
+
+    ResumableUploadBuilder() {
+      this.deps = RetryingDependencies.attemptOnce();
+      this.alg = Retrying.neverRetry();
+    }
+
+    ResumableUploadBuilder withRetryConfig(RetryingDependencies deps, ResultRetryAlgorithm<?> alg) {
+      this.deps = requireNonNull(deps, "deps must be non null");
+      this.alg = requireNonNull(alg, "alg must be non null");
+      return this;
+    }
+
     /**
      * Do not apply any intermediate buffering. Any call to {@link
      * java.nio.channels.WritableByteChannel#write(ByteBuffer)} will be segmented as is and sent to
@@ -201,98 +263,56 @@ final class GapicWritableByteChannelSessionBuilder {
     private BufferedResumableUploadBuilder buffered(BufferHandle bufferHandle) {
       return new BufferedResumableUploadBuilder(bufferHandle);
     }
-  }
-
-  final class UnbufferedDirectUploadBuilder {
-
-    private WriteObjectRequest req;
-
-    /** Specify the {@link WriteObjectRequest} which will be used to start the Write stream. */
-    UnbufferedDirectUploadBuilder setRequest(WriteObjectRequest req) {
-      this.req = requireNonNull(req, "req must be non null");
-      return this;
-    }
-
-    UnbufferedWritableByteChannelSession<WriteObjectResponse> build() {
-      return new UnbufferedWriteSession<>(
-          ApiFutures.immediateFuture(requireNonNull(req, "req must be non null")),
-          bindFunction(
-                  WriteFlushStrategy.fsyncOnClose(write), WriteObjectRequestBuilderFactory::simple)
-              .andThen(StorageByteChannels.writable()::createSynchronized));
-    }
-  }
-
-  final class BufferedDirectUploadBuilder {
-
-    private final BufferHandle bufferHandle;
-    private WriteObjectRequest req;
-
-    BufferedDirectUploadBuilder(BufferHandle bufferHandle) {
-      this.bufferHandle = bufferHandle;
-    }
-
-    /** Specify the {@link WriteObjectRequest} which will be used to start the Write stream. */
-    BufferedDirectUploadBuilder setRequest(WriteObjectRequest req) {
-      this.req = requireNonNull(req, "req must be non null");
-      return this;
-    }
-
-    BufferedWritableByteChannelSession<WriteObjectResponse> build() {
-      return new BufferedWriteSession<>(
-          ApiFutures.immediateFuture(requireNonNull(req, "req must be non null")),
-          bindFunction(
-                  WriteFlushStrategy.fsyncOnClose(write), WriteObjectRequestBuilderFactory::simple)
-              .andThen(c -> new DefaultBufferedWritableByteChannel(bufferHandle, c))
-              .andThen(StorageByteChannels.writable()::createSynchronized));
-    }
-  }
 
-  final class UnbufferedResumableUploadBuilder {
-
-    private ApiFuture<ResumableWrite> start;
-
-    /**
-     * Set the Future which will contain the ResumableWrite information necessary to open the Write
-     * stream.
-     */
-    UnbufferedResumableUploadBuilder setStartAsync(ApiFuture<ResumableWrite> start) {
-      this.start = requireNonNull(start, "start must be non null");
-      return this;
-    }
-
-    UnbufferedWritableByteChannelSession<WriteObjectResponse> build() {
-      return new UnbufferedWriteSession<>(
-          requireNonNull(start, "start must be non null"),
-          bindFunction(WriteFlushStrategy.fsyncEveryFlush(write), ResumableWrite::identity)
-              .andThen(StorageByteChannels.writable()::createSynchronized));
-    }
-  }
-
-  final class BufferedResumableUploadBuilder {
-
-    private final BufferHandle bufferHandle;
-
-    private ApiFuture<ResumableWrite> start;
-
-    BufferedResumableUploadBuilder(BufferHandle bufferHandle) {
-      this.bufferHandle = bufferHandle;
-    }
-
-    /**
-     * Set the Future which will contain the ResumableWrite information necessary to open the Write
-     * stream.
-     */
-    BufferedResumableUploadBuilder setStartAsync(ApiFuture<ResumableWrite> start) {
-      this.start = requireNonNull(start, "start must be non null");
-      return this;
+    final class UnbufferedResumableUploadBuilder {
+
+      private ApiFuture<ResumableWrite> start;
+
+      /**
+       * Set the Future which will contain the ResumableWrite information necessary to open the
+       * Write stream.
+       */
+      UnbufferedResumableUploadBuilder setStartAsync(ApiFuture<ResumableWrite> start) {
+        this.start = requireNonNull(start, "start must be non null");
+        return this;
+      }
+
+      UnbufferedWritableByteChannelSession<WriteObjectResponse> build() {
+        return new UnbufferedWriteSession<>(
+            requireNonNull(start, "start must be non null"),
+            bindFunction(
+                    WriteFlushStrategy.fsyncEveryFlush(write, deps, alg), ResumableWrite::identity)
+                .andThen(StorageByteChannels.writable()::createSynchronized));
+      }
     }
 
-    BufferedWritableByteChannelSession<WriteObjectResponse> build() {
-      return new BufferedWriteSession<>(
-          requireNonNull(start, "start must be non null"),
-          bindFunction(WriteFlushStrategy.fsyncEveryFlush(write), ResumableWrite::identity)
-              .andThen(c -> new DefaultBufferedWritableByteChannel(bufferHandle, c))
-              .andThen(StorageByteChannels.writable()::createSynchronized));
+    final class BufferedResumableUploadBuilder {
+
+      private final BufferHandle bufferHandle;
+
+      private ApiFuture<ResumableWrite> start;
+
+      BufferedResumableUploadBuilder(BufferHandle bufferHandle) {
+        this.bufferHandle = bufferHandle;
+      }
+
+      /**
+       * Set the Future which will contain the ResumableWrite information necessary to open the
+       * Write stream.
+       */
+      BufferedResumableUploadBuilder setStartAsync(ApiFuture<ResumableWrite> start) {
+        this.start = requireNonNull(start, "start must be non null");
+        return this;
+      }
+
+      BufferedWritableByteChannelSession<WriteObjectResponse> build() {
+        return new BufferedWriteSession<>(
+            requireNonNull(start, "start must be non null"),
+            bindFunction(
+                    WriteFlushStrategy.fsyncEveryFlush(write, deps, alg), ResumableWrite::identity)
+                .andThen(c -> new DefaultBufferedWritableByteChannel(bufferHandle, c))
+                .andThen(StorageByteChannels.writable()::createSynchronized));
+      }
     }
   }
 }
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcBlobWriteChannel.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcBlobWriteChannel.java
index 4ac3b3ac..de394466 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcBlobWriteChannel.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcBlobWriteChannel.java
@@ -20,10 +20,12 @@ import static com.google.cloud.storage.ByteSizeConstants._15MiB;
 import static com.google.cloud.storage.ByteSizeConstants._256KiB;
 
 import com.google.api.core.ApiFuture;
+import com.google.api.gax.retrying.ResultRetryAlgorithm;
 import com.google.api.gax.rpc.ClientStreamingCallable;
 import com.google.cloud.RestorableState;
 import com.google.cloud.WriteChannel;
 import com.google.cloud.storage.BufferedWritableByteChannelSession.BufferedWritableByteChannel;
+import com.google.cloud.storage.Retrying.RetryingDependencies;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Suppliers;
 import com.google.storage.v2.WriteObjectRequest;
@@ -40,6 +42,8 @@ final class GrpcBlobWriteChannel implements WriteChannel {
 
   GrpcBlobWriteChannel(
       ClientStreamingCallable<WriteObjectRequest, WriteObjectResponse> write,
+      RetryingDependencies deps,
+      ResultRetryAlgorithm<?> alg,
       Supplier<ApiFuture<ResumableWrite>> start) {
     lazyWriteChannel =
         new LazyWriteChannel(
@@ -51,6 +55,7 @@ final class GrpcBlobWriteChannel implements WriteChannel {
                         .setHasher(Hasher.noop())
                         .setByteStringStrategy(ByteStringStrategy.copy())
                         .resumable()
+                        .withRetryConfig(deps, alg)
                         .buffered(Buffers.allocateAligned(chunkSize, _256KiB))
                         .setStartAsync(start.get())
                         .build()));
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
index 69306fe7..10336a57 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcConversions.java
@@ -57,6 +57,8 @@ import java.time.Instant;
 import java.time.LocalDate;
 import java.time.OffsetDateTime;
 import java.time.ZoneOffset;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -779,14 +781,16 @@ final class GrpcConversions {
     if (entity != null) {
       toBuilder.setOwner(Owner.newBuilder().setEntity(entityEncode(entity)).build());
     }
-    ifNonNull(from.getMetadata(), toBuilder::putAllMetadata);
+    ifNonNull(from.getMetadata(), this::removeNullValues, toBuilder::putAllMetadata);
     ifNonNull(from.getAcl(), toImmutableListOf(objectAcl()::encode), toBuilder::addAllAcl);
     return toBuilder.build();
   }
 
   private BlobInfo blobInfoDecode(Object from) {
     BlobInfo.Builder toBuilder =
-        BlobInfo.newBuilder(BlobId.of(from.getBucket(), from.getName(), from.getGeneration()));
+        BlobInfo.newBuilder(
+            BlobId.of(
+                bucketNameCodec.decode(from.getBucket()), from.getName(), from.getGeneration()));
     ifNonNull(from.getCacheControl(), toBuilder::setCacheControl);
     ifNonNull(from.getSize(), toBuilder::setSize);
     ifNonNull(from.getContentType(), toBuilder::setContentType);
@@ -927,6 +931,12 @@ final class GrpcConversions {
     return BaseEncoding.base64().encode(Ints.toByteArray(from));
   }
 
+  private Map<String, String> removeNullValues(Map<String, String> from) {
+    Map<String, String> to = new HashMap<>(from);
+    to.values().removeAll(Collections.singleton(null));
+    return to;
+  }
+
   private static <T> T todo() {
     throw new IllegalStateException("Not yet implemented");
   }
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java
index cced65c8..fb6b1eae 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageImpl.java
@@ -34,6 +34,7 @@ import com.google.api.gax.retrying.ResultRetryAlgorithm;
 import com.google.api.gax.rpc.ApiException;
 import com.google.api.gax.rpc.ApiExceptionFactory;
 import com.google.api.gax.rpc.ApiExceptions;
+import com.google.api.gax.rpc.NotFoundException;
 import com.google.api.gax.rpc.StatusCode;
 import com.google.api.gax.rpc.UnaryCallable;
 import com.google.api.gax.rpc.UnimplementedException;
@@ -47,6 +48,7 @@ import com.google.cloud.storage.HmacKey.HmacKeyMetadata;
 import com.google.cloud.storage.HmacKey.HmacKeyState;
 import com.google.cloud.storage.PostPolicyV4.PostConditionsV4;
 import com.google.cloud.storage.PostPolicyV4.PostFieldsV4;
+import com.google.cloud.storage.Storage.ComposeRequest.SourceBlob;
 import com.google.cloud.storage.UnbufferedReadableByteChannelSession.UnbufferedReadableByteChannel;
 import com.google.cloud.storage.UnbufferedWritableByteChannelSession.UnbufferedWritableByteChannel;
 import com.google.cloud.storage.UnifiedOpts.BucketListOpt;
@@ -300,6 +302,7 @@ final class GrpcStorageImpl extends BaseService<StorageOptions> implements Stora
       session =
           channelSessionBuilder
               .resumable()
+              .withRetryConfig(getOptions(), retryAlgorithmManager.idempotent())
               .buffered(Buffers.allocateAligned(bufferSize, _256KiB))
               .setStartAsync(start)
               .build();
@@ -346,6 +349,7 @@ final class GrpcStorageImpl extends BaseService<StorageOptions> implements Stora
             .setHasher(Hasher.enabled())
             .setByteStringStrategy(ByteStringStrategy.noCopy())
             .resumable()
+            .withRetryConfig(getOptions(), retryAlgorithmManager.idempotent())
             .buffered(Buffers.allocateAligned(bufferSize, _256KiB))
             .setStartAsync(start)
             .build();
@@ -407,6 +411,7 @@ final class GrpcStorageImpl extends BaseService<StorageOptions> implements Stora
         GetObjectRequest.newBuilder()
             .setBucket(bucketNameCodec.encode(blob.getBucket()))
             .setObject(blob.getName());
+    ifNonNull(blob.getGeneration(), builder::setGeneration);
     GetObjectRequest req = opts.getObjectsRequest().apply(builder).build();
     return Retrying.run(
         getOptions(),
@@ -548,16 +553,19 @@ final class GrpcStorageImpl extends BaseService<StorageOptions> implements Stora
             .setObject(blob.getName());
     ifNonNull(blob.getGeneration(), builder::setGeneration);
     DeleteObjectRequest req = opts.deleteObjectsRequest().apply(builder).build();
-    try {
-      Retrying.run(
-          getOptions(),
-          retryAlgorithmManager.getFor(req),
-          () -> storageClient.deleteObjectCallable().call(req, grpcCallContext),
-          Decoder.identity());
-      return true;
-    } catch (StorageException e) {
-      return false;
-    }
+    return Boolean.TRUE.equals(
+        Retrying.run(
+            getOptions(),
+            retryAlgorithmManager.getFor(req),
+            () -> {
+              try {
+                storageClient.deleteObjectCallable().call(req, grpcCallContext);
+                return true;
+              } catch (NotFoundException e) {
+                return false;
+              }
+            },
+            Decoder.identity()));
   }
 
   @Override
@@ -573,12 +581,7 @@ final class GrpcStorageImpl extends BaseService<StorageOptions> implements Stora
         opts.grpcMetadataMapper().apply(GrpcCallContext.createDefault());
     ComposeObjectRequest.Builder builder = ComposeObjectRequest.newBuilder();
     composeRequest.getSourceBlobs().stream()
-        .map(
-            src ->
-                SourceObject.newBuilder()
-                    .setName(src.getName())
-                    .setGeneration(src.getGeneration())
-                    .build())
+        .map(src -> sourceObjectEncode(src))
         .forEach(builder::addSourceObjects);
     final Object target = codecs.blobInfo().encode(composeRequest.getTarget());
     builder.setDestination(target);
@@ -715,6 +718,8 @@ final class GrpcStorageImpl extends BaseService<StorageOptions> implements Stora
     WriteObjectRequest req = getWriteObjectRequest(blobInfo, opts);
     return new GrpcBlobWriteChannel(
         storageClient.writeObjectCallable(),
+        getOptions(),
+        retryAlgorithmManager.idempotent(),
         () ->
             ResumableMedia.gapic()
                 .write()
@@ -1429,4 +1434,11 @@ final class GrpcStorageImpl extends BaseService<StorageOptions> implements Stora
         "labels",
         "event_based_hold");
   }
+
+  private SourceObject sourceObjectEncode(SourceBlob from) {
+    SourceObject.Builder to = SourceObject.newBuilder();
+    to.setName(from.getName());
+    ifNonNull(from.getGeneration(), to::setGeneration);
+    return to.build();
+  }
 }
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/Retrying.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/Retrying.java
index e529ee9f..0740d120 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/Retrying.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/Retrying.java
@@ -19,6 +19,8 @@ package com.google.cloud.storage;
 import static com.google.cloud.RetryHelper.runWithRetries;
 
 import com.google.api.core.ApiClock;
+import com.google.api.core.NanoClock;
+import com.google.api.gax.retrying.BasicResultRetryAlgorithm;
 import com.google.api.gax.retrying.ResultRetryAlgorithm;
 import com.google.api.gax.retrying.RetrySettings;
 import com.google.cloud.RetryHelper.RetryHelperException;
@@ -95,6 +97,15 @@ final class Retrying {
     }
   }
 
+  static ResultRetryAlgorithm<?> neverRetry() {
+    return new BasicResultRetryAlgorithm<Object>() {
+      @Override
+      public boolean shouldRetry(Throwable previousThrowable, Object previousResponse) {
+        return false;
+      }
+    };
+  }
+
   /**
    * Rather than requiring a full set of {@link StorageOptions} to be passed specify what we
    * actually need and have StorageOptions implement this interface.
@@ -104,5 +115,19 @@ final class Retrying {
     RetrySettings getRetrySettings();
 
     ApiClock getClock();
+
+    static RetryingDependencies attemptOnce() {
+      return new RetryingDependencies() {
+        @Override
+        public RetrySettings getRetrySettings() {
+          return RetrySettings.newBuilder().setMaxAttempts(1).build();
+        }
+
+        @Override
+        public ApiClock getClock() {
+          return NanoClock.getDefaultClock();
+        }
+      };
+    }
   }
 }
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java
index 813371a6..5b8f2a75 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/UnifiedOpts.java
@@ -341,6 +341,7 @@ final class UnifiedOpts {
     return new Delimiter(s);
   }
 
+  @Deprecated
   static DetectContentType detectContentType() {
     return DetectContentType.INSTANCE;
   }
@@ -1436,7 +1437,7 @@ final class UnifiedOpts {
    */
   @Deprecated
   static final class DetectContentType implements ObjectOptExtractor<ObjectTargetOpt> {
-    private static final DetectContentType INSTANCE = new DetectContentType();
+    @Deprecated private static final DetectContentType INSTANCE = new DetectContentType();
     private static final FileNameMap FILE_NAME_MAP = URLConnection.getFileNameMap();
     private static final long serialVersionUID = 1L;
 
diff --git a/google-cloud-storage/src/main/java/com/google/cloud/storage/WriteFlushStrategy.java b/google-cloud-storage/src/main/java/com/google/cloud/storage/WriteFlushStrategy.java
index 6da9c5e3..0007ec68 100644
--- a/google-cloud-storage/src/main/java/com/google/cloud/storage/WriteFlushStrategy.java
+++ b/google-cloud-storage/src/main/java/com/google/cloud/storage/WriteFlushStrategy.java
@@ -18,8 +18,11 @@ package com.google.cloud.storage;
 
 import com.google.api.core.SettableApiFuture;
 import com.google.api.gax.grpc.GrpcCallContext;
+import com.google.api.gax.retrying.ResultRetryAlgorithm;
 import com.google.api.gax.rpc.ApiStreamObserver;
 import com.google.api.gax.rpc.ClientStreamingCallable;
+import com.google.cloud.storage.Conversions.Decoder;
+import com.google.cloud.storage.Retrying.RetryingDependencies;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.storage.v2.WriteObjectRequest;
@@ -52,11 +55,14 @@ final class WriteFlushStrategy {
    * along with {@link Flusher#close(WriteObjectRequest)}.
    */
   static FlusherFactory fsyncEveryFlush(
-      ClientStreamingCallable<WriteObjectRequest, WriteObjectResponse> write) {
+      ClientStreamingCallable<WriteObjectRequest, WriteObjectResponse> write,
+      RetryingDependencies deps,
+      ResultRetryAlgorithm<?> alg) {
     return (String bucketName,
         LongConsumer committedTotalBytesCallback,
         Consumer<WriteObjectResponse> onSuccessCallback) ->
-        new FsyncEveryFlusher(write, bucketName, committedTotalBytesCallback, onSuccessCallback);
+        new FsyncEveryFlusher(
+            write, deps, alg, bucketName, committedTotalBytesCallback, onSuccessCallback);
   }
 
   /**
@@ -105,39 +111,51 @@ final class WriteFlushStrategy {
   private static final class FsyncEveryFlusher implements Flusher {
 
     private final ClientStreamingCallable<WriteObjectRequest, WriteObjectResponse> write;
+    private final RetryingDependencies deps;
+    private final ResultRetryAlgorithm<?> alg;
     private final String bucketName;
     private final LongConsumer sizeCallback;
     private final Consumer<WriteObjectResponse> completeCallback;
 
     private FsyncEveryFlusher(
         ClientStreamingCallable<WriteObjectRequest, WriteObjectResponse> write,
+        RetryingDependencies deps,
+        ResultRetryAlgorithm<?> alg,
         String bucketName,
         LongConsumer sizeCallback,
         Consumer<WriteObjectResponse> completeCallback) {
       this.write = write;
+      this.deps = deps;
+      this.alg = alg;
       this.bucketName = bucketName;
       this.sizeCallback = sizeCallback;
       this.completeCallback = completeCallback;
     }
 
     public void flush(@NonNull List<WriteObjectRequest> segments) {
-
-      Observer observer = new Observer(sizeCallback, completeCallback);
-      GrpcCallContext internalContext = contextWithBucketName(bucketName);
-      ApiStreamObserver<WriteObjectRequest> write =
-          this.write.withDefaultCallContext(internalContext).clientStreamingCall(observer);
-
-      boolean first = true;
-      for (WriteObjectRequest message : segments) {
-        if (!first) {
-          message = message.toBuilder().clearUploadId().clearWriteObjectSpec().build();
-        }
-
-        write.onNext(message);
-        first = false;
-      }
-      write.onCompleted();
-      observer.await();
+      Retrying.run(
+          deps,
+          alg,
+          () -> {
+            Observer observer = new Observer(sizeCallback, completeCallback);
+            GrpcCallContext internalContext = contextWithBucketName(bucketName);
+            ApiStreamObserver<WriteObjectRequest> write =
+                this.write.withDefaultCallContext(internalContext).clientStreamingCall(observer);
+
+            boolean first = true;
+            for (WriteObjectRequest message : segments) {
+              if (!first) {
+                message = message.toBuilder().clearUploadId().clearWriteObjectSpec().build();
+              }
+
+              write.onNext(message);
+              first = false;
+            }
+            write.onCompleted();
+            observer.await();
+            return null;
+          },
+          Decoder.identity());
     }
 
     public void close(@Nullable WriteObjectRequest req) {
@@ -243,7 +261,6 @@ final class WriteFlushStrategy {
      */
     @Override
     public void onError(Throwable t) {
-      // TODO: is retryable?
       invocationHandle.setException(t);
     }
 


Test patch:
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/DetectContentTypeTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/DetectContentTypeTest.java
new file mode 100644
index 00000000..69d6ad63
--- /dev/null
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/DetectContentTypeTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.cloud.storage.UnifiedOpts.ObjectTargetOpt;
+import com.google.cloud.storage.UnifiedOpts.SetContentType;
+import com.google.storage.v2.Object;
+import com.google.storage.v2.UpdateObjectRequest;
+import com.google.storage.v2.WriteObjectRequest;
+import com.google.storage.v2.WriteObjectSpec;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public final class DetectContentTypeTest {
+
+  private final String objectName;
+  private final String expectedContentType;
+  private final SetContentType expected;
+
+  public DetectContentTypeTest(String objectName, String expectedContentType) {
+    this.objectName = objectName;
+    this.expectedContentType = expectedContentType;
+    this.expected = UnifiedOpts.setContentType(expectedContentType);
+  }
+
+  @Test
+  public void blobId() {
+    ObjectTargetOpt actual =
+        UnifiedOpts.detectContentType().extractFromBlobId(BlobId.of("bucket", objectName));
+    assertThat(actual).isEqualTo(expected);
+  }
+
+  @Test
+  public void blobInfo() {
+    ObjectTargetOpt actual =
+        UnifiedOpts.detectContentType()
+            .extractFromBlobInfo(BlobInfo.newBuilder("bucket", objectName).build());
+    assertThat(actual).isEqualTo(expected);
+  }
+
+  @Test
+  public void updateBlobInfo() {
+    BlobInfo base = BlobInfo.newBuilder("bucket", objectName).build();
+    BlobInfo actual =
+        UnifiedOpts.detectContentType()
+            .extractFromBlobInfo(base)
+            .blobInfo()
+            .apply(base.toBuilder())
+            .build();
+    BlobInfo expected = base.toBuilder().setContentType(expectedContentType).build();
+    assertThat(actual).isEqualTo(expected);
+  }
+
+  @Test
+  public void writeObjectRequest() {
+    WriteObjectRequest base =
+        WriteObjectRequest.newBuilder()
+            .setWriteObjectSpec(
+                WriteObjectSpec.newBuilder()
+                    .setResource(
+                        Object.newBuilder().setBucket("bucket").setName(objectName).build())
+                    .build())
+            .build();
+    WriteObjectRequest actual =
+        UnifiedOpts.detectContentType()
+            .extractFromBlobInfo(BlobInfo.newBuilder("bucket", objectName).build())
+            .writeObject()
+            .apply(base.toBuilder())
+            .build();
+    WriteObjectRequest.Builder b2 = base.toBuilder();
+    b2.getWriteObjectSpecBuilder().getResourceBuilder().setContentType(expectedContentType);
+    WriteObjectRequest expected = b2.build();
+    assertThat(actual).isEqualTo(expected);
+  }
+
+  @Test
+  public void updateObjectRequest() {
+    UpdateObjectRequest base =
+        UpdateObjectRequest.newBuilder()
+            .setObject(Object.newBuilder().setBucket("bucket").setName(objectName).build())
+            .build();
+    UpdateObjectRequest actual =
+        UnifiedOpts.detectContentType()
+            .extractFromBlobInfo(BlobInfo.newBuilder("bucket", objectName).build())
+            .updateObject()
+            .apply(base.toBuilder())
+            .build();
+    UpdateObjectRequest.Builder b2 = base.toBuilder();
+    b2.getObjectBuilder().setContentType(expectedContentType);
+    UpdateObjectRequest expected = b2.build();
+    assertThat(actual).isEqualTo(expected);
+  }
+
+  @Parameters(name = "{0}")
+  public static Iterable<java.lang.Object[]> data() {
+    return Arrays.asList(
+        new java.lang.Object[] {"file1.txt", "text/plain"},
+        new java.lang.Object[] {"file.Jpg", "image/jpeg"},
+        new java.lang.Object[] {"file", "application/octet-stream"});
+  }
+}
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannelTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannelTest.java
index 70ad6a34..c9f122fb 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannelTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/GapicUnbufferedWritableByteChannelTest.java
@@ -16,9 +16,13 @@
 
 package com.google.cloud.storage;
 
+import static com.google.cloud.storage.TestUtils.apiException;
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.api.core.SettableApiFuture;
+import com.google.api.gax.retrying.BasicResultRetryAlgorithm;
+import com.google.api.gax.rpc.DataLossException;
+import com.google.cloud.storage.Retrying.RetryingDependencies;
 import com.google.cloud.storage.WriteCtx.WriteObjectRequestBuilderFactory;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -38,11 +42,70 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
 import org.junit.Test;
 
 public final class GapicUnbufferedWritableByteChannelTest {
 
-  ChunkSegmenter segmenter = new ChunkSegmenter(Hasher.noop(), ByteStringStrategy.copy(), 10, 5);
+  private static final ChunkSegmenter segmenter =
+      new ChunkSegmenter(Hasher.noop(), ByteStringStrategy.copy(), 10, 5);
+
+  private static final String uploadId = "upload-id";
+
+  private static final Object obj = Object.newBuilder().setBucket("buck").setName("obj").build();
+  private static final WriteObjectSpec spec = WriteObjectSpec.newBuilder().setResource(obj).build();
+
+  private static final StartResumableWriteRequest startReq =
+      StartResumableWriteRequest.newBuilder().setWriteObjectSpec(spec).build();
+  private static final StartResumableWriteResponse startResp =
+      StartResumableWriteResponse.newBuilder().setUploadId(uploadId).build();
+
+  private static final byte[] bytes = DataGenerator.base64Characters().genBytes(40);
+  private static final WriteObjectRequest req1 =
+      WriteObjectRequest.newBuilder()
+          .setUploadId(uploadId)
+          .setChecksummedData(TestUtils.getChecksummedData(ByteString.copyFrom(bytes, 0, 10)))
+          .build();
+  private static final WriteObjectRequest req2 =
+      WriteObjectRequest.newBuilder()
+          .setUploadId(uploadId)
+          .setWriteOffset(10)
+          .setChecksummedData(TestUtils.getChecksummedData(ByteString.copyFrom(bytes, 10, 10)))
+          .build();
+  private static final WriteObjectRequest req3 =
+      WriteObjectRequest.newBuilder()
+          .setUploadId(uploadId)
+          .setWriteOffset(20)
+          .setChecksummedData(TestUtils.getChecksummedData(ByteString.copyFrom(bytes, 20, 10)))
+          .build();
+  private static final WriteObjectRequest req4 =
+      WriteObjectRequest.newBuilder()
+          .setUploadId(uploadId)
+          .setWriteOffset(30)
+          .setChecksummedData(TestUtils.getChecksummedData(ByteString.copyFrom(bytes, 30, 10)))
+          .build();
+  private static final WriteObjectRequest req5 =
+      WriteObjectRequest.newBuilder()
+          .setUploadId(uploadId)
+          .setWriteOffset(40)
+          .setFinishWrite(true)
+          .build();
+
+  private static final WriteObjectResponse resp1 =
+      WriteObjectResponse.newBuilder().setPersistedSize(10).build();
+  private static final WriteObjectResponse resp2 =
+      WriteObjectResponse.newBuilder().setPersistedSize(20).build();
+  private static final WriteObjectResponse resp3 =
+      WriteObjectResponse.newBuilder().setPersistedSize(30).build();
+  private static final WriteObjectResponse resp4 =
+      WriteObjectResponse.newBuilder().setPersistedSize(40).build();
+  private static final WriteObjectResponse resp5 =
+      WriteObjectResponse.newBuilder().setResource(obj.toBuilder().setSize(40)).build();
+
+  private static final WriteObjectRequestBuilderFactory reqFactory =
+      new ResumableWrite(startReq, startResp);
 
   @Test
   public void directUpload() throws IOException, InterruptedException, ExecutionException {
@@ -99,56 +162,6 @@ public final class GapicUnbufferedWritableByteChannelTest {
 
   @Test
   public void resumableUpload() throws IOException, InterruptedException, ExecutionException {
-    String uploadId = "upload-id";
-
-    Object obj = Object.newBuilder().setBucket("buck").setName("obj").build();
-    WriteObjectSpec spec = WriteObjectSpec.newBuilder().setResource(obj).build();
-
-    StartResumableWriteRequest startReq =
-        StartResumableWriteRequest.newBuilder().setWriteObjectSpec(spec).build();
-    StartResumableWriteResponse startResp =
-        StartResumableWriteResponse.newBuilder().setUploadId(uploadId).build();
-
-    byte[] bytes = DataGenerator.base64Characters().genBytes(40);
-    WriteObjectRequest req1 =
-        WriteObjectRequest.newBuilder()
-            .setUploadId(uploadId)
-            .setChecksummedData(TestUtils.getChecksummedData(ByteString.copyFrom(bytes, 0, 10)))
-            .build();
-    WriteObjectRequest req2 =
-        WriteObjectRequest.newBuilder()
-            .setUploadId(uploadId)
-            .setWriteOffset(10)
-            .setChecksummedData(TestUtils.getChecksummedData(ByteString.copyFrom(bytes, 10, 10)))
-            .build();
-    WriteObjectRequest req3 =
-        WriteObjectRequest.newBuilder()
-            .setUploadId(uploadId)
-            .setWriteOffset(20)
-            .setChecksummedData(TestUtils.getChecksummedData(ByteString.copyFrom(bytes, 20, 10)))
-            .build();
-    WriteObjectRequest req4 =
-        WriteObjectRequest.newBuilder()
-            .setUploadId(uploadId)
-            .setWriteOffset(30)
-            .setChecksummedData(TestUtils.getChecksummedData(ByteString.copyFrom(bytes, 30, 10)))
-            .build();
-    WriteObjectRequest req5 =
-        WriteObjectRequest.newBuilder()
-            .setUploadId(uploadId)
-            .setWriteOffset(40)
-            .setFinishWrite(true)
-            .build();
-
-    WriteObjectResponse resp1 = WriteObjectResponse.newBuilder().setPersistedSize(10).build();
-    WriteObjectResponse resp2 = WriteObjectResponse.newBuilder().setPersistedSize(20).build();
-    WriteObjectResponse resp3 = WriteObjectResponse.newBuilder().setPersistedSize(30).build();
-    WriteObjectResponse resp4 = WriteObjectResponse.newBuilder().setPersistedSize(40).build();
-    WriteObjectResponse resp5 =
-        WriteObjectResponse.newBuilder().setResource(obj.toBuilder().setSize(40)).build();
-
-    WriteObjectRequestBuilderFactory reqFactory = new ResumableWrite(startReq, startResp);
-
     ImmutableMap<List<WriteObjectRequest>, WriteObjectResponse> writes =
         ImmutableMap.<List<WriteObjectRequest>, WriteObjectResponse>builder()
             .put(ImmutableList.of(req1), resp1)
@@ -166,7 +179,10 @@ public final class GapicUnbufferedWritableByteChannelTest {
               result,
               segmenter,
               reqFactory,
-              WriteFlushStrategy.fsyncEveryFlush(sc.writeObjectCallable()))) {
+              WriteFlushStrategy.fsyncEveryFlush(
+                  sc.writeObjectCallable(),
+                  RetryingDependencies.attemptOnce(),
+                  Retrying.neverRetry()))) {
         ImmutableList<ByteBuffer> buffers = TestUtils.subDivide(bytes, 10);
         for (ByteBuffer buf : buffers) {
           c.write(buf);
@@ -176,16 +192,113 @@ public final class GapicUnbufferedWritableByteChannelTest {
     }
   }
 
+  @Test
+  public void resumableUpload_chunkAutomaticRetry()
+      throws IOException, InterruptedException, ExecutionException {
+    AtomicBoolean req2SendErr = new AtomicBoolean(true);
+    AtomicBoolean req4SendErr = new AtomicBoolean(true);
+    AtomicInteger writeCount = new AtomicInteger(0);
+    StorageImplBase service =
+        new DirectWriteService(
+            (obs, requests) -> {
+              writeCount.getAndIncrement();
+              if (requests.equals(ImmutableList.of(req1))) {
+                obs.onNext(resp1);
+                obs.onCompleted();
+              } else if (requests.equals(ImmutableList.of(req2))) {
+                obs.onNext(resp2);
+                if (req2SendErr.get()) {
+                  req2SendErr.set(false);
+                  obs.onError(apiException(Code.DATA_LOSS));
+                } else {
+                  obs.onCompleted();
+                }
+              } else if (requests.equals(ImmutableList.of(req3))) {
+                obs.onNext(resp3);
+                obs.onCompleted();
+              } else if (requests.equals(ImmutableList.of(req4))) {
+                obs.onNext(resp4);
+                if (req4SendErr.get()) {
+                  req4SendErr.set(false);
+                  obs.onError(apiException(Code.DATA_LOSS));
+                } else {
+                  obs.onCompleted();
+                }
+              } else if (requests.equals(ImmutableList.of(req5))) {
+                obs.onNext(resp5);
+                obs.onCompleted();
+              } else {
+                obs.onError(
+                    TestUtils.apiException(Code.PERMISSION_DENIED, "Unexpected request chain."));
+              }
+            });
+    WriteCtx<?> writeCtx;
+    try (FakeServer fake = FakeServer.of(service);
+        StorageClient sc = StorageClient.create(fake.storageSettings())) {
+      SettableApiFuture<WriteObjectResponse> result = SettableApiFuture.create();
+      try (GapicUnbufferedWritableByteChannel<?> c =
+          new GapicUnbufferedWritableByteChannel<>(
+              result,
+              segmenter,
+              reqFactory,
+              WriteFlushStrategy.fsyncEveryFlush(
+                  sc.writeObjectCallable(),
+                  TestUtils.defaultRetryingDeps(),
+                  new BasicResultRetryAlgorithm<Object>() {
+                    @Override
+                    public boolean shouldRetry(Throwable t, Object ignore) {
+                      return t instanceof DataLossException;
+                    }
+                  }))) {
+        writeCtx = c.getWriteCtx();
+        ImmutableList<ByteBuffer> buffers = TestUtils.subDivide(bytes, 10);
+        c.write(buffers.get(0));
+        assertThat(writeCtx.getTotalSentBytes().get()).isEqualTo(10);
+        assertThat(writeCtx.getConfirmedBytes().get()).isEqualTo(10);
+        c.write(buffers.get(1));
+        assertThat(writeCtx.getTotalSentBytes().get()).isEqualTo(20);
+        assertThat(writeCtx.getConfirmedBytes().get()).isEqualTo(20);
+        c.write(buffers.get(2));
+        assertThat(writeCtx.getTotalSentBytes().get()).isEqualTo(30);
+        assertThat(writeCtx.getConfirmedBytes().get()).isEqualTo(30);
+        c.write(buffers.get(3));
+        assertThat(writeCtx.getTotalSentBytes().get()).isEqualTo(40);
+        assertThat(writeCtx.getConfirmedBytes().get()).isEqualTo(40);
+      }
+      assertThat(result.get()).isEqualTo(resp5);
+    }
+    assertThat(req2SendErr.get()).isFalse();
+    assertThat(req4SendErr.get()).isFalse();
+    assertThat(writeCount.get()).isEqualTo(7);
+
+    assertThat(writeCtx.getTotalSentBytes().get()).isEqualTo(40);
+    assertThat(writeCtx.getConfirmedBytes().get()).isEqualTo(40);
+  }
+
   private static class DirectWriteService extends StorageImplBase {
-    private final ImmutableMap<List<WriteObjectRequest>, WriteObjectResponse> writes;
+    private final BiConsumer<StreamObserver<WriteObjectResponse>, List<WriteObjectRequest>> c;
 
     private ImmutableList.Builder<WriteObjectRequest> requests;
 
-    private DirectWriteService(ImmutableMap<List<WriteObjectRequest>, WriteObjectResponse> writes) {
-      this.writes = writes;
+    private DirectWriteService(
+        BiConsumer<StreamObserver<WriteObjectResponse>, List<WriteObjectRequest>> c) {
+      this.c = c;
       this.requests = new ImmutableList.Builder<>();
     }
 
+    private DirectWriteService(ImmutableMap<List<WriteObjectRequest>, WriteObjectResponse> writes) {
+      this(
+          (obs, build) -> {
+            if (writes.containsKey(build)) {
+              obs.onNext(writes.get(build));
+              obs.onCompleted();
+            } else {
+              obs.onError(
+                  TestUtils.apiException(Code.PERMISSION_DENIED, "Unexpected request chain."));
+            }
+          });
+    }
+
     @Override
     public StreamObserver<WriteObjectRequest> writeObject(StreamObserver<WriteObjectResponse> obs) {
       return new Adapter() {
@@ -200,14 +313,8 @@ public final class GapicUnbufferedWritableByteChannelTest {
         @Override
         public void onCompleted() {
           ImmutableList<WriteObjectRequest> build = requests.build();
-          if (writes.containsKey(build)) {
-            requests = new ImmutableList.Builder<>();
-            obs.onNext(writes.get(build));
-            obs.onCompleted();
-          } else {
-            obs.onError(
-                TestUtils.apiException(Code.PERMISSION_DENIED, "Unexpected request chain."));
-          }
+          c.accept(obs, build);
+          requests = new ImmutableList.Builder<>();
         }
       };
     }
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/GrpcTransformPageDecoratorTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/GrpcTransformPageDecoratorTest.java
index 187348a9..f741ada0 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/GrpcTransformPageDecoratorTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/GrpcTransformPageDecoratorTest.java
@@ -18,20 +18,16 @@ package com.google.cloud.storage;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.api.core.ApiClock;
 import com.google.api.core.ApiFuture;
 import com.google.api.core.ApiFutures;
-import com.google.api.core.NanoClock;
 import com.google.api.gax.paging.AbstractPage;
 import com.google.api.gax.retrying.ResultRetryAlgorithm;
-import com.google.api.gax.retrying.RetrySettings;
 import com.google.api.gax.retrying.TimedAttemptSettings;
 import com.google.api.gax.rpc.ApiCallContext;
 import com.google.api.gax.rpc.PageContext;
 import com.google.api.gax.rpc.PagedListDescriptor;
 import com.google.api.gax.rpc.UnaryCallable;
 import com.google.cloud.storage.GrpcStorageImpl.TransformingPageDecorator;
-import com.google.cloud.storage.Retrying.RetryingDependencies;
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -85,7 +81,7 @@ public class GrpcTransformPageDecoratorTest {
         new TransformingPageDecorator<>(
             page,
             String::toUpperCase,
-            retryingDeps(),
+            TestUtils.defaultRetryingDeps(),
             StorageRetryStrategy.getUniformStorageRetryStrategy().getIdempotentHandler());
 
     assertThat(ImmutableList.copyOf(decorator.getValues().iterator()))
@@ -106,7 +102,8 @@ public class GrpcTransformPageDecoratorTest {
         PageContext.create(callable, descriptor, req1, apiCallContext);
     ReqRespPage page = new ReqRespPage(context, resp1);
     TransformingPageDecorator<Req, Resp, String, ReqRespPage, String> decorator =
-        new TransformingPageDecorator<>(page, String::toUpperCase, retryingDeps(), alg);
+        new TransformingPageDecorator<>(
+            page, String::toUpperCase, TestUtils.defaultRetryingDeps(), alg);
 
     ImmutableList<String> actual = ImmutableList.copyOf(decorator.iterateAll().iterator());
     assertThat(actual).containsExactlyElementsIn(expectedValues);
@@ -116,20 +113,6 @@ public class GrpcTransformPageDecoratorTest {
     assertThat(callable.callableCallCount.get()).isEqualTo(3);
   }
 
-  static RetryingDependencies retryingDeps() {
-    return new RetryingDependencies() {
-      @Override
-      public RetrySettings getRetrySettings() {
-        return StorageOptions.getDefaultRetrySettings();
-      }
-
-      @Override
-      public ApiClock getClock() {
-        return NanoClock.getDefaultClock();
-      }
-    };
-  }
-
   private static class ReqRespPage extends AbstractPage<Req, Resp, String, ReqRespPage> {
 
     private final Resp response;
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/TestUtils.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/TestUtils.java
index 573c2781..2bd1d54b 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/TestUtils.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/TestUtils.java
@@ -16,13 +16,17 @@
 
 package com.google.cloud.storage;
 
+import com.google.api.core.ApiClock;
+import com.google.api.core.NanoClock;
 import com.google.api.gax.grpc.GrpcCallContext;
 import com.google.api.gax.grpc.GrpcStatusCode;
+import com.google.api.gax.retrying.RetrySettings;
 import com.google.api.gax.rpc.ApiException;
 import com.google.api.gax.rpc.ApiExceptionFactory;
 import com.google.api.gax.rpc.ErrorDetails;
 import com.google.api.gax.rpc.StatusCode;
 import com.google.cloud.storage.Crc32cValue.Crc32cLengthKnown;
+import com.google.cloud.storage.Retrying.RetryingDependencies;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.protobuf.Any;
@@ -108,4 +112,18 @@ public final class TestUtils {
           .collect(ImmutableList.toImmutableList());
     }
   }
+
+  static RetryingDependencies defaultRetryingDeps() {
+    return new RetryingDependencies() {
+      @Override
+      public RetrySettings getRetrySettings() {
+        return StorageOptions.getDefaultRetrySettings();
+      }
+
+      @Override
+      public ApiClock getClock() {
+        return NanoClock.getDefaultClock();
+      }
+    };
+  }
 }
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/WriteFlushStrategyTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/WriteFlushStrategyTest.java
index 9ad61533..4c96d430 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/WriteFlushStrategyTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/WriteFlushStrategyTest.java
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
 import com.google.api.gax.rpc.ApiCallContext;
 import com.google.api.gax.rpc.ApiStreamObserver;
 import com.google.api.gax.rpc.ClientStreamingCallable;
+import com.google.cloud.storage.Retrying.RetryingDependencies;
 import com.google.cloud.storage.WriteFlushStrategy.Flusher;
 import com.google.cloud.storage.WriteFlushStrategy.FlusherFactory;
 import com.google.common.collect.ImmutableList;
@@ -43,7 +44,12 @@ public final class WriteFlushStrategyTest {
 
   @Test
   public void bucketNameAddedToXGoogRequestParams_nonNull_nonEmpty_fsyncEveryFlush() {
-    doTest(WriteFlushStrategy::fsyncEveryFlush, "bucket-name", expectedHeaderNonNullNonEmpty);
+    doTest(
+        write ->
+            WriteFlushStrategy.fsyncEveryFlush(
+                write, RetryingDependencies.attemptOnce(), Retrying.neverRetry()),
+        "bucket-name",
+        expectedHeaderNonNullNonEmpty);
   }
 
   @Test
@@ -53,7 +59,12 @@ public final class WriteFlushStrategyTest {
 
   @Test
   public void bucketNameNotAddedToXGoogRequestParams_nonNull_empty_fsyncEveryFlush() {
-    doTest(WriteFlushStrategy::fsyncEveryFlush, "", expectedHeaderNonNullEmpty);
+    doTest(
+        write ->
+            WriteFlushStrategy.fsyncEveryFlush(
+                write, RetryingDependencies.attemptOnce(), Retrying.neverRetry()),
+        "",
+        expectedHeaderNonNullEmpty);
   }
 
   @Test
@@ -63,7 +74,12 @@ public final class WriteFlushStrategyTest {
 
   @Test
   public void bucketNameNotAddedToXGoogRequestParams_null_fsyncEveryFlush() {
-    doTest(WriteFlushStrategy::fsyncEveryFlush, null, expectedHeaderNull);
+    doTest(
+        write ->
+            WriteFlushStrategy.fsyncEveryFlush(
+                write, RetryingDependencies.attemptOnce(), Retrying.neverRetry()),
+        null,
+        expectedHeaderNull);
   }
 
   @Test
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITAccessTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITAccessTest.java
index 21bfbf36..178e2570 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITAccessTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITAccessTest.java
@@ -58,38 +58,82 @@ import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
-import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class ITAccessTest {
   @ClassRule(order = 1)
-  public static final StorageFixture storageFixture = StorageFixture.defaultHttp();
+  public static final StorageFixture storageFixtureHttp = StorageFixture.defaultHttp();
+
+  /*
+  @ClassRule(order = 1)
+  public static final StorageFixture storageFixtureGrpc = StorageFixture.defaultGrpc();
+   */
 
   @ClassRule(order = 2)
-  public static final BucketFixture bucketFixture =
-      BucketFixture.newBuilder().setHandle(storageFixture::getInstance).build();
+  public static final BucketFixture bucketFixtureHttp =
+      BucketFixture.newBuilder().setHandle(storageFixtureHttp::getInstance).build();
 
   @ClassRule(order = 3)
-  public static final BucketFixture requesterPaysFixture =
-      BucketFixture.newBuilder().setHandle(storageFixture::getInstance).build();
+  public static final BucketFixture requesterPaysFixtureHttp =
+      BucketFixture.newBuilder().setHandle(storageFixtureHttp::getInstance).build();
+
+  /*
+  @ClassRule(order = 2)
+  public static final BucketFixture bucketFixtureGrpc =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-grpc-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
+   */
+
+  /*
+  @ClassRule(order = 2)
+  public static final BucketFixture requesterPaysFixtureGrpc =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-grpc-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
+   */
 
   private static final Long RETENTION_PERIOD = 5L;
 
-  private static Storage storage;
-  private static String bucketName;
-  private static String requesterPaysBucketName;
+  private final Storage storage;
+  private final BucketFixture bucketFixture;
+  private final BucketFixture requesterPaysFixture;
+  private final String clientName;
+
+  public ITAccessTest(
+      String clientName,
+      StorageFixture storageFixture,
+      BucketFixture bucketFixture,
+      BucketFixture requesterPaysFixture) {
+    this.clientName = clientName;
+    this.storage = storageFixture.getInstance();
+    this.bucketFixture = bucketFixture;
+    this.requesterPaysFixture = requesterPaysFixture;
+  }
 
-  @BeforeClass
-  public static void setup() {
-    storage = storageFixture.getInstance();
-    bucketName = bucketFixture.getBucketInfo().getName();
-    requesterPaysBucketName = requesterPaysFixture.getBucketInfo().getName();
+  @Parameters(name = "{0}")
+  public static Iterable<Object[]> data() {
+    return ImmutableList.of(
+        new Object[] {
+          "JSON/Prod", storageFixtureHttp, bucketFixtureHttp, requesterPaysFixtureHttp
+        });
+    /*
+    return ImmutableList.of(
+        new Object[] {"JSON/Prod", storageFixtureHttp, bucketFixtureHttp, requesterPaysFixtureHttp},
+        new Object[] {"GRPC/Prod", storageFixtureGrpc, bucketFixtureGrpc, requesterPaysFixtureGrpc});
+     */
   }
 
   @Test
   public void testBucketAcl() {
-    unsetRequesterPays();
+    unsetRequesterPays(storage, requesterPaysFixture);
     testBucketAclRequesterPays(true);
     testBucketAclRequesterPays(false);
   }
@@ -98,7 +142,7 @@ public class ITAccessTest {
     if (requesterPays) {
       Bucket remoteBucket =
           storage.get(
-              requesterPaysBucketName,
+              requesterPaysFixture.getBucketInfo().getName(),
               Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
       assertTrue(remoteBucket.requesterPays() == null || !remoteBucket.requesterPays());
       remoteBucket = remoteBucket.toBuilder().setRequesterPays(true).build();
@@ -114,26 +158,41 @@ public class ITAccessTest {
             : new Storage.BucketSourceOption[] {};
 
     assertNull(
-        storage.getAcl(requesterPaysBucketName, User.ofAllAuthenticatedUsers(), bucketOptions));
+        storage.getAcl(
+            requesterPaysFixture.getBucketInfo().getName(),
+            User.ofAllAuthenticatedUsers(),
+            bucketOptions));
     assertFalse(
-        storage.deleteAcl(requesterPaysBucketName, User.ofAllAuthenticatedUsers(), bucketOptions));
+        storage.deleteAcl(
+            requesterPaysFixture.getBucketInfo().getName(),
+            User.ofAllAuthenticatedUsers(),
+            bucketOptions));
     Acl acl = Acl.of(User.ofAllAuthenticatedUsers(), Role.READER);
-    assertNotNull(storage.createAcl(requesterPaysBucketName, acl, bucketOptions));
+    assertNotNull(
+        storage.createAcl(requesterPaysFixture.getBucketInfo().getName(), acl, bucketOptions));
     Acl updatedAcl =
         storage.updateAcl(
-            requesterPaysBucketName, acl.toBuilder().setRole(Role.WRITER).build(), bucketOptions);
+            requesterPaysFixture.getBucketInfo().getName(),
+            acl.toBuilder().setRole(Role.WRITER).build(),
+            bucketOptions);
     assertEquals(Role.WRITER, updatedAcl.getRole());
     Set<Acl> acls = new HashSet<>();
-    acls.addAll(storage.listAcls(requesterPaysBucketName, bucketOptions));
+    acls.addAll(storage.listAcls(requesterPaysFixture.getBucketInfo().getName(), bucketOptions));
     assertTrue(acls.contains(updatedAcl));
     assertTrue(
-        storage.deleteAcl(requesterPaysBucketName, User.ofAllAuthenticatedUsers(), bucketOptions));
+        storage.deleteAcl(
+            requesterPaysFixture.getBucketInfo().getName(),
+            User.ofAllAuthenticatedUsers(),
+            bucketOptions));
     assertNull(
-        storage.getAcl(requesterPaysBucketName, User.ofAllAuthenticatedUsers(), bucketOptions));
+        storage.getAcl(
+            requesterPaysFixture.getBucketInfo().getName(),
+            User.ofAllAuthenticatedUsers(),
+            bucketOptions));
     if (requesterPays) {
       Bucket remoteBucket =
           storage.get(
-              requesterPaysBucketName,
+              requesterPaysFixture.getBucketInfo().getName(),
               Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING),
               Storage.BucketGetOption.userProject(projectId));
       assertTrue(remoteBucket.requesterPays());
@@ -150,29 +209,49 @@ public class ITAccessTest {
     // it can take up to 30 seconds for default acl updates to propagate
     // Since this test is performing so many mutations to default acls there are several calls
     // that are otherwise non-idempotent wrapped with retries.
-    assertNull(retry429s(() -> storage.getDefaultAcl(bucketName, User.ofAllAuthenticatedUsers())));
+    assertNull(
+        retry429s(
+            () ->
+                storage.getDefaultAcl(
+                    bucketFixture.getBucketInfo().getName(), User.ofAllAuthenticatedUsers()),
+            storage));
     assertFalse(
-        retry429s(() -> storage.deleteDefaultAcl(bucketName, User.ofAllAuthenticatedUsers())));
+        retry429s(
+            () ->
+                storage.deleteDefaultAcl(
+                    bucketFixture.getBucketInfo().getName(), User.ofAllAuthenticatedUsers()),
+            storage));
     Acl acl = Acl.of(User.ofAllAuthenticatedUsers(), Role.READER);
-    assertNotNull(retry429s(() -> storage.createDefaultAcl(bucketName, acl)));
+    assertNotNull(
+        retry429s(
+            () -> storage.createDefaultAcl(bucketFixture.getBucketInfo().getName(), acl), storage));
     Acl updatedAcl =
         retry429s(
             () ->
-                storage.updateDefaultAcl(bucketName, acl.toBuilder().setRole(Role.OWNER).build()));
+                storage.updateDefaultAcl(
+                    bucketFixture.getBucketInfo().getName(),
+                    acl.toBuilder().setRole(Role.OWNER).build()),
+            storage);
     assertEquals(Role.OWNER, updatedAcl.getRole());
-    Set<Acl> acls = new HashSet<>(storage.listDefaultAcls(bucketName));
+    Set<Acl> acls = new HashSet<>(storage.listDefaultAcls(bucketFixture.getBucketInfo().getName()));
     assertTrue(acls.contains(updatedAcl));
     assertTrue(
-        retry429s(() -> storage.deleteDefaultAcl(bucketName, User.ofAllAuthenticatedUsers())));
-    assertNull(storage.getDefaultAcl(bucketName, User.ofAllAuthenticatedUsers()));
+        retry429s(
+            () ->
+                storage.deleteDefaultAcl(
+                    bucketFixture.getBucketInfo().getName(), User.ofAllAuthenticatedUsers()),
+            storage));
+    assertNull(
+        storage.getDefaultAcl(
+            bucketFixture.getBucketInfo().getName(), User.ofAllAuthenticatedUsers()));
   }
 
   @Test
   public void testBucketPolicyV1RequesterPays() {
-    unsetRequesterPays();
+    unsetRequesterPays(storage, requesterPaysFixture);
     Bucket bucketDefault =
         storage.get(
-            requesterPaysBucketName,
+            requesterPaysFixture.getBucketInfo().getName(),
             Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
     assertTrue(bucketDefault.requesterPays() == null || !bucketDefault.requesterPays());
 
@@ -202,13 +281,14 @@ public class ITAccessTest {
             (Set<Identity>) new HashSet<>(Collections.singleton(Identity.allUsers())));
 
     // Validate getting policy.
-    Policy currentPolicy = storage.getIamPolicy(requesterPaysBucketName, bucketOptions);
+    Policy currentPolicy =
+        storage.getIamPolicy(requesterPaysFixture.getBucketInfo().getName(), bucketOptions);
     assertEquals(bindingsWithoutPublicRead, currentPolicy.getBindings());
 
     // Validate updating policy.
     Policy updatedPolicy =
         storage.setIamPolicy(
-            requesterPaysBucketName,
+            requesterPaysFixture.getBucketInfo().getName(),
             currentPolicy
                 .toBuilder()
                 .addIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
@@ -217,7 +297,7 @@ public class ITAccessTest {
     assertEquals(bindingsWithPublicRead, updatedPolicy.getBindings());
     Policy revertedPolicy =
         storage.setIamPolicy(
-            requesterPaysBucketName,
+            requesterPaysFixture.getBucketInfo().getName(),
             updatedPolicy
                 .toBuilder()
                 .removeIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
@@ -230,7 +310,7 @@ public class ITAccessTest {
     assertEquals(
         expectedPermissions,
         storage.testIamPermissions(
-            requesterPaysBucketName,
+            requesterPaysFixture.getBucketInfo().getName(),
             ImmutableList.of("storage.buckets.getIamPolicy", "storage.buckets.setIamPolicy"),
             bucketOptions));
     Bucket bucketFalse =
@@ -264,13 +344,14 @@ public class ITAccessTest {
             (Set<Identity>) new HashSet<>(Collections.singleton(Identity.allUsers())));
 
     // Validate getting policy.
-    Policy currentPolicy = storage.getIamPolicy(bucketName, bucketOptions);
+    Policy currentPolicy =
+        storage.getIamPolicy(bucketFixture.getBucketInfo().getName(), bucketOptions);
     assertEquals(bindingsWithoutPublicRead, currentPolicy.getBindings());
 
     // Validate updating policy.
     Policy updatedPolicy =
         storage.setIamPolicy(
-            bucketName,
+            bucketFixture.getBucketInfo().getName(),
             currentPolicy
                 .toBuilder()
                 .addIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
@@ -279,7 +360,7 @@ public class ITAccessTest {
     assertEquals(bindingsWithPublicRead, updatedPolicy.getBindings());
     Policy revertedPolicy =
         storage.setIamPolicy(
-            bucketName,
+            bucketFixture.getBucketInfo().getName(),
             updatedPolicy
                 .toBuilder()
                 .removeIdentity(StorageRoles.legacyObjectReader(), Identity.allUsers())
@@ -292,7 +373,7 @@ public class ITAccessTest {
     assertEquals(
         expectedPermissions,
         storage.testIamPermissions(
-            bucketName,
+            bucketFixture.getBucketInfo().getName(),
             ImmutableList.of("storage.buckets.getIamPolicy", "storage.buckets.setIamPolicy"),
             bucketOptions));
   }
@@ -301,7 +382,7 @@ public class ITAccessTest {
   public void testBucketPolicyV3() {
     // Enable Uniform Bucket-Level Access
     storage.update(
-        BucketInfo.newBuilder(bucketName)
+        BucketInfo.newBuilder(bucketFixture.getBucketInfo().getName())
             .setIamConfiguration(
                 BucketInfo.IamConfiguration.newBuilder()
                     .setIsUniformBucketLevelAccessEnabled(true)
@@ -364,7 +445,8 @@ public class ITAccessTest {
                 .build());
 
     // Validate getting policy.
-    Policy currentPolicy = storage.getIamPolicy(bucketName, bucketOptions);
+    Policy currentPolicy =
+        storage.getIamPolicy(bucketFixture.getBucketInfo().getName(), bucketOptions);
     assertEquals(bindingsWithoutPublicRead, currentPolicy.getBindingsList());
 
     // Validate updating policy.
@@ -376,7 +458,7 @@ public class ITAccessTest {
             .build());
     Policy updatedPolicy =
         storage.setIamPolicy(
-            bucketName,
+            bucketFixture.getBucketInfo().getName(),
             currentPolicy.toBuilder().setBindings(currentBindings).build(),
             bucketOptions);
     assertTrue(
@@ -397,7 +479,7 @@ public class ITAccessTest {
 
     Policy revertedPolicy =
         storage.setIamPolicy(
-            bucketName,
+            bucketFixture.getBucketInfo().getName(),
             updatedPolicy.toBuilder().setBindings(updatedBindings).build(),
             bucketOptions);
 
@@ -424,7 +506,7 @@ public class ITAccessTest {
             .build());
     Policy conditionalPolicy =
         storage.setIamPolicy(
-            bucketName,
+            bucketFixture.getBucketInfo().getName(),
             revertedPolicy.toBuilder().setBindings(conditionalBindings).setVersion(3).build(),
             bucketOptions);
     assertTrue(
@@ -434,7 +516,7 @@ public class ITAccessTest {
     // Remove Conditional Policy
     conditionalPolicy =
         storage.setIamPolicy(
-            bucketName,
+            bucketFixture.getBucketInfo().getName(),
             conditionalPolicy.toBuilder().setBindings(updatedBindings).setVersion(3).build(),
             bucketOptions);
 
@@ -443,13 +525,13 @@ public class ITAccessTest {
     assertEquals(
         expectedPermissions,
         storage.testIamPermissions(
-            bucketName,
+            bucketFixture.getBucketInfo().getName(),
             ImmutableList.of("storage.buckets.getIamPolicy", "storage.buckets.setIamPolicy"),
             bucketOptions));
 
     // Disable Uniform Bucket-Level Access
     storage.update(
-        BucketInfo.newBuilder(bucketName)
+        BucketInfo.newBuilder(bucketFixture.getBucketInfo().getName())
             .setIamConfiguration(
                 BucketInfo.IamConfiguration.newBuilder()
                     .setIsUniformBucketLevelAccessEnabled(false)
@@ -489,7 +571,8 @@ public class ITAccessTest {
         // Expected: Listing legacy ACLs should fail on a BPO enabled bucket
       }
     } finally {
-      RemoteStorageHelper.forceDelete(storage, bucket, 1, TimeUnit.MINUTES);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), bucket, 1, TimeUnit.MINUTES);
     }
   }
 
@@ -523,7 +606,8 @@ public class ITAccessTest {
         // Expected: Listing legacy ACLs should fail on a BPO enabled bucket
       }
     } finally {
-      RemoteStorageHelper.forceDelete(storage, bucket, 1, TimeUnit.MINUTES);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), bucket, 1, TimeUnit.MINUTES);
     }
   }
 
@@ -576,7 +660,8 @@ public class ITAccessTest {
       assertEquals(User.ofAllAuthenticatedUsers(), remoteBucket.getAcl().get(0).getEntity());
       assertEquals(Role.READER, remoteBucket.getAcl().get(0).getRole());
     } finally {
-      RemoteStorageHelper.forceDelete(storage, bpoBucket, 1, TimeUnit.MINUTES);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), bpoBucket, 1, TimeUnit.MINUTES);
     }
   }
 
@@ -619,7 +704,8 @@ public class ITAccessTest {
         assertEquals(storageException.getCode(), 412);
       }
     } finally {
-      RemoteStorageHelper.forceDelete(storage, papBucket, 1, TimeUnit.MINUTES);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), papBucket, 1, TimeUnit.MINUTES);
     }
   }
 
@@ -657,7 +743,8 @@ public class ITAccessTest {
         fail("pap: expected adding allUsers policy to bucket to succeed");
       }
     } finally {
-      RemoteStorageHelper.forceDelete(storage, papBucket, 1, TimeUnit.MINUTES);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), papBucket, 1, TimeUnit.MINUTES);
     }
   }
 
@@ -708,7 +795,8 @@ public class ITAccessTest {
           bucket.getIamConfiguration().getPublicAccessPrevention(),
           BucketInfo.PublicAccessPrevention.ENFORCED);
     } finally {
-      RemoteStorageHelper.forceDelete(storage, papBucket, 1, TimeUnit.MINUTES);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), papBucket, 1, TimeUnit.MINUTES);
     }
   }
 
@@ -718,7 +806,7 @@ public class ITAccessTest {
     Iterator<Bucket> bucketIterator =
         storage
             .list(
-                Storage.BucketListOption.prefix(bucketName),
+                Storage.BucketListOption.prefix(bucketFixture.getBucketInfo().getName()),
                 Storage.BucketListOption.fields(),
                 Storage.BucketListOption.userProject(projectId))
             .iterateAll()
@@ -727,13 +815,15 @@ public class ITAccessTest {
       Thread.sleep(500);
       bucketIterator =
           storage
-              .list(Storage.BucketListOption.prefix(bucketName), Storage.BucketListOption.fields())
+              .list(
+                  Storage.BucketListOption.prefix(bucketFixture.getBucketInfo().getName()),
+                  Storage.BucketListOption.fields())
               .iterateAll()
               .iterator();
     }
     while (bucketIterator.hasNext()) {
       Bucket remoteBucket = bucketIterator.next();
-      assertTrue(remoteBucket.getName().startsWith(bucketName));
+      assertTrue(remoteBucket.getName().startsWith(bucketFixture.getBucketInfo().getName()));
       assertNull(remoteBucket.getCreateTime());
       assertNull(remoteBucket.getSelfLink());
     }
@@ -741,7 +831,7 @@ public class ITAccessTest {
 
   @Test
   public void testRetentionPolicyNoLock() throws ExecutionException, InterruptedException {
-    String bucketName = RemoteStorageHelper.generateBucketName();
+    String bucketName = bucketFixture.newBucketName();
     Bucket remoteBucket =
         storage.create(
             BucketInfo.newBuilder(bucketName).setRetentionPeriod(RETENTION_PERIOD).build());
@@ -763,14 +853,15 @@ public class ITAccessTest {
       remoteBucket = remoteBucket.toBuilder().setRetentionPeriod(null).build().update();
       assertNull(remoteBucket.getRetentionPeriod());
     } finally {
-      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), bucketName, 5, TimeUnit.SECONDS);
     }
   }
 
-  private static void unsetRequesterPays() {
+  private static void unsetRequesterPays(Storage storage, BucketFixture requesterPaysFixture) {
     Bucket remoteBucket =
         storage.get(
-            requesterPaysBucketName,
+            requesterPaysFixture.getBucketInfo().getName(),
             Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING),
             Storage.BucketGetOption.userProject(storage.getOptions().getProjectId()));
     // Disable requester pays in case a test fails to clean up.
@@ -786,7 +877,7 @@ public class ITAccessTest {
   @Test
   @SuppressWarnings({"unchecked", "deprecation"})
   public void testEnableAndDisableBucketPolicyOnlyOnExistingBucket() throws Exception {
-    String bpoBucket = RemoteStorageHelper.generateBucketName();
+    String bpoBucket = bucketFixture.newBucketName();
     try {
       // BPO is disabled by default.
       Bucket bucket =
@@ -832,13 +923,14 @@ public class ITAccessTest {
       assertEquals(User.ofAllAuthenticatedUsers(), remoteBucket.getAcl().get(0).getEntity());
       assertEquals(Role.READER, remoteBucket.getAcl().get(0).getRole());
     } finally {
-      RemoteStorageHelper.forceDelete(storage, bpoBucket, 1, TimeUnit.MINUTES);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), bpoBucket, 1, TimeUnit.MINUTES);
     }
   }
 
   @Test
   public void testBlobAcl() {
-    BlobId blobId = BlobId.of(bucketName, "test-blob-acl");
+    BlobId blobId = BlobId.of(bucketFixture.getBucketInfo().getName(), "test-blob-acl");
     BlobInfo blob = BlobInfo.newBuilder(blobId).build();
     storage.create(blob);
     assertNull(storage.getAcl(blobId, User.ofAllAuthenticatedUsers()));
@@ -851,7 +943,7 @@ public class ITAccessTest {
     assertTrue(storage.deleteAcl(blobId, User.ofAllAuthenticatedUsers()));
     assertNull(storage.getAcl(blobId, User.ofAllAuthenticatedUsers()));
     // test non-existing blob
-    BlobId otherBlobId = BlobId.of(bucketName, "test-blob-acl", -1L);
+    BlobId otherBlobId = BlobId.of(bucketFixture.getBucketInfo().getName(), "test-blob-acl", -1L);
     try {
       assertNull(storage.getAcl(otherBlobId, User.ofAllAuthenticatedUsers()));
       fail("Expected an 'Invalid argument' exception");
@@ -899,7 +991,7 @@ public class ITAccessTest {
             .build());
   }
 
-  static <T> T retry429s(Callable<T> c) {
+  static <T> T retry429s(Callable<T> c, Storage storage) {
     return RetryHelper.runWithRetries(
         c,
         storage.getOptions().getRetrySettings(),
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobReadChannelTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobReadChannelTest.java
index 47dbe5e2..c456148c 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobReadChannelTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITBlobReadChannelTest.java
@@ -32,6 +32,7 @@ import com.google.cloud.storage.BucketFixture;
 import com.google.cloud.storage.DataGeneration;
 import com.google.cloud.storage.Storage;
 import com.google.cloud.storage.StorageFixture;
+import com.google.common.collect.ImmutableList;
 import com.google.common.io.BaseEncoding;
 import com.google.common.io.ByteStreams;
 import java.io.ByteArrayInputStream;
@@ -46,15 +47,20 @@ import java.nio.file.StandardOpenOption;
 import java.util.Arrays;
 import java.util.Random;
 import java.util.zip.GZIPInputStream;
-import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public final class ITBlobReadChannelTest {
 
+  // Stubbed From GRPC Uncomment Fixtures and parameters to enable GRPC tests
+
   private static final int _16MiB = 16 * 1024 * 1024;
   private static final int _256KiB = 256 * 1024;
   private static final String BLOB_STRING_CONTENT = "Hello Google Cloud Storage!";
@@ -69,22 +75,47 @@ public final class ITBlobReadChannelTest {
   @Rule public final TemporaryFolder tmp = new TemporaryFolder();
 
   @ClassRule(order = 1)
-  public static final StorageFixture storageFixture = StorageFixture.defaultHttp();
+  public static final StorageFixture storageFixtureHttp = StorageFixture.defaultHttp();
+  /*
+  @ClassRule(order = 1)
+  public static final StorageFixture storageFixtureGrpc = StorageFixture.defaultGrpc();
+   */
 
   @ClassRule(order = 2)
-  public static final BucketFixture bucketFixture =
-      BucketFixture.newBuilder().setHandle(storageFixture::getInstance).build();
-
-  private Storage storage;
-  private String bucketName;
-  private String blobName;
+  public static final BucketFixture bucketFixtureHttp =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-http-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
+  /*
+  @ClassRule(order = 2)
+  public static final BucketFixture bucketFixtureGrpc =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-grpc-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
+   */
+
+  private final Storage storage;
+  private final BucketFixture bucketFixture;
+  private final String clientName;
+
+  public ITBlobReadChannelTest(
+      String clientName, StorageFixture storageFixture, BucketFixture bucketFixture) {
+    this.storage = storageFixture.getInstance();
+    this.bucketFixture = bucketFixture;
+    this.clientName = clientName;
+  }
 
-  @Before
-  public void setUp() throws Exception {
-    storage = storageFixture.getInstance();
-    bucketName = bucketFixture.getBucketInfo().getName();
+  @Parameters(name = "{0}")
+  public static Iterable<Object[]> data() {
+    return ImmutableList.of(new Object[] {"JSON/Prod", storageFixtureHttp, bucketFixtureHttp});
+    /*
+    return ImmutableList.of(
+        new Object[] {"JSON/Prod", storageFixtureHttp, bucketFixtureHttp},
+        new Object[] {"GRPC/Prod", storageFixtureGrpc, bucketFixtureGrpc});
+    */
 
-    blobName = String.format("%s/src", testName.getMethodName());
   }
 
   @Test
@@ -126,7 +157,8 @@ public final class ITBlobReadChannelTest {
 
   @Test
   public void testLimit_downloadToFile() throws IOException {
-    BlobId blobId = BlobId.of(bucketName, blobName);
+    String blobName = String.format("%s/src", testName.getMethodName());
+    BlobId blobId = BlobId.of(bucketFixture.getBucketInfo().getName(), blobName);
     ByteBuffer content = dataGeneration.randByteBuffer(108);
     try (WriteChannel writer = storage.writer(BlobInfo.newBuilder(blobId).build())) {
       writer.write(content);
@@ -159,7 +191,7 @@ public final class ITBlobReadChannelTest {
   @Test
   public void testReadChannelFail() {
     String blobName = "test-read-channel-blob-fail";
-    BlobInfo blob = BlobInfo.newBuilder(bucketName, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     Blob remoteBlob = storage.create(blob);
     assertNotNull(remoteBlob);
     try (ReadChannel reader =
@@ -176,7 +208,8 @@ public final class ITBlobReadChannelTest {
     } catch (IOException ex) {
       // expected
     }
-    BlobId blobIdWrongGeneration = BlobId.of(bucketName, blobName, -1L);
+    BlobId blobIdWrongGeneration =
+        BlobId.of(bucketFixture.getBucketInfo().getName(), blobName, -1L);
     try (ReadChannel reader =
         storage.reader(blobIdWrongGeneration, Storage.BlobSourceOption.generationMatch())) {
       reader.read(ByteBuffer.allocate(42));
@@ -189,7 +222,7 @@ public final class ITBlobReadChannelTest {
   @Test
   public void testReadChannelFailUpdatedGeneration() throws IOException {
     String blobName = "test-read-blob-fail-updated-generation";
-    BlobInfo blob = BlobInfo.newBuilder(bucketName, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     Random random = new Random();
     int chunkSize = 1024;
     int blobSize = 2 * chunkSize;
@@ -224,13 +257,14 @@ public final class ITBlobReadChannelTest {
   public void testReadCompressedBlob() throws IOException {
     String blobName = "test-read-compressed-blob";
     BlobInfo blobInfo =
-        BlobInfo.newBuilder(BlobId.of(bucketName, blobName))
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
             .setContentType("text/plain")
             .setContentEncoding("gzip")
             .build();
     Blob blob = storage.create(blobInfo, COMPRESSED_CONTENT);
     try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
-      try (ReadChannel reader = storage.reader(BlobId.of(bucketName, blobName))) {
+      try (ReadChannel reader =
+          storage.reader(BlobId.of(bucketFixture.getBucketInfo().getName(), blobName))) {
         reader.setChunkSize(8);
         ByteBuffer buffer = ByteBuffer.allocate(8);
         while (reader.read(buffer) != -1) {
@@ -240,7 +274,8 @@ public final class ITBlobReadChannelTest {
         }
       }
       assertArrayEquals(
-          BLOB_STRING_CONTENT.getBytes(UTF_8), storage.readAllBytes(bucketName, blobName));
+          BLOB_STRING_CONTENT.getBytes(UTF_8),
+          storage.readAllBytes(bucketFixture.getBucketInfo().getName(), blobName));
       assertArrayEquals(COMPRESSED_CONTENT, output.toByteArray());
       try (GZIPInputStream zipInput =
           new GZIPInputStream(new ByteArrayInputStream(output.toByteArray()))) {
@@ -251,7 +286,8 @@ public final class ITBlobReadChannelTest {
 
   private void doLimitTest(int srcContentSize, int rangeBegin, int rangeEnd, int chunkSize)
       throws IOException {
-    BlobInfo src = BlobInfo.newBuilder(bucketName, blobName).build();
+    String blobName = String.format("%s/src", testName.getMethodName());
+    BlobInfo src = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     ByteBuffer content = dataGeneration.randByteBuffer(srcContentSize);
     ByteBuffer dup = content.duplicate();
     dup.position(rangeBegin);
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNotificationTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNotificationTest.java
index 097ca9ae..a6204b8a 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNotificationTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITNotificationTest.java
@@ -26,6 +26,7 @@ import com.google.cloud.storage.NotificationInfo;
 import com.google.cloud.storage.Storage;
 import com.google.cloud.storage.StorageFixture;
 import com.google.cloud.storage.testing.RemoteStorageHelper;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.iam.v1.Binding;
 import com.google.iam.v1.GetIamPolicyRequest;
@@ -42,14 +43,32 @@ import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class ITNotificationTest {
   @ClassRule(order = 1)
-  public static final StorageFixture storageFixture = StorageFixture.defaultHttp();
+  public static final StorageFixture storageFixtureHttp = StorageFixture.defaultHttp();
 
+  /*
+  @ClassRule(order = 1)
+  public static final StorageFixture storageFixtureGrpc = StorageFixture.defaultGrpc();
+   */
+
+  @ClassRule(order = 2)
+  public static final BucketFixture bucketFixtureHttp =
+      BucketFixture.newBuilder().setHandle(storageFixtureHttp::getInstance).build();
+
+  /*
   @ClassRule(order = 2)
-  public static final BucketFixture bucketFixture =
-      BucketFixture.newBuilder().setHandle(storageFixture::getInstance).build();
+  public static final BucketFixture bucketFixtureGrpc =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-grpc-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
+   */
 
   private static final String PROJECT = ServiceOptions.getDefaultProjectId();
   private static final String ID = UUID.randomUUID().toString().substring(0, 8);
@@ -60,14 +79,30 @@ public class ITNotificationTest {
   private static final Map<String, String> CUSTOM_ATTRIBUTES = ImmutableMap.of("label1", "value1");
   private static final Logger log = Logger.getLogger(ITNotificationTest.class.getName());
 
-  private static Storage storage;
-  private static String bucketName;
+  private final Storage storage;
+  private final BucketFixture bucketFixture;
+  private final String clientName;
   private static TopicAdminClient topicAdminClient;
 
+  public ITNotificationTest(
+      String clientName, StorageFixture storageFixture, BucketFixture bucketFixture) {
+    this.clientName = clientName;
+    this.storage = storageFixture.getInstance();
+    this.bucketFixture = bucketFixture;
+  }
+
+  @Parameters(name = "{0}")
+  public static Iterable<Object[]> data() {
+    return ImmutableList.of(new Object[] {"JSON/Prod", storageFixtureHttp, bucketFixtureHttp});
+    /*
+    return ImmutableList.of(
+        new Object[] {"JSON/Prod", storageFixtureHttp, bucketFixtureHttp},
+        new Object[] {"GRPC/Prod", storageFixtureGrpc, bucketFixtureGrpc});
+     */
+  }
+
   @BeforeClass
   public static void setup() throws IOException {
-    storage = storageFixture.getInstance();
-    bucketName = bucketFixture.getBucketInfo().getName();
     // Configure topic admin client for notification.
     topicAdminClient = configureTopicAdminClient();
   }
@@ -111,8 +146,9 @@ public class ITNotificationTest {
             .setPayloadFormat(PAYLOAD_FORMAT)
             .build();
     try {
-      assertThat(storage.listNotifications(bucketName)).isEmpty();
-      Notification notification = storage.createNotification(bucketName, notificationInfo);
+      assertThat(storage.listNotifications(bucketFixture.getBucketInfo().getName())).isEmpty();
+      Notification notification =
+          storage.createNotification(bucketFixture.getBucketInfo().getName(), notificationInfo);
       assertThat(notification.getNotificationId()).isNotNull();
       assertThat(CUSTOM_ATTRIBUTES).isEqualTo(notification.getCustomAttributes());
       assertThat(PAYLOAD_FORMAT.name()).isEqualTo(notification.getPayloadFormat().name());
@@ -120,7 +156,8 @@ public class ITNotificationTest {
 
       // Gets the notification with the specified id.
       Notification actualNotification =
-          storage.getNotification(bucketName, notification.getNotificationId());
+          storage.getNotification(
+              bucketFixture.getBucketInfo().getName(), notification.getNotificationId());
       assertThat(actualNotification.getNotificationId())
           .isEqualTo(notification.getNotificationId());
       assertThat(actualNotification.getTopic().trim()).isEqualTo(notification.getTopic().trim());
@@ -132,19 +169,29 @@ public class ITNotificationTest {
           .isEqualTo(notification.getCustomAttributes());
 
       // Retrieves the list of notifications associated with the bucket.
-      List<Notification> notifications = storage.listNotifications(bucketName);
+      List<Notification> notifications =
+          storage.listNotifications(bucketFixture.getBucketInfo().getName());
       assertThat(notifications.size()).isEqualTo(1);
       assertThat(notifications.get(0).getNotificationId())
           .isEqualTo(actualNotification.getNotificationId());
 
       // Deletes the notification with the specified id.
-      assertThat(storage.deleteNotification(bucketName, notification.getNotificationId())).isTrue();
-      assertThat(storage.deleteNotification(bucketName, notification.getNotificationId()))
+      assertThat(
+              storage.deleteNotification(
+                  bucketFixture.getBucketInfo().getName(), notification.getNotificationId()))
+          .isTrue();
+      assertThat(
+              storage.deleteNotification(
+                  bucketFixture.getBucketInfo().getName(), notification.getNotificationId()))
           .isFalse();
-      assertThat(storage.getNotification(bucketName, notification.getNotificationId())).isNull();
-      assertThat(storage.listNotifications(bucketName)).isEmpty();
+      assertThat(
+              storage.getNotification(
+                  bucketFixture.getBucketInfo().getName(), notification.getNotificationId()))
+          .isNull();
+      assertThat(storage.listNotifications(bucketFixture.getBucketInfo().getName())).isEmpty();
     } finally {
-      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
+      RemoteStorageHelper.forceDelete(
+          storage, bucketFixture.getBucketInfo().getName(), 5, TimeUnit.SECONDS);
     }
   }
 }
diff --git a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java
index 3249e03a..30ddf659 100644
--- a/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java
+++ b/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITObjectTest.java
@@ -26,6 +26,7 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import com.google.api.gax.paging.Page;
 import com.google.cloud.ReadChannel;
@@ -70,10 +71,13 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import javax.crypto.spec.SecretKeySpec;
 import org.junit.AfterClass;
-import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class ITObjectTest {
   private static final String CONTENT_TYPE = "text/plain";
   private static final byte[] BLOB_BYTE_CONTENT = {0xD, 0xE, 0xA, 0xD};
@@ -87,32 +91,68 @@ public class ITObjectTest {
   private static final Long RETENTION_PERIOD_IN_MILLISECONDS = RETENTION_PERIOD * 1000;
 
   @ClassRule(order = 1)
-  public static final StorageFixture storageFixture =
-      StorageFixture.of(() -> RemoteStorageHelper.create().getOptions().getService());
+  public static final StorageFixture storageFixtureHttp = StorageFixture.defaultHttp();
+
+  @ClassRule(order = 1)
+  public static final StorageFixture storageFixtureGrpc = StorageFixture.defaultGrpc();
+
+  @ClassRule(order = 2)
+  public static final BucketFixture bucketFixtureHttp =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-http-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
+
+  @ClassRule(order = 2)
+  public static final BucketFixture requesterPaysBucketFixtureHttp =
+      BucketFixture.newBuilder().setHandle(storageFixtureHttp::getInstance).build();
+
+  @ClassRule(order = 2)
+  public static final BucketFixture bucketFixtureGrpc =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-grpc-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
 
   @ClassRule(order = 2)
-  public static final BucketFixture bucketFixture =
-      BucketFixture.newBuilder().setHandle(storageFixture::getInstance).build();
-
-  @ClassRule(order = 3)
-  public static final BucketFixture requesterPaysFixture =
-      BucketFixture.newBuilder().setHandle(storageFixture::getInstance).build();
-
-  private static String BUCKET;
-  private static String BUCKET_REQUESTER_PAYS;
-  private static Storage storage;
-
-  @BeforeClass
-  public static void beforeClass() throws IOException {
-    storage = storageFixture.getInstance();
-    BUCKET = bucketFixture.getBucketInfo().getName();
-    BUCKET_REQUESTER_PAYS = requesterPaysFixture.getBucketInfo().getName();
+  public static final BucketFixture requesterPaysBucketFixtureGrpc =
+      BucketFixture.newBuilder()
+          .setBucketNameFmtString("java-storage-grpc-%s")
+          .setHandle(storageFixtureHttp::getInstance)
+          .build();
+
+  private final BucketFixture bucketFixture;
+  private final BucketFixture requesterPaysBucketFixture;
+  private final String clientName;
+  private final Storage storage;
+
+  public ITObjectTest(
+      String clientName,
+      StorageFixture storageFixture,
+      BucketFixture bucketFixture,
+      BucketFixture requesterPaysBucketFixture) {
+    this.storage = storageFixture.getInstance();
+    this.bucketFixture = bucketFixture;
+    this.requesterPaysBucketFixture = requesterPaysBucketFixture;
+    this.clientName = clientName;
+  }
+
+  @Parameters(name = "{0}")
+  public static Iterable<Object[]> data() {
+    return Arrays.asList(
+        new Object[] {
+          "JSON/Prod", storageFixtureHttp, bucketFixtureHttp, requesterPaysBucketFixtureHttp
+        },
+        new Object[] {
+          "GRPC/Prod", storageFixtureGrpc, bucketFixtureGrpc, requesterPaysBucketFixtureGrpc
+        });
   }
 
-  private static void unsetRequesterPays() {
+  private static void unsetRequesterPays(
+      Storage storage, BucketFixture requesterPaysBucketFixture) {
     Bucket remoteBucket =
         storage.get(
-            BUCKET_REQUESTER_PAYS,
+            requesterPaysBucketFixture.getBucketInfo().getName(),
             Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING),
             Storage.BucketGetOption.userProject(storage.getOptions().getProjectId()));
     // Disable requester pays in case a test fails to clean up.
@@ -127,20 +167,23 @@ public class ITObjectTest {
 
   @AfterClass
   public static void afterClass() {
-    unsetRequesterPays();
+    unsetRequesterPays(storageFixtureHttp.getInstance(), requesterPaysBucketFixtureHttp);
+    unsetRequesterPays(storageFixtureGrpc.getInstance(), requesterPaysBucketFixtureGrpc);
   }
 
   @Test
   public void testCreateBlob() {
     String blobName = "test-create-blob";
     BlobInfo blob =
-        BlobInfo.newBuilder(BUCKET, blobName).setCustomTime(System.currentTimeMillis()).build();
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
+            .setCustomTime(System.currentTimeMillis())
+            .build();
     Blob remoteBlob = storage.create(blob, BLOB_BYTE_CONTENT);
     assertNotNull(remoteBlob);
     assertNotNull(remoteBlob.getCustomTime());
     assertEquals(blob.getBucket(), remoteBlob.getBucket());
     assertEquals(blob.getName(), remoteBlob.getName());
-    byte[] readBytes = storage.readAllBytes(BUCKET, blobName);
+    byte[] readBytes = storage.readAllBytes(bucketFixture.getBucketInfo().getName(), blobName);
     assertArrayEquals(BLOB_BYTE_CONTENT, readBytes);
     assertTrue(remoteBlob.delete());
   }
@@ -149,7 +192,7 @@ public class ITObjectTest {
   public void testCreateBlobMd5Crc32cFromHexString() {
     String blobName = "test-create-blob-md5-crc32c-from-hex-string";
     BlobInfo blob =
-        BlobInfo.newBuilder(BUCKET, blobName)
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
             .setContentType(CONTENT_TYPE)
             .setMd5FromHexString("3b54781b51c94835084898e821899585")
             .setCrc32cFromHexString("f4ddc43d")
@@ -160,7 +203,7 @@ public class ITObjectTest {
     assertEquals(blob.getName(), remoteBlob.getName());
     assertEquals(blob.getMd5ToHexString(), remoteBlob.getMd5ToHexString());
     assertEquals(blob.getCrc32cToHexString(), remoteBlob.getCrc32cToHexString());
-    byte[] readBytes = storage.readAllBytes(BUCKET, blobName);
+    byte[] readBytes = storage.readAllBytes(bucketFixture.getBucketInfo().getName(), blobName);
     assertArrayEquals(BLOB_BYTE_CONTENT, readBytes);
     assertTrue(remoteBlob.delete());
   }
@@ -168,14 +211,17 @@ public class ITObjectTest {
   @Test
   public void testCreateGetBlobWithEncryptionKey() {
     String blobName = "test-create-with-customer-key-blob";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     Blob remoteBlob =
         storage.create(blob, BLOB_BYTE_CONTENT, Storage.BlobTargetOption.encryptionKey(KEY));
     assertNotNull(remoteBlob);
     assertEquals(blob.getBucket(), remoteBlob.getBucket());
     assertEquals(blob.getName(), remoteBlob.getName());
     byte[] readBytes =
-        storage.readAllBytes(BUCKET, blobName, Storage.BlobSourceOption.decryptionKey(BASE64_KEY));
+        storage.readAllBytes(
+            bucketFixture.getBucketInfo().getName(),
+            blobName,
+            Storage.BlobSourceOption.decryptionKey(BASE64_KEY));
     assertArrayEquals(BLOB_BYTE_CONTENT, readBytes);
     remoteBlob =
         storage.get(
@@ -189,12 +235,12 @@ public class ITObjectTest {
   @Test
   public void testCreateEmptyBlob() {
     String blobName = "test-create-empty-blob";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     Blob remoteBlob = storage.create(blob);
     assertNotNull(remoteBlob);
     assertEquals(blob.getBucket(), remoteBlob.getBucket());
     assertEquals(blob.getName(), remoteBlob.getName());
-    byte[] readBytes = storage.readAllBytes(BUCKET, blobName);
+    byte[] readBytes = storage.readAllBytes(bucketFixture.getBucketInfo().getName(), blobName);
     assertArrayEquals(new byte[0], readBytes);
   }
 
@@ -202,14 +248,17 @@ public class ITObjectTest {
   @SuppressWarnings({"unchecked", "deprecation"})
   public void testCreateBlobStream() {
     String blobName = "test-create-blob-stream";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).setContentType(CONTENT_TYPE).build();
+    BlobInfo blob =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
+            .setContentType(CONTENT_TYPE)
+            .build();
     ByteArrayInputStream stream = new ByteArrayInputStream(BLOB_STRING_CONTENT.getBytes(UTF_8));
     Blob remoteBlob = storage.create(blob, stream);
     assertNotNull(remoteBlob);
     assertEquals(blob.getBucket(), remoteBlob.getBucket());
     assertEquals(blob.getName(), remoteBlob.getName());
     assertEquals(blob.getContentType(), remoteBlob.getContentType());
-    byte[] readBytes = storage.readAllBytes(BUCKET, blobName);
+    byte[] readBytes = storage.readAllBytes(bucketFixture.getBucketInfo().getName(), blobName);
     assertEquals(BLOB_STRING_CONTENT, new String(readBytes, UTF_8));
   }
 
@@ -217,24 +266,28 @@ public class ITObjectTest {
   @SuppressWarnings({"unchecked", "deprecation"})
   public void testCreateBlobStreamDisableGzipContent() {
     String blobName = "test-create-blob-stream-disable-gzip-compression";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).setContentType(CONTENT_TYPE).build();
+    BlobInfo blob =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
+            .setContentType(CONTENT_TYPE)
+            .build();
     ByteArrayInputStream stream = new ByteArrayInputStream(BLOB_STRING_CONTENT.getBytes(UTF_8));
     Blob remoteBlob = storage.create(blob, stream, BlobWriteOption.disableGzipContent());
     assertNotNull(remoteBlob);
     assertEquals(blob.getBucket(), remoteBlob.getBucket());
     assertEquals(blob.getName(), remoteBlob.getName());
     assertEquals(blob.getContentType(), remoteBlob.getContentType());
-    byte[] readBytes = storage.readAllBytes(BUCKET, blobName);
+    byte[] readBytes = storage.readAllBytes(bucketFixture.getBucketInfo().getName(), blobName);
     assertEquals(BLOB_STRING_CONTENT, new String(readBytes, UTF_8));
   }
 
   @Test
   public void testCreateBlobFail() {
     String blobName = "test-create-blob-fail";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     Blob remoteBlob = storage.create(blob);
     assertNotNull(remoteBlob);
-    BlobInfo wrongGenerationBlob = BlobInfo.newBuilder(BUCKET, blobName, -1L).build();
+    BlobInfo wrongGenerationBlob =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName, -1L).build();
     try {
       storage.create(
           wrongGenerationBlob, BLOB_BYTE_CONTENT, Storage.BlobTargetOption.generationMatch());
@@ -247,9 +300,13 @@ public class ITObjectTest {
   @Test
   @SuppressWarnings({"unchecked", "deprecation"})
   public void testCreateBlobMd5Fail() {
+    // Error Handling for GRPC not complete
+    // b/247621346
+    assumeTrue(clientName.startsWith("JSON"));
+
     String blobName = "test-create-blob-md5-fail";
     BlobInfo blob =
-        BlobInfo.newBuilder(BUCKET, blobName)
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
             .setContentType(CONTENT_TYPE)
             .setMd5("O1R4G1HJSDUISJjoIYmVhQ==")
             .build();
@@ -264,8 +321,14 @@ public class ITObjectTest {
 
   @Test
   public void testGetBlobEmptySelectedFields() {
+    // FieldMask on get not supported by GRPC yet.
+    assumeTrue(clientName.startsWith("JSON"));
+
     String blobName = "test-get-empty-selected-fields-blob";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).setContentType(CONTENT_TYPE).build();
+    BlobInfo blob =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
+            .setContentType(CONTENT_TYPE)
+            .build();
     assertNotNull(storage.create(blob));
     Blob remoteBlob = storage.get(blob.getBlobId(), Storage.BlobGetOption.fields());
     assertEquals(blob.getBlobId(), remoteBlob.getBlobId());
@@ -274,9 +337,12 @@ public class ITObjectTest {
 
   @Test
   public void testGetBlobSelectedFields() {
+    // FieldMask on get not supported by GRPC yet.
+    assumeTrue(clientName.startsWith("JSON"));
+
     String blobName = "test-get-selected-fields-blob";
     BlobInfo blob =
-        BlobInfo.newBuilder(BUCKET, blobName)
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
             .setContentType(CONTENT_TYPE)
             .setMetadata(ImmutableMap.of("k", "v"))
             .build();
@@ -290,9 +356,12 @@ public class ITObjectTest {
 
   @Test
   public void testGetBlobAllSelectedFields() {
+    // FieldMask on get not supported by GRPC yet.
+    assumeTrue(clientName.startsWith("JSON"));
+
     String blobName = "test-get-all-selected-fields-blob";
     BlobInfo blob =
-        BlobInfo.newBuilder(BUCKET, blobName)
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
             .setContentType(CONTENT_TYPE)
             .setMetadata(ImmutableMap.of("k", "v"))
             .build();
@@ -309,10 +378,10 @@ public class ITObjectTest {
   @Test
   public void testGetBlobFail() {
     String blobName = "test-get-blob-fail";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     Blob remoteBlob = storage.create(blob);
     assertNotNull(remoteBlob);
-    BlobId wrongGenerationBlob = BlobId.of(BUCKET, blobName);
+    BlobId wrongGenerationBlob = BlobId.of(bucketFixture.getBucketInfo().getName(), blobName);
     try {
       storage.get(wrongGenerationBlob, Storage.BlobGetOption.generationMatch(-1));
       fail("StorageException was expected");
@@ -324,10 +393,10 @@ public class ITObjectTest {
   @Test
   public void testGetBlobFailNonExistingGeneration() {
     String blobName = "test-get-blob-fail-non-existing-generation";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     Blob remoteBlob = storage.create(blob);
     assertNotNull(remoteBlob);
-    BlobId wrongGenerationBlob = BlobId.of(BUCKET, blobName, -1L);
+    BlobId wrongGenerationBlob = BlobId.of(bucketFixture.getBucketInfo().getName(), blobName, -1L);
     try {
       assertNull(storage.get(wrongGenerationBlob));
       fail("Expected an 'Invalid argument' exception");
@@ -338,17 +407,20 @@ public class ITObjectTest {
 
   @Test(timeout = 5000)
   public void testListBlobsSelectedFields() throws InterruptedException {
+    // FieldMask on list not supported by GRPC yet.
+    assumeTrue(clientName.startsWith("JSON"));
+
     String[] blobNames = {
       "test-list-blobs-selected-fields-blob1", "test-list-blobs-selected-fields-blob2"
     };
     ImmutableMap<String, String> metadata = ImmutableMap.of("k", "v");
     BlobInfo blob1 =
-        BlobInfo.newBuilder(BUCKET, blobNames[0])
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobNames[0])
             .setContentType(CONTENT_TYPE)
             .setMetadata(metadata)
             .build();
     BlobInfo blob2 =
-        BlobInfo.newBuilder(BUCKET, blobNames[1])
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobNames[1])
             .setContentType(CONTENT_TYPE)
             .setMetadata(metadata)
             .build();
@@ -358,7 +430,7 @@ public class ITObjectTest {
     assertNotNull(remoteBlob2);
     Page<Blob> page =
         storage.list(
-            BUCKET,
+            bucketFixture.getBucketInfo().getName(),
             Storage.BlobListOption.prefix("test-list-blobs-selected-fields-blob"),
             Storage.BlobListOption.fields(BlobField.METADATA));
     // Listing blobs is eventually consistent, we loop until the list is of the expected size. The
@@ -367,7 +439,7 @@ public class ITObjectTest {
       Thread.sleep(500);
       page =
           storage.list(
-              BUCKET,
+              bucketFixture.getBucketInfo().getName(),
               Storage.BlobListOption.prefix("test-list-blobs-selected-fields-blob"),
               Storage.BlobListOption.fields(BlobField.METADATA));
     }
@@ -375,7 +447,7 @@ public class ITObjectTest {
     Iterator<Blob> iterator = page.iterateAll().iterator();
     while (iterator.hasNext()) {
       Blob remoteBlob = iterator.next();
-      assertEquals(BUCKET, remoteBlob.getBucket());
+      assertEquals(bucketFixture.getBucketInfo().getName(), remoteBlob.getBucket());
       assertTrue(blobSet.contains(remoteBlob.getName()));
       assertEquals(metadata, remoteBlob.getMetadata());
       assertNull(remoteBlob.getContentType());
@@ -384,18 +456,27 @@ public class ITObjectTest {
 
   @Test(timeout = 5000)
   public void testListBlobsEmptySelectedFields() throws InterruptedException {
+    // FieldMask on list not supported by GRPC yet.
+    assumeTrue(clientName.startsWith("JSON"));
+
     String[] blobNames = {
       "test-list-blobs-empty-selected-fields-blob1", "test-list-blobs-empty-selected-fields-blob2"
     };
-    BlobInfo blob1 = BlobInfo.newBuilder(BUCKET, blobNames[0]).setContentType(CONTENT_TYPE).build();
-    BlobInfo blob2 = BlobInfo.newBuilder(BUCKET, blobNames[1]).setContentType(CONTENT_TYPE).build();
+    BlobInfo blob1 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobNames[0])
+            .setContentType(CONTENT_TYPE)
+            .build();
+    BlobInfo blob2 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobNames[1])
+            .setContentType(CONTENT_TYPE)
+            .build();
     Blob remoteBlob1 = storage.create(blob1);
     Blob remoteBlob2 = storage.create(blob2);
     assertNotNull(remoteBlob1);
     assertNotNull(remoteBlob2);
     Page<Blob> page =
         storage.list(
-            BUCKET,
+            bucketFixture.getBucketInfo().getName(),
             Storage.BlobListOption.prefix("test-list-blobs-empty-selected-fields-blob"),
             Storage.BlobListOption.fields());
     // Listing blobs is eventually consistent, we loop until the list is of the expected size. The
@@ -404,7 +485,7 @@ public class ITObjectTest {
       Thread.sleep(500);
       page =
           storage.list(
-              BUCKET,
+              bucketFixture.getBucketInfo().getName(),
               Storage.BlobListOption.prefix("test-list-blobs-empty-selected-fields-blob"),
               Storage.BlobListOption.fields());
     }
@@ -412,7 +493,7 @@ public class ITObjectTest {
     Iterator<Blob> iterator = page.iterateAll().iterator();
     while (iterator.hasNext()) {
       Blob remoteBlob = iterator.next();
-      assertEquals(BUCKET, remoteBlob.getBucket());
+      assertEquals(bucketFixture.getBucketInfo().getName(), remoteBlob.getBucket());
       assertTrue(blobSet.contains(remoteBlob.getName()));
       assertNull(remoteBlob.getContentType());
     }
@@ -420,9 +501,11 @@ public class ITObjectTest {
 
   @Test(timeout = 7500)
   public void testListBlobRequesterPays() throws InterruptedException {
-    unsetRequesterPays();
+    unsetRequesterPays(storage, requesterPaysBucketFixture);
     BlobInfo blob1 =
-        BlobInfo.newBuilder(BUCKET_REQUESTER_PAYS, "test-list-blobs-empty-selected-fields-blob1")
+        BlobInfo.newBuilder(
+                requesterPaysBucketFixture.getBucketInfo().getName(),
+                "test-list-blobs-empty-selected-fields-blob1")
             .setContentType(CONTENT_TYPE)
             .build();
     assertNotNull(storage.create(blob1));
@@ -430,7 +513,7 @@ public class ITObjectTest {
     // Test listing a Requester Pays bucket.
     Bucket remoteBucket =
         storage.get(
-            BUCKET_REQUESTER_PAYS,
+            requesterPaysBucketFixture.getBucketInfo().getName(),
             Storage.BucketGetOption.fields(BucketField.ID, BucketField.BILLING));
 
     assertTrue(remoteBucket.requesterPays() == null || !remoteBucket.requesterPays());
@@ -439,7 +522,7 @@ public class ITObjectTest {
     assertTrue(updatedBucket.requesterPays());
     try {
       storage.list(
-          BUCKET_REQUESTER_PAYS,
+          requesterPaysBucketFixture.getBucketInfo().getName(),
           Storage.BlobListOption.prefix("test-list-blobs-empty-selected-fields-blob"),
           Storage.BlobListOption.fields(),
           Storage.BlobListOption.userProject("fakeBillingProjectId"));
@@ -452,7 +535,7 @@ public class ITObjectTest {
     while (true) {
       Page<Blob> page =
           storage.list(
-              BUCKET_REQUESTER_PAYS,
+              requesterPaysBucketFixture.getBucketInfo().getName(),
               Storage.BlobListOption.prefix("test-list-blobs-empty-selected-fields-blob"),
               Storage.BlobListOption.fields(),
               Storage.BlobListOption.userProject(projectId));
@@ -469,9 +552,11 @@ public class ITObjectTest {
 
   @Test(timeout = 15000)
   public void testListBlobsVersioned() throws ExecutionException, InterruptedException {
-    String bucketName = RemoteStorageHelper.generateBucketName();
+    String bucketName = bucketFixture.newBucketName();
     Bucket bucket =
-        storage.create(BucketInfo.newBuilder(bucketName).setVersioningEnabled(true).build());
+        storageFixtureHttp
+            .getInstance()
+            .create(BucketInfo.newBuilder(bucketName).setVersioningEnabled(true).build());
     try {
       String[] blobNames = {"test-list-blobs-versioned-blob1", "test-list-blobs-versioned-blob2"};
       BlobInfo blob1 =
@@ -508,15 +593,18 @@ public class ITObjectTest {
         assertNotNull(remoteBlob.getGeneration());
       }
     } finally {
-      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), bucketName, 5, TimeUnit.SECONDS);
     }
   }
 
   @Test
   public void testListBlobsWithOffset() throws ExecutionException, InterruptedException {
-    String bucketName = RemoteStorageHelper.generateBucketName();
+    String bucketName = bucketFixture.newBucketName();
     Bucket bucket =
-        storage.create(BucketInfo.newBuilder(bucketName).setVersioningEnabled(true).build());
+        storageFixtureHttp
+            .getInstance()
+            .create(BucketInfo.newBuilder(bucketName).setVersioningEnabled(true).build());
     try {
       List<String> blobNames =
           ImmutableList.of("startOffset_blob1", "startOffset_blob2", "blob3_endOffset");
@@ -555,24 +643,34 @@ public class ITObjectTest {
               Storage.BlobListOption.endOffset("endOffset"));
       assertEquals(0, Iterators.size(page4.iterateAll().iterator()));
     } finally {
-      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), bucketName, 5, TimeUnit.SECONDS);
     }
   }
 
   @Test(timeout = 5000)
   public void testListBlobsCurrentDirectory() throws InterruptedException {
+    // This test is currently timing out for GRPC
+    assumeTrue(clientName.startsWith("JSON"));
+
     String directoryName = "test-list-blobs-current-directory/";
     String subdirectoryName = "subdirectory/";
     String[] blobNames = {directoryName + subdirectoryName + "blob1", directoryName + "blob2"};
-    BlobInfo blob1 = BlobInfo.newBuilder(BUCKET, blobNames[0]).setContentType(CONTENT_TYPE).build();
-    BlobInfo blob2 = BlobInfo.newBuilder(BUCKET, blobNames[1]).setContentType(CONTENT_TYPE).build();
+    BlobInfo blob1 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobNames[0])
+            .setContentType(CONTENT_TYPE)
+            .build();
+    BlobInfo blob2 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobNames[1])
+            .setContentType(CONTENT_TYPE)
+            .build();
     Blob remoteBlob1 = storage.create(blob1, BLOB_BYTE_CONTENT);
     Blob remoteBlob2 = storage.create(blob2, BLOB_BYTE_CONTENT);
     assertNotNull(remoteBlob1);
     assertNotNull(remoteBlob2);
     Page<Blob> page =
         storage.list(
-            BUCKET,
+            bucketFixture.getBucketInfo().getName(),
             Storage.BlobListOption.prefix("test-list-blobs-current-directory/"),
             Storage.BlobListOption.currentDirectory());
     // Listing blobs is eventually consistent, we loop until the list is of the expected size. The
@@ -581,14 +679,14 @@ public class ITObjectTest {
       Thread.sleep(500);
       page =
           storage.list(
-              BUCKET,
+              bucketFixture.getBucketInfo().getName(),
               Storage.BlobListOption.prefix("test-list-blobs-current-directory/"),
               Storage.BlobListOption.currentDirectory());
     }
     Iterator<Blob> iterator = page.iterateAll().iterator();
     while (iterator.hasNext()) {
       Blob remoteBlob = iterator.next();
-      assertEquals(BUCKET, remoteBlob.getBucket());
+      assertEquals(bucketFixture.getBucketInfo().getName(), remoteBlob.getBucket());
       if (remoteBlob.getName().equals(blobNames[1])) {
         assertEquals(CONTENT_TYPE, remoteBlob.getContentType());
         assertEquals(BLOB_BYTE_CONTENT.length, (long) remoteBlob.getSize());
@@ -605,7 +703,7 @@ public class ITObjectTest {
   @Test
   public void testUpdateBlob() {
     String blobName = "test-update-blob";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     Blob remoteBlob = storage.create(blob);
     assertNotNull(remoteBlob);
     Blob updatedBlob = remoteBlob.toBuilder().setContentType(CONTENT_TYPE).build().update();
@@ -621,7 +719,7 @@ public class ITObjectTest {
     ImmutableMap<String, String> metadata = ImmutableMap.of("k1", "a");
     ImmutableMap<String, String> newMetadata = ImmutableMap.of("k2", "b");
     BlobInfo blob =
-        BlobInfo.newBuilder(BUCKET, blobName)
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
             .setContentType(CONTENT_TYPE)
             .setMetadata(metadata)
             .build();
@@ -643,7 +741,7 @@ public class ITObjectTest {
     ImmutableMap<String, String> newMetadata = ImmutableMap.of("k2", "b");
     ImmutableMap<String, String> expectedMetadata = ImmutableMap.of("k1", "a", "k2", "b");
     BlobInfo blob =
-        BlobInfo.newBuilder(BUCKET, blobName)
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
             .setContentType(CONTENT_TYPE)
             .setMetadata(metadata)
             .build();
@@ -658,6 +756,9 @@ public class ITObjectTest {
 
   @Test
   public void testUpdateBlobUnsetMetadata() {
+    // Metadata update bug b/230510191
+    assumeTrue(clientName.startsWith("JSON"));
+
     String blobName = "test-update-blob-unset-metadata";
     ImmutableMap<String, String> metadata = ImmutableMap.of("k1", "a", "k2", "b");
     Map<String, String> newMetadata = new HashMap<>();
@@ -665,7 +766,7 @@ public class ITObjectTest {
     newMetadata.put("k2", null);
     ImmutableMap<String, String> expectedMetadata = ImmutableMap.of("k1", "a");
     BlobInfo blob =
-        BlobInfo.newBuilder(BUCKET, blobName)
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
             .setContentType(CONTENT_TYPE)
             .setMetadata(metadata)
             .build();
@@ -681,11 +782,13 @@ public class ITObjectTest {
   @Test
   public void testUpdateBlobFail() {
     String blobName = "test-update-blob-fail";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     Blob remoteBlob = storage.create(blob);
     assertNotNull(remoteBlob);
     BlobInfo wrongGenerationBlob =
-        BlobInfo.newBuilder(BUCKET, blobName, -1L).setContentType(CONTENT_TYPE).build();
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName, -1L)
+            .setContentType(CONTENT_TYPE)
+            .build();
     try {
       storage.update(wrongGenerationBlob, Storage.BlobTargetOption.generationMatch());
       fail("StorageException was expected");
@@ -697,16 +800,17 @@ public class ITObjectTest {
   @Test
   public void testDeleteNonExistingBlob() {
     String blobName = "test-delete-non-existing-blob";
-    assertFalse(storage.delete(BUCKET, blobName));
+    assertFalse(storage.delete(bucketFixture.getBucketInfo().getName(), blobName));
   }
 
   @Test
   public void testDeleteBlobNonExistingGeneration() {
     String blobName = "test-delete-blob-non-existing-generation";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     assertNotNull(storage.create(blob));
     try {
-      assertFalse(storage.delete(BlobId.of(BUCKET, blobName, -1L)));
+      assertFalse(
+          storage.delete(BlobId.of(bucketFixture.getBucketInfo().getName(), blobName, -1L)));
       fail("Expected an 'Invalid argument' exception");
     } catch (StorageException e) {
       assertThat(e.getMessage()).contains("Invalid argument");
@@ -716,11 +820,14 @@ public class ITObjectTest {
   @Test
   public void testDeleteBlobFail() {
     String blobName = "test-delete-blob-fail";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     Blob remoteBlob = storage.create(blob);
     assertNotNull(remoteBlob);
     try {
-      storage.delete(BUCKET, blob.getName(), Storage.BlobSourceOption.generationMatch(-1L));
+      storage.delete(
+          bucketFixture.getBucketInfo().getName(),
+          blob.getName(),
+          Storage.BlobSourceOption.generationMatch(-1L));
       fail("StorageException was expected");
     } catch (StorageException ex) {
       // expected
@@ -732,22 +839,25 @@ public class ITObjectTest {
   public void testComposeBlob() {
     String sourceBlobName1 = "test-compose-blob-source-1";
     String sourceBlobName2 = "test-compose-blob-source-2";
-    BlobInfo sourceBlob1 = BlobInfo.newBuilder(BUCKET, sourceBlobName1).build();
-    BlobInfo sourceBlob2 = BlobInfo.newBuilder(BUCKET, sourceBlobName2).build();
+    BlobInfo sourceBlob1 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName1).build();
+    BlobInfo sourceBlob2 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName2).build();
     Blob remoteSourceBlob1 = storage.create(sourceBlob1, BLOB_BYTE_CONTENT);
     Blob remoteSourceBlob2 = storage.create(sourceBlob2, BLOB_BYTE_CONTENT);
     assertNotNull(remoteSourceBlob1);
     assertNotNull(remoteSourceBlob2);
     String targetBlobName = "test-compose-blob-target";
-    BlobInfo targetBlob = BlobInfo.newBuilder(BUCKET, targetBlobName).build();
+    BlobInfo targetBlob =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), targetBlobName).build();
     Storage.ComposeRequest req =
         Storage.ComposeRequest.of(ImmutableList.of(sourceBlobName1, sourceBlobName2), targetBlob);
     Blob remoteTargetBlob = storage.compose(req);
     assertNotNull(remoteTargetBlob);
     assertEquals(targetBlob.getName(), remoteTargetBlob.getName());
     assertEquals(targetBlob.getBucket(), remoteTargetBlob.getBucket());
-    assertNull(remoteTargetBlob.getContentType());
-    byte[] readBytes = storage.readAllBytes(BUCKET, targetBlobName);
+    byte[] readBytes =
+        storage.readAllBytes(bucketFixture.getBucketInfo().getName(), targetBlobName);
     byte[] composedBytes = Arrays.copyOf(BLOB_BYTE_CONTENT, BLOB_BYTE_CONTENT.length * 2);
     System.arraycopy(
         BLOB_BYTE_CONTENT, 0, composedBytes, BLOB_BYTE_CONTENT.length, BLOB_BYTE_CONTENT.length);
@@ -758,15 +868,19 @@ public class ITObjectTest {
   public void testComposeBlobWithContentType() {
     String sourceBlobName1 = "test-compose-blob-with-content-type-source-1";
     String sourceBlobName2 = "test-compose-blob-with-content-type-source-2";
-    BlobInfo sourceBlob1 = BlobInfo.newBuilder(BUCKET, sourceBlobName1).build();
-    BlobInfo sourceBlob2 = BlobInfo.newBuilder(BUCKET, sourceBlobName2).build();
+    BlobInfo sourceBlob1 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName1).build();
+    BlobInfo sourceBlob2 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName2).build();
     Blob remoteSourceBlob1 = storage.create(sourceBlob1, BLOB_BYTE_CONTENT);
     Blob remoteSourceBlob2 = storage.create(sourceBlob2, BLOB_BYTE_CONTENT);
     assertNotNull(remoteSourceBlob1);
     assertNotNull(remoteSourceBlob2);
     String targetBlobName = "test-compose-blob-with-content-type-target";
     BlobInfo targetBlob =
-        BlobInfo.newBuilder(BUCKET, targetBlobName).setContentType(CONTENT_TYPE).build();
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), targetBlobName)
+            .setContentType(CONTENT_TYPE)
+            .build();
     Storage.ComposeRequest req =
         Storage.ComposeRequest.of(ImmutableList.of(sourceBlobName1, sourceBlobName2), targetBlob);
     Blob remoteTargetBlob = storage.compose(req);
@@ -774,7 +888,8 @@ public class ITObjectTest {
     assertEquals(targetBlob.getName(), remoteTargetBlob.getName());
     assertEquals(targetBlob.getBucket(), remoteTargetBlob.getBucket());
     assertEquals(CONTENT_TYPE, remoteTargetBlob.getContentType());
-    byte[] readBytes = storage.readAllBytes(BUCKET, targetBlobName);
+    byte[] readBytes =
+        storage.readAllBytes(bucketFixture.getBucketInfo().getName(), targetBlobName);
     byte[] composedBytes = Arrays.copyOf(BLOB_BYTE_CONTENT, BLOB_BYTE_CONTENT.length * 2);
     System.arraycopy(
         BLOB_BYTE_CONTENT, 0, composedBytes, BLOB_BYTE_CONTENT.length, BLOB_BYTE_CONTENT.length);
@@ -785,14 +900,17 @@ public class ITObjectTest {
   public void testComposeBlobFail() {
     String sourceBlobName1 = "test-compose-blob-fail-source-1";
     String sourceBlobName2 = "test-compose-blob-fail-source-2";
-    BlobInfo sourceBlob1 = BlobInfo.newBuilder(BUCKET, sourceBlobName1).build();
-    BlobInfo sourceBlob2 = BlobInfo.newBuilder(BUCKET, sourceBlobName2).build();
+    BlobInfo sourceBlob1 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName1).build();
+    BlobInfo sourceBlob2 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName2).build();
     Blob remoteSourceBlob1 = storage.create(sourceBlob1);
     Blob remoteSourceBlob2 = storage.create(sourceBlob2);
     assertNotNull(remoteSourceBlob1);
     assertNotNull(remoteSourceBlob2);
     String targetBlobName = "test-compose-blob-fail-target";
-    BlobInfo targetBlob = BlobInfo.newBuilder(BUCKET, targetBlobName).build();
+    BlobInfo targetBlob =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), targetBlobName).build();
     Storage.ComposeRequest req =
         Storage.ComposeRequest.newBuilder()
             .addSource(sourceBlobName1, -1L)
@@ -809,29 +927,37 @@ public class ITObjectTest {
 
   @Test
   public void testCopyBlob() {
+    // Bucket attribute extration on allowlist bug b/246634709
+    assumeTrue(clientName.startsWith("JSON"));
+
     String sourceBlobName = "test-copy-blob-source";
-    BlobId source = BlobId.of(BUCKET, sourceBlobName);
+    BlobId source = BlobId.of(bucketFixture.getBucketInfo().getName(), sourceBlobName);
     ImmutableMap<String, String> metadata = ImmutableMap.of("k", "v");
     BlobInfo blob =
         BlobInfo.newBuilder(source).setContentType(CONTENT_TYPE).setMetadata(metadata).build();
     Blob remoteBlob = storage.create(blob, BLOB_BYTE_CONTENT);
     assertNotNull(remoteBlob);
     String targetBlobName = "test-copy-blob-target";
-    Storage.CopyRequest req = Storage.CopyRequest.of(source, BlobId.of(BUCKET, targetBlobName));
+    Storage.CopyRequest req =
+        Storage.CopyRequest.of(
+            source, BlobId.of(bucketFixture.getBucketInfo().getName(), targetBlobName));
     CopyWriter copyWriter = storage.copy(req);
-    assertEquals(BUCKET, copyWriter.getResult().getBucket());
+    assertEquals(bucketFixture.getBucketInfo().getName(), copyWriter.getResult().getBucket());
     assertEquals(targetBlobName, copyWriter.getResult().getName());
     assertEquals(CONTENT_TYPE, copyWriter.getResult().getContentType());
     assertEquals(metadata, copyWriter.getResult().getMetadata());
     assertTrue(copyWriter.isDone());
     assertTrue(remoteBlob.delete());
-    assertTrue(storage.delete(BUCKET, targetBlobName));
+    assertTrue(storage.delete(bucketFixture.getBucketInfo().getName(), targetBlobName));
   }
 
   @Test
   public void testCopyBlobWithPredefinedAcl() {
+    // Bucket attribute extration on allowlist bug b/246634709
+    assumeTrue(clientName.startsWith("JSON"));
+
     String sourceBlobName = "test-copy-blob-source";
-    BlobId source = BlobId.of(BUCKET, sourceBlobName);
+    BlobId source = BlobId.of(bucketFixture.getBucketInfo().getName(), sourceBlobName);
     ImmutableMap<String, String> metadata = ImmutableMap.of("k", "v");
     BlobInfo blob =
         BlobInfo.newBuilder(source).setContentType(CONTENT_TYPE).setMetadata(metadata).build();
@@ -842,24 +968,27 @@ public class ITObjectTest {
         Storage.CopyRequest.newBuilder()
             .setSource(source)
             .setTarget(
-                BlobId.of(BUCKET, targetBlobName),
+                BlobId.of(bucketFixture.getBucketInfo().getName(), targetBlobName),
                 Storage.BlobTargetOption.predefinedAcl(Storage.PredefinedAcl.PUBLIC_READ))
             .build();
     CopyWriter copyWriter = storage.copy(req);
-    assertEquals(BUCKET, copyWriter.getResult().getBucket());
+    assertEquals(bucketFixture.getBucketInfo().getName(), copyWriter.getResult().getBucket());
     assertEquals(targetBlobName, copyWriter.getResult().getName());
     assertEquals(CONTENT_TYPE, copyWriter.getResult().getContentType());
     assertEquals(metadata, copyWriter.getResult().getMetadata());
     assertNotNull(copyWriter.getResult().getAcl(User.ofAllUsers()));
     assertTrue(copyWriter.isDone());
     assertTrue(remoteBlob.delete());
-    assertTrue(storage.delete(BUCKET, targetBlobName));
+    assertTrue(storage.delete(bucketFixture.getBucketInfo().getName(), targetBlobName));
   }
 
   @Test
   public void testCopyBlobWithEncryptionKeys() {
+    // Bucket attribute extration on allowlist bug b/246634709
+    assumeTrue(clientName.startsWith("JSON"));
+
     String sourceBlobName = "test-copy-blob-encryption-key-source";
-    BlobId source = BlobId.of(BUCKET, sourceBlobName);
+    BlobId source = BlobId.of(bucketFixture.getBucketInfo().getName(), sourceBlobName);
     ImmutableMap<String, String> metadata = ImmutableMap.of("k", "v");
     Blob remoteBlob =
         storage.create(
@@ -869,7 +998,7 @@ public class ITObjectTest {
     assertNotNull(remoteBlob);
     String targetBlobName = "test-copy-blob-encryption-key-target";
     BlobInfo target =
-        BlobInfo.newBuilder(BUCKET, targetBlobName)
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), targetBlobName)
             .setContentType(CONTENT_TYPE)
             .setMetadata(metadata)
             .build();
@@ -880,7 +1009,7 @@ public class ITObjectTest {
             .setSourceOptions(Storage.BlobSourceOption.decryptionKey(BASE64_KEY))
             .build();
     CopyWriter copyWriter = storage.copy(req);
-    assertEquals(BUCKET, copyWriter.getResult().getBucket());
+    assertEquals(bucketFixture.getBucketInfo().getName(), copyWriter.getResult().getBucket());
     assertEquals(targetBlobName, copyWriter.getResult().getName());
     assertEquals(CONTENT_TYPE, copyWriter.getResult().getContentType());
     assertArrayEquals(
@@ -895,45 +1024,48 @@ public class ITObjectTest {
             .setSourceOptions(Storage.BlobSourceOption.decryptionKey(BASE64_KEY))
             .build();
     copyWriter = storage.copy(req);
-    assertEquals(BUCKET, copyWriter.getResult().getBucket());
+    assertEquals(bucketFixture.getBucketInfo().getName(), copyWriter.getResult().getBucket());
     assertEquals(targetBlobName, copyWriter.getResult().getName());
     assertEquals(CONTENT_TYPE, copyWriter.getResult().getContentType());
     assertArrayEquals(BLOB_BYTE_CONTENT, copyWriter.getResult().getContent());
     assertEquals(metadata, copyWriter.getResult().getMetadata());
     assertTrue(copyWriter.isDone());
     assertTrue(remoteBlob.delete());
-    assertTrue(storage.delete(BUCKET, targetBlobName));
+    assertTrue(storage.delete(bucketFixture.getBucketInfo().getName(), targetBlobName));
   }
 
   @Test
   public void testCopyBlobUpdateMetadata() {
+    // Bucket attribute extration on allowlist bug b/246634709
+    assumeTrue(clientName.startsWith("JSON"));
+
     String sourceBlobName = "test-copy-blob-update-metadata-source";
-    BlobId source = BlobId.of(BUCKET, sourceBlobName);
+    BlobId source = BlobId.of(bucketFixture.getBucketInfo().getName(), sourceBlobName);
     Blob remoteSourceBlob = storage.create(BlobInfo.newBuilder(source).build(), BLOB_BYTE_CONTENT);
     assertNotNull(remoteSourceBlob);
     String targetBlobName = "test-copy-blob-update-metadata-target";
     ImmutableMap<String, String> metadata = ImmutableMap.of("k", "v");
     BlobInfo target =
-        BlobInfo.newBuilder(BUCKET, targetBlobName)
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), targetBlobName)
             .setContentType(CONTENT_TYPE)
             .setMetadata(metadata)
             .build();
     Storage.CopyRequest req = Storage.CopyRequest.of(source, target);
     CopyWriter copyWriter = storage.copy(req);
-    assertEquals(BUCKET, copyWriter.getResult().getBucket());
+    assertEquals(bucketFixture.getBucketInfo().getName(), copyWriter.getResult().getBucket());
     assertEquals(targetBlobName, copyWriter.getResult().getName());
     assertEquals(CONTENT_TYPE, copyWriter.getResult().getContentType());
     assertEquals(metadata, copyWriter.getResult().getMetadata());
     assertTrue(copyWriter.isDone());
     assertTrue(remoteSourceBlob.delete());
-    assertTrue(storage.delete(BUCKET, targetBlobName));
+    assertTrue(storage.delete(bucketFixture.getBucketInfo().getName(), targetBlobName));
   }
 
   // Re-enable this test when it stops failing
   // @Test
   public void testCopyBlobUpdateStorageClass() {
     String sourceBlobName = "test-copy-blob-update-storage-class-source";
-    BlobId source = BlobId.of(BUCKET, sourceBlobName);
+    BlobId source = BlobId.of(bucketFixture.getBucketInfo().getName(), sourceBlobName);
     BlobInfo sourceInfo =
         BlobInfo.newBuilder(source).setStorageClass(StorageClass.STANDARD).build();
     Blob remoteSourceBlob = storage.create(sourceInfo, BLOB_BYTE_CONTENT);
@@ -942,49 +1074,63 @@ public class ITObjectTest {
 
     String targetBlobName = "test-copy-blob-update-storage-class-target";
     BlobInfo targetInfo =
-        BlobInfo.newBuilder(BUCKET, targetBlobName).setStorageClass(StorageClass.COLDLINE).build();
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), targetBlobName)
+            .setStorageClass(StorageClass.COLDLINE)
+            .build();
     Storage.CopyRequest req = Storage.CopyRequest.of(source, targetInfo);
     CopyWriter copyWriter = storage.copy(req);
-    assertEquals(BUCKET, copyWriter.getResult().getBucket());
+    assertEquals(bucketFixture.getBucketInfo().getName(), copyWriter.getResult().getBucket());
     assertEquals(targetBlobName, copyWriter.getResult().getName());
     assertEquals(StorageClass.COLDLINE, copyWriter.getResult().getStorageClass());
     assertTrue(copyWriter.isDone());
     assertTrue(remoteSourceBlob.delete());
-    assertTrue(storage.delete(BUCKET, targetBlobName));
+    assertTrue(storage.delete(bucketFixture.getBucketInfo().getName(), targetBlobName));
   }
 
   @Test
   public void testCopyBlobNoContentType() {
+    // Bucket attribute extration on allowlist bug b/246634709
+    assumeTrue(clientName.startsWith("JSON"));
+
     String sourceBlobName = "test-copy-blob-no-content-type-source";
-    BlobId source = BlobId.of(BUCKET, sourceBlobName);
+    BlobId source = BlobId.of(bucketFixture.getBucketInfo().getName(), sourceBlobName);
     Blob remoteSourceBlob = storage.create(BlobInfo.newBuilder(source).build(), BLOB_BYTE_CONTENT);
     assertNotNull(remoteSourceBlob);
     String targetBlobName = "test-copy-blob-no-content-type-target";
     ImmutableMap<String, String> metadata = ImmutableMap.of("k", "v");
-    BlobInfo target = BlobInfo.newBuilder(BUCKET, targetBlobName).setMetadata(metadata).build();
+    BlobInfo target =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), targetBlobName)
+            .setMetadata(metadata)
+            .build();
     Storage.CopyRequest req = Storage.CopyRequest.of(source, target);
     CopyWriter copyWriter = storage.copy(req);
-    assertEquals(BUCKET, copyWriter.getResult().getBucket());
+    assertEquals(bucketFixture.getBucketInfo().getName(), copyWriter.getResult().getBucket());
     assertEquals(targetBlobName, copyWriter.getResult().getName());
     assertNull(copyWriter.getResult().getContentType());
     assertEquals(metadata, copyWriter.getResult().getMetadata());
     assertTrue(copyWriter.isDone());
     assertTrue(remoteSourceBlob.delete());
-    assertTrue(storage.delete(BUCKET, targetBlobName));
+    assertTrue(storage.delete(bucketFixture.getBucketInfo().getName(), targetBlobName));
   }
 
   @Test
   public void testCopyBlobFail() {
+    // Verified against testbench
+    // Bucket attribute extration on allowlist bug b/246634709
+    assumeTrue(clientName.startsWith("JSON"));
+
     String sourceBlobName = "test-copy-blob-source-fail";
-    BlobId source = BlobId.of(BUCKET, sourceBlobName, -1L);
+    BlobId source = BlobId.of(bucketFixture.getBucketInfo().getName(), sourceBlobName, -1L);
     Blob remoteSourceBlob = storage.create(BlobInfo.newBuilder(source).build(), BLOB_BYTE_CONTENT);
     assertNotNull(remoteSourceBlob);
     String targetBlobName = "test-copy-blob-target-fail";
     BlobInfo target =
-        BlobInfo.newBuilder(BUCKET, targetBlobName).setContentType(CONTENT_TYPE).build();
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), targetBlobName)
+            .setContentType(CONTENT_TYPE)
+            .build();
     Storage.CopyRequest req =
         Storage.CopyRequest.newBuilder()
-            .setSource(BUCKET, sourceBlobName)
+            .setSource(bucketFixture.getBucketInfo().getName(), sourceBlobName)
             .setSourceOptions(Storage.BlobSourceOption.generationMatch(-1L))
             .setTarget(target)
             .build();
@@ -1011,7 +1157,7 @@ public class ITObjectTest {
   @Test
   public void testReadAndWriteChannelWithEncryptionKey() throws IOException {
     String blobName = "test-read-write-channel-with-customer-key-blob";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     byte[] stringBytes;
     try (WriteChannel writer =
         storage.writer(blob, Storage.BlobWriteOption.encryptionKey(BASE64_KEY))) {
@@ -1030,7 +1176,7 @@ public class ITObjectTest {
     }
     assertArrayEquals(BLOB_BYTE_CONTENT, readBytes.array());
     assertEquals(BLOB_STRING_CONTENT, new String(readStringBytes.array(), UTF_8));
-    assertTrue(storage.delete(BUCKET, blobName));
+    assertTrue(storage.delete(bucketFixture.getBucketInfo().getName(), blobName));
   }
 
   @Test
@@ -1040,7 +1186,7 @@ public class ITObjectTest {
     Random rnd = new Random();
     for (int blobSize : blobSizes) {
       String blobName = blobNamePrefix + blobSize;
-      BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+      BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
       byte[] bytes = new byte[blobSize];
       rnd.nextBytes(bytes);
       try (WriteChannel writer = storage.writer(blob)) {
@@ -1056,14 +1202,17 @@ public class ITObjectTest {
         }
       }
       assertArrayEquals(bytes, output.toByteArray());
-      assertTrue(storage.delete(BUCKET, blobName));
+      assertTrue(storage.delete(bucketFixture.getBucketInfo().getName(), blobName));
     }
   }
 
   @Test
   public void testReadAndWriteCaptureChannels() throws IOException {
+    // Capture not implemented yet
+    assumeTrue(clientName.startsWith("JSON"));
+
     String blobName = "test-read-and-write-capture-channels-blob";
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).build();
+    BlobInfo blob = BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).build();
     byte[] stringBytes;
     WriteChannel writer = storage.writer(blob);
     stringBytes = BLOB_STRING_CONTENT.getBytes(UTF_8);
@@ -1086,15 +1235,19 @@ public class ITObjectTest {
     secondReader.close();
     assertArrayEquals(BLOB_BYTE_CONTENT, readBytes.array());
     assertEquals(BLOB_STRING_CONTENT, new String(readStringBytes.array(), UTF_8));
-    assertTrue(storage.delete(BUCKET, blobName));
+    assertTrue(storage.delete(bucketFixture.getBucketInfo().getName(), blobName));
   }
 
   @Test
   public void testGetBlobs() {
+    // Only supported in JSON right now
+    assumeTrue(clientName.startsWith("JSON"));
     String sourceBlobName1 = "test-get-blobs-1";
     String sourceBlobName2 = "test-get-blobs-2";
-    BlobInfo sourceBlob1 = BlobInfo.newBuilder(BUCKET, sourceBlobName1).build();
-    BlobInfo sourceBlob2 = BlobInfo.newBuilder(BUCKET, sourceBlobName2).build();
+    BlobInfo sourceBlob1 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName1).build();
+    BlobInfo sourceBlob2 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName2).build();
     assertNotNull(storage.create(sourceBlob1));
     assertNotNull(storage.create(sourceBlob2));
     List<Blob> remoteBlobs = storage.get(sourceBlob1.getBlobId(), sourceBlob2.getBlobId());
@@ -1106,10 +1259,15 @@ public class ITObjectTest {
 
   @Test
   public void testGetBlobsFail() {
+    // Only supported in JSON right now
+    assumeTrue(clientName.startsWith("JSON"));
+
     String sourceBlobName1 = "test-get-blobs-fail-1";
     String sourceBlobName2 = "test-get-blobs-fail-2";
-    BlobInfo sourceBlob1 = BlobInfo.newBuilder(BUCKET, sourceBlobName1).build();
-    BlobInfo sourceBlob2 = BlobInfo.newBuilder(BUCKET, sourceBlobName2).build();
+    BlobInfo sourceBlob1 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName1).build();
+    BlobInfo sourceBlob2 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName2).build();
     assertNotNull(storage.create(sourceBlob1));
     List<Blob> remoteBlobs = storage.get(sourceBlob1.getBlobId(), sourceBlob2.getBlobId());
     assertEquals(sourceBlob1.getBucket(), remoteBlobs.get(0).getBucket());
@@ -1119,10 +1277,15 @@ public class ITObjectTest {
 
   @Test
   public void testDeleteBlobs() {
+    // Only supported in JSON right now
+    assumeTrue(clientName.startsWith("JSON"));
+
     String sourceBlobName1 = "test-delete-blobs-1";
     String sourceBlobName2 = "test-delete-blobs-2";
-    BlobInfo sourceBlob1 = BlobInfo.newBuilder(BUCKET, sourceBlobName1).build();
-    BlobInfo sourceBlob2 = BlobInfo.newBuilder(BUCKET, sourceBlobName2).build();
+    BlobInfo sourceBlob1 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName1).build();
+    BlobInfo sourceBlob2 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName2).build();
     assertNotNull(storage.create(sourceBlob1));
     assertNotNull(storage.create(sourceBlob2));
     List<Boolean> deleteStatus = storage.delete(sourceBlob1.getBlobId(), sourceBlob2.getBlobId());
@@ -1132,22 +1295,41 @@ public class ITObjectTest {
 
   @Test
   public void testDeleteBlobsFail() {
+    // Only supported in JSON right now
+    assumeTrue(clientName.startsWith("JSON"));
     String sourceBlobName1 = "test-delete-blobs-fail-1";
     String sourceBlobName2 = "test-delete-blobs-fail-2";
-    BlobInfo sourceBlob1 = BlobInfo.newBuilder(BUCKET, sourceBlobName1).build();
-    BlobInfo sourceBlob2 = BlobInfo.newBuilder(BUCKET, sourceBlobName2).build();
+    BlobInfo sourceBlob1 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName1).build();
+    BlobInfo sourceBlob2 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName2).build();
     assertNotNull(storage.create(sourceBlob1));
     List<Boolean> deleteStatus = storage.delete(sourceBlob1.getBlobId(), sourceBlob2.getBlobId());
     assertTrue(deleteStatus.get(0));
     assertFalse(deleteStatus.get(1));
   }
 
+  @Test
+  public void testDeleteBlob() {
+    String sourceBlobName = "test-delete-one-success";
+    BlobInfo sourceBlob =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName).build();
+    assertNotNull(storage.create(sourceBlob));
+    boolean result = storage.delete(sourceBlob.getBlobId());
+    assertTrue(result);
+  }
+
   @Test
   public void testUpdateBlobs() {
+    // Only supported in JSON right now
+    assumeTrue(clientName.startsWith("JSON"));
+
     String sourceBlobName1 = "test-update-blobs-1";
     String sourceBlobName2 = "test-update-blobs-2";
-    BlobInfo sourceBlob1 = BlobInfo.newBuilder(BUCKET, sourceBlobName1).build();
-    BlobInfo sourceBlob2 = BlobInfo.newBuilder(BUCKET, sourceBlobName2).build();
+    BlobInfo sourceBlob1 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName1).build();
+    BlobInfo sourceBlob2 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName2).build();
     Blob remoteBlob1 = storage.create(sourceBlob1);
     Blob remoteBlob2 = storage.create(sourceBlob2);
     assertNotNull(remoteBlob1);
@@ -1166,10 +1348,15 @@ public class ITObjectTest {
 
   @Test
   public void testUpdateBlobsFail() {
+    // Only supported in JSON right now
+    assumeTrue(clientName.startsWith("JSON"));
+
     String sourceBlobName1 = "test-update-blobs-fail-1";
     String sourceBlobName2 = "test-update-blobs-fail-2";
-    BlobInfo sourceBlob1 = BlobInfo.newBuilder(BUCKET, sourceBlobName1).build();
-    BlobInfo sourceBlob2 = BlobInfo.newBuilder(BUCKET, sourceBlobName2).build();
+    BlobInfo sourceBlob1 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName1).build();
+    BlobInfo sourceBlob2 =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), sourceBlobName2).build();
     BlobInfo remoteBlob1 = storage.create(sourceBlob1);
     assertNotNull(remoteBlob1);
     List<Blob> updatedBlobs =
@@ -1185,10 +1372,11 @@ public class ITObjectTest {
   @Test
   public void testAttemptObjectDeleteWithRetentionPolicy()
       throws ExecutionException, InterruptedException {
-    String bucketName = RemoteStorageHelper.generateBucketName();
+    String bucketName = bucketFixture.newBucketName();
     Bucket remoteBucket =
-        storage.create(
-            BucketInfo.newBuilder(bucketName).setRetentionPeriod(RETENTION_PERIOD).build());
+        storageFixtureHttp
+            .getInstance()
+            .create(BucketInfo.newBuilder(bucketName).setRetentionPeriod(RETENTION_PERIOD).build());
     assertEquals(RETENTION_PERIOD, remoteBucket.getRetentionPeriod());
     String blobName = "test-create-with-retention-policy";
     BlobInfo blobInfo = BlobInfo.newBuilder(bucketName, blobName).build();
@@ -1201,14 +1389,16 @@ public class ITObjectTest {
       // expected
     } finally {
       Thread.sleep(RETENTION_PERIOD_IN_MILLISECONDS);
-      RemoteStorageHelper.forceDelete(storage, bucketName, 5, TimeUnit.SECONDS);
+      RemoteStorageHelper.forceDelete(
+          storageFixtureHttp.getInstance(), bucketName, 5, TimeUnit.SECONDS);
     }
   }
 
   @Test
   public void testEnableDisableTemporaryHold() {
     String blobName = "test-create-with-temporary-hold";
-    BlobInfo blobInfo = BlobInfo.newBuilder(BUCKET, blobName).setTemporaryHold(true).build();
+    BlobInfo blobInfo =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).setTemporaryHold(true).build();
     Blob remoteBlob = storage.create(blobInfo);
     assertTrue(remoteBlob.getTemporaryHold());
     remoteBlob =
@@ -1221,7 +1411,10 @@ public class ITObjectTest {
   @Test
   public void testAttemptObjectDeleteWithEventBasedHold() {
     String blobName = "test-create-with-event-based-hold";
-    BlobInfo blobInfo = BlobInfo.newBuilder(BUCKET, blobName).setEventBasedHold(true).build();
+    BlobInfo blobInfo =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
+            .setEventBasedHold(true)
+            .build();
     Blob remoteBlob = storage.create(blobInfo);
     assertTrue(remoteBlob.getEventBasedHold());
     try {
@@ -1237,7 +1430,8 @@ public class ITObjectTest {
   @Test
   public void testAttemptDeletionObjectTemporaryHold() {
     String blobName = "test-create-with-temporary-hold";
-    BlobInfo blobInfo = BlobInfo.newBuilder(BUCKET, blobName).setTemporaryHold(true).build();
+    BlobInfo blobInfo =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName).setTemporaryHold(true).build();
     Blob remoteBlob = storage.create(blobInfo);
     assertTrue(remoteBlob.getTemporaryHold());
     try {
@@ -1252,8 +1446,12 @@ public class ITObjectTest {
 
   @Test
   public void testBlobReload() throws Exception {
+    // GRPC Error Handling bug
+    // b/247621346
+    assumeTrue(clientName.startsWith("JSON"));
+
     String blobName = "test-blob-reload";
-    BlobId blobId = BlobId.of(BUCKET, blobName);
+    BlobId blobId = BlobId.of(bucketFixture.getBucketInfo().getName(), blobName);
     BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build();
     Blob blob = storage.create(blobInfo, new byte[] {0, 1, 2});
 
@@ -1282,7 +1480,7 @@ public class ITObjectTest {
   @Test
   public void testUploadWithEncryption() throws Exception {
     String blobName = "test-upload-withEncryption";
-    BlobId blobId = BlobId.of(BUCKET, blobName);
+    BlobId blobId = BlobId.of(bucketFixture.getBucketInfo().getName(), blobName);
     BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build();
 
     ByteArrayInputStream content = new ByteArrayInputStream(BLOB_BYTE_CONTENT);
@@ -1328,16 +1526,16 @@ public class ITObjectTest {
     String[] names = {"file1.txt", "dir with spaces/Pic.Jpg", "no_extension"};
     String[] types = {"text/plain", "image/jpeg", "application/octet-stream"};
     for (int i = 0; i < names.length; i++) {
-      BlobId blobId = BlobId.of(BUCKET, names[i]);
+      BlobId blobId = BlobId.of(bucketFixture.getBucketInfo().getName(), names[i]);
       BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build();
       Blob blob_true = createBlob(method, blobInfo, true);
       assertEquals(types[i], blob_true.getContentType());
 
       Blob blob_false = createBlob(method, blobInfo, false);
-      assertEquals("application/octet-stream", blob_false.getContentType());
+      assertThat(blob_false.getContentType()).isAnyOf("application/octet-stream", "");
     }
     String customType = "custom/type";
-    BlobId blobId = BlobId.of(BUCKET, names[0]);
+    BlobId blobId = BlobId.of(bucketFixture.getBucketInfo().getName(), names[0]);
     BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType(customType).build();
     Blob blob = createBlob(method, blobInfo, true);
     assertEquals(customType, blob.getContentType());
@@ -1355,17 +1553,26 @@ public class ITObjectTest {
 
   @Test
   public void testAutoContentTypeWriter() throws IOException {
+    // GRPC Write Channel bug
+    // b/248605626
+    assumeTrue(clientName.startsWith("JSON"));
     testAutoContentType("writer");
   }
 
   @Test
   public void testBlobTimeStorageClassUpdated() {
+    // Bucket attribute extration on allowlist bug b/246634709
+    assumeTrue(clientName.startsWith("JSON"));
+
     String blobName = "test-blob-with-storage-class";
     StorageClass storageClass = StorageClass.COLDLINE;
-    BlobInfo blob = BlobInfo.newBuilder(BUCKET, blobName).setStorageClass(storageClass).build();
+    BlobInfo blob =
+        BlobInfo.newBuilder(bucketFixture.getBucketInfo(), blobName)
+            .setStorageClass(storageClass)
+            .build();
     Blob remoteBlob = storage.create(blob);
     assertThat(remoteBlob).isNotNull();
-    assertThat(remoteBlob.getBucket()).isEqualTo(blob.getBucket());
+    assertEquals(blob.getBucket(), remoteBlob.getBucket());
     assertThat(remoteBlob.getName()).isEqualTo(blob.getName());
     assertThat(remoteBlob.getCreateTime()).isNotNull();
     assertThat(remoteBlob.getUpdateTime()).isEqualTo(remoteBlob.getCreateTime());
@@ -1373,7 +1580,7 @@ public class ITObjectTest {
 
     // We can't change an object's storage class directly, the only way is to rewrite the object
     // with the desired storage class.
-    BlobId blobId = BlobId.of(BUCKET, blobName);
+    BlobId blobId = BlobId.of(bucketFixture.getBucketInfo().getName(), blobName);
     Storage.CopyRequest request =
         Storage.CopyRequest.newBuilder()
             .setSource(blobId)