Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.FileNameMap;
import java.net.URI;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -158,11 +159,26 @@ public HttpStorageRpc(StorageOptions options, JsonFactory jsonFactory) {
initializer = censusHttpModule.getHttpRequestInitializer(initializer);
initializer = new InvocationIdInitializer(initializer, applicationName, tm);
batchRequestInitializer = censusHttpModule.getHttpRequestInitializer(null);
storage =
String host = options.getHost();
Storage.Builder storageBuilder =
new Storage.Builder(transport, jsonFactory, initializer)
.setRootUrl(options.getHost())
.setApplicationName(applicationName)
.build();
.setApplicationName(applicationName);
if (host != null) {
URI uri = URI.create(host);
String path = uri.getRawPath();
if (path != null && !path.isEmpty() && !"/".equals(path)) {
String rootUrl = host.substring(0, host.indexOf(path));
String servicePath = path.startsWith("/") ? path.substring(1) : path;
if (!servicePath.endsWith("/")) {
servicePath += "/";
}
storageBuilder.setRootUrl(rootUrl);
storageBuilder.setServicePath(servicePath);
} else {
storageBuilder.setRootUrl(host);
}
}
storage = storageBuilder.build();
}

public Storage getStorage() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@
import com.google.cloud.storage.TransportCompatibility.Transport;
import com.google.cloud.storage.it.runner.annotations.Backend;
import com.google.cloud.storage.it.runner.annotations.CrossRun;
import com.google.cloud.storage.it.runner.annotations.LocationType;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;
import java.util.Collections;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.ThreadSafe;
import org.checkerframework.checker.nullness.qual.Nullable;
Expand All @@ -39,10 +42,15 @@ public final class CrossRunIntersection {

private final @Nullable Backend backend;
private final @Nullable Transport transport;
private final @Nullable LocationType locationType;

private CrossRunIntersection(@Nullable Backend backend, @Nullable Transport transport) {
private CrossRunIntersection(
@Nullable Backend backend,
@Nullable Transport transport,
@Nullable LocationType locationType) {
this.backend = backend;
this.transport = transport;
this.locationType = locationType;
}

@Nullable
Expand All @@ -55,19 +63,32 @@ public Transport getTransport() {
return transport;
}

@Nullable
public LocationType getLocationType() {
return locationType;
}

public CrossRunIntersection clearBackend() {
if (backend == null) {
return this;
} else {
return new CrossRunIntersection(null, transport);
return new CrossRunIntersection(null, transport, locationType);
}
}

public CrossRunIntersection clearTransport() {
if (transport == null) {
return this;
} else {
return new CrossRunIntersection(backend, null);
return new CrossRunIntersection(backend, null, locationType);
}
}

public CrossRunIntersection clearLocationType() {
if (locationType == null) {
return this;
} else {
return new CrossRunIntersection(backend, transport, null);
}
}

Expand All @@ -76,7 +97,7 @@ public CrossRunIntersection withBackend(Backend backend) {
if (this.backend == backend) {
return this;
} else {
return new CrossRunIntersection(backend, transport);
return new CrossRunIntersection(backend, transport, locationType);
}
}

Expand All @@ -85,7 +106,16 @@ public CrossRunIntersection withTransport(Transport transport) {
if (this.transport == transport) {
return this;
} else {
return new CrossRunIntersection(backend, transport);
return new CrossRunIntersection(backend, transport, locationType);
}
}

public CrossRunIntersection withLocationType(LocationType locationType) {
requireNonNull(locationType, "locationType must be non null");
if (this.locationType == locationType) {
return this;
} else {
return new CrossRunIntersection(backend, transport, locationType);
}
}

Expand All @@ -107,6 +137,13 @@ public boolean anyMatch(CrossRunIntersection other) {
l = l.clearTransport();
}

if (l.locationType == null) {
r = r.clearLocationType();
}
if (r.locationType == null) {
l = l.clearLocationType();
}

return l.equals(r);
}

Expand All @@ -119,6 +156,9 @@ public boolean anyMatch(CrossRunIntersection other) {
public String fmtSuiteName() {
String t = transport != null ? transport.toString() : "NULL_TRANSPORT";
String b = backend != null ? backend.toString() : "NULL_BACKEND";
if (locationType != null) {
return String.format(Locale.US, "[%s][%s][%s]", t, b, locationType);
}
return String.format(Locale.US, "[%s][%s]", t, b);
}

Expand All @@ -131,54 +171,69 @@ public boolean equals(Object o) {
return false;
}
CrossRunIntersection crossRunIntersection = (CrossRunIntersection) o;
return backend == crossRunIntersection.backend && transport == crossRunIntersection.transport;
return backend == crossRunIntersection.backend
&& transport == crossRunIntersection.transport
&& locationType == crossRunIntersection.locationType;
}

@Override
public int hashCode() {
return Objects.hash(backend, transport);
return Objects.hash(backend, transport, locationType);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("backend", backend)
.add("transport", transport)
.add("locationType", locationType)
.toString();
}

public static CrossRunIntersection of(@Nullable Backend t, @Nullable Transport s) {
return new CrossRunIntersection(t, s);
public static CrossRunIntersection of(@Nullable Backend b, @Nullable Transport t) {
return new CrossRunIntersection(b, t, null);
}

public static CrossRunIntersection of(
@Nullable Backend b, @Nullable Transport t, @Nullable LocationType lt) {
return new CrossRunIntersection(b, t, lt);
}

public static ImmutableSet<CrossRunIntersection> expand(CrossRun.Ignore i) {
ImmutableSet<Backend> backends = ImmutableSet.copyOf(i.backends());
ImmutableSet<Transport> transports = ImmutableSet.copyOf(i.transports());
return expand(backends, transports);
ImmutableSet<LocationType> locations = ImmutableSet.copyOf(i.locations());
return expand(backends, transports, locations);
}

public static ImmutableSet<CrossRunIntersection> expand(CrossRun.Exclude i) {
ImmutableSet<Backend> backends = ImmutableSet.copyOf(i.backends());
ImmutableSet<Transport> transports = ImmutableSet.copyOf(i.transports());
return expand(backends, transports);
ImmutableSet<LocationType> locations = ImmutableSet.copyOf(i.locations());
return expand(backends, transports, locations);
}

public static ImmutableSet<CrossRunIntersection> expand(
ImmutableSet<Backend> backends, ImmutableSet<@Nullable Transport> transports) {
if (backends.isEmpty() && transports.isEmpty()) {
ImmutableSet<Backend> backends,
ImmutableSet<@Nullable Transport> transports,
ImmutableSet<@Nullable LocationType> locations) {
if (backends.isEmpty() && transports.isEmpty() && locations.isEmpty()) {
return ImmutableSet.of();
} else if (!backends.isEmpty() && !transports.isEmpty()) {
return backends.stream()
.flatMap(t -> transports.stream().map(s -> new CrossRunIntersection(t, s)))
.collect(ImmutableSet.toImmutableSet());
} else if (!backends.isEmpty()) {
return backends.stream()
.map(t -> new CrossRunIntersection(t, null))
.collect(ImmutableSet.toImmutableSet());
} else {
return transports.stream()
.map(s -> new CrossRunIntersection(null, s))
.collect(ImmutableSet.toImmutableSet());
}

Set<@Nullable Backend> bSet =
backends.isEmpty() ? Collections.singleton((Backend) null) : backends;
Set<@Nullable Transport> tSet =
transports.isEmpty() ? Collections.singleton((Transport) null) : transports;
Set<@Nullable LocationType> lSet =
locations.isEmpty() ? Collections.singleton((LocationType) null) : locations;

return bSet.stream()
.flatMap(
b ->
tSet.stream()
.flatMap(t -> lSet.stream().map(l -> new CrossRunIntersection(b, t, l))))
.filter(i -> !(i.backend == null && i.transport == null && i.locationType == null))
.collect(ImmutableSet.toImmutableSet());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.cloud.storage.it.runner.annotations.Backend;
import com.google.cloud.storage.it.runner.annotations.CrossRun;
import com.google.cloud.storage.it.runner.annotations.CrossRun.AllowClassRule;
import com.google.cloud.storage.it.runner.annotations.LocationType;
import com.google.cloud.storage.it.runner.annotations.ParallelFriendly;
import com.google.cloud.storage.it.runner.annotations.Parameterized;
import com.google.cloud.storage.it.runner.annotations.Parameterized.Parameter;
Expand Down Expand Up @@ -166,7 +167,10 @@ private static List<Runner> computeRunners(Class<?> klass, Registry registry)
.flatMap(
b ->
ImmutableSet.copyOf(crossRun.transports()).stream()
.map(t -> CrossRunIntersection.of(b, t)))
.flatMap(
t ->
ImmutableSet.copyOf(crossRun.locations()).stream()
.map(l -> CrossRunIntersection.of(b, t, l))))
.flatMap(
c -> {
TestInitializer ti = registry.newTestInitializerForCell(c);
Expand All @@ -187,23 +191,33 @@ private static List<Runner> computeRunners(Class<?> klass, Registry registry)
.collect(ImmutableList.toImmutableList()));
} else {
Backend backend = singleBackend.value();
CrossRunIntersection crossRunIntersection = CrossRunIntersection.of(backend, null);
TestInitializer ti = registry.newTestInitializerForCell(crossRunIntersection);
if (parameters != null) {
return SneakyException.unwrap(
() ->
parameters.stream()
.map(
param ->
StorageITLeafRunner.unsafeOf(
testClass,
crossRunIntersection,
fmtParam(param),
ti.andThen(setFieldTo(testClass, param))))
.collect(ImmutableList.toImmutableList()));
} else {
return ImmutableList.of(StorageITLeafRunner.of(testClass, crossRunIntersection, null, ti));
}
boolean isDefault =
singleBackend.locations().length == 1
&& singleBackend.locations()[0] == LocationType.REGIONAL_STANDARD;

return SneakyException.unwrap(
() ->
ImmutableSet.copyOf(singleBackend.locations()).stream()
.map(l -> CrossRunIntersection.of(backend, null, l))
.flatMap(
c -> {
TestInitializer ti = registry.newTestInitializerForCell(c);
if (parameters != null) {
return parameters.stream()
.map(
param ->
StorageITLeafRunner.unsafeOf(
testClass,
c,
isDefault ? fmtParam(param) : fmtParam(c, param),
ti.andThen(setFieldTo(testClass, param))));
} else {
return Stream.of(
StorageITLeafRunner.unsafeOf(
testClass, c, isDefault ? null : c.fmtSuiteName(), ti));
}
})
.collect(ImmutableList.toImmutableList()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
public enum Backend {
/** Use the "Production" GCS endpoints */
PROD,
/** Use the GCS Pre-prod (Staging) endpoints */
PREPROD,
/** Use the test bench as a backend */
TEST_BENCH
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@

Transport[] transports();

LocationType[] locations() default {LocationType.REGIONAL_STANDARD};

/**
* Exclude a method from being included in the generated test suite if it's backend and transport
* match with those defined. When matching, if the empty set is defined as a value it will be
Expand All @@ -53,6 +55,8 @@
Backend[] backends() default {};

TransportCompatibility.Transport[] transports() default {};

LocationType[] locations() default {};
}

/**
Expand All @@ -69,6 +73,8 @@
Backend[] backends() default {};

TransportCompatibility.Transport[] transports() default {};

LocationType[] locations() default {};
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2026 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.it.runner.annotations;

/** An enum defining the bucket location and topology types used in integration testing. */
public enum LocationType {
/** Standard regional storage bucket */
REGIONAL_STANDARD,
/** Regional bucket with Rapid Cache enabled */
REGIONAL_RAPID,
/** Zonal bucket placed in a specific availability zone */
ZONAL_RAPID
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@
@Retention(RetentionPolicy.RUNTIME)
public @interface SingleBackend {
Backend value();

LocationType[] locations() default {LocationType.REGIONAL_STANDARD};
}
Loading
Loading