Skip to content

Commit 40880b2

Browse files
committed
test: add new StorageNativeCanaryTest
Update native-image related config to be specific for this class. Update maven config to explicitly configure this new class as its only class for test when run under -Pnative
1 parent 6928556 commit 40880b2

File tree

4 files changed

+209
-26
lines changed

4 files changed

+209
-26
lines changed

google-cloud-storage/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,5 +340,15 @@
340340
</dependency>
341341
</dependencies>
342342
</profile>
343+
<profile>
344+
<id>native</id>
345+
<properties>
346+
<!--
347+
Override the default match pattern for native image tests since we have a custom
348+
test specifically for native image validation.
349+
-->
350+
<test>com.google.cloud.storage.it.StorageNativeCanary</test>
351+
</properties>
352+
</profile>
343353
</profiles>
344354
</project>
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.storage.it;
18+
19+
import static com.google.cloud.storage.TestUtils.assertAll;
20+
import static com.google.common.truth.Truth.assertThat;
21+
22+
import com.google.api.gax.paging.Page;
23+
import com.google.cloud.ReadChannel;
24+
import com.google.cloud.WriteChannel;
25+
import com.google.cloud.storage.Blob;
26+
import com.google.cloud.storage.BlobId;
27+
import com.google.cloud.storage.BlobInfo;
28+
import com.google.cloud.storage.BucketInfo;
29+
import com.google.cloud.storage.DataGenerator;
30+
import com.google.cloud.storage.Storage;
31+
import com.google.cloud.storage.Storage.BlobListOption;
32+
import com.google.cloud.storage.Storage.BlobSourceOption;
33+
import com.google.cloud.storage.Storage.BlobWriteOption;
34+
import com.google.cloud.storage.TransportCompatibility.Transport;
35+
import com.google.cloud.storage.it.runner.StorageITRunner;
36+
import com.google.cloud.storage.it.runner.annotations.Backend;
37+
import com.google.cloud.storage.it.runner.annotations.CrossRun;
38+
import com.google.cloud.storage.it.runner.annotations.Inject;
39+
import com.google.cloud.storage.it.runner.registry.Generator;
40+
import com.google.common.collect.ImmutableList;
41+
import com.google.common.io.ByteStreams;
42+
import java.io.ByteArrayInputStream;
43+
import java.io.ByteArrayOutputStream;
44+
import java.io.IOException;
45+
import java.nio.channels.Channels;
46+
import java.nio.channels.WritableByteChannel;
47+
import java.util.Arrays;
48+
import java.util.List;
49+
import java.util.function.Predicate;
50+
import org.junit.Test;
51+
import org.junit.runner.RunWith;
52+
53+
@RunWith(StorageITRunner.class)
54+
@CrossRun(
55+
backends = {Backend.PROD},
56+
transports = {Transport.HTTP, Transport.GRPC})
57+
public final class StorageNativeCanary {
58+
59+
private static final int _512KiB = 512 * 1024;
60+
private static final int _256KiB = 256 * 1024;
61+
private static final byte[] bytes = DataGenerator.base64Characters().genBytes(_512KiB);
62+
63+
@Inject public Storage storage;
64+
@Inject public Generator generator;
65+
66+
/**
67+
* When testing on Native Image, we're primarily wanting to verify the primary code paths are
68+
* properly detected by the native image compiler.
69+
*
70+
* <p>For Storage, we have a few "primary code paths" we want to ensure are validated:
71+
*
72+
* <ul>
73+
* <li>Can a (Unary) Request Succeed?
74+
* <li>Can a (ServerStream) Object Read Request Succeed?
75+
* <li>Can a (ClientStream) Object Write Request Succeed?
76+
* <li>Can a (Page over Unary) Paginated Request Succeed?
77+
* </ul>
78+
*
79+
* To validate this, our happy path test is as follows:
80+
*
81+
* <ul>
82+
* <li>Create a temporary bucket (Unary)
83+
* <li>Insert two (2) objects (Unary, ServerStream)
84+
* <li>List all objects, using a pageSize of 1 (Page over Unary)
85+
* <li>Read all bytes of each object (ServerStream)
86+
* <li>Delete each object (Unary)
87+
* <li>Delete temporary bucket (Unary)
88+
* </ul>
89+
*/
90+
@Test
91+
public void canary_happyPath() throws Exception {
92+
// create a temporary bucket
93+
try (TemporaryBucket tempB =
94+
TemporaryBucket.newBuilder()
95+
.setStorage(storage)
96+
.setBucketInfo(BucketInfo.of(generator.randomBucketName()))
97+
.build()) {
98+
String bucketName = tempB.getBucket().getName();
99+
100+
// insert 2 objects
101+
BlobInfo info1 = BlobInfo.newBuilder(bucketName, generator.randomObjectName()).build();
102+
BlobInfo info2 = BlobInfo.newBuilder(bucketName, generator.randomObjectName()).build();
103+
uploadUsingWriter(info1);
104+
uploadUsingWriter(info2);
105+
106+
// list objects
107+
Page<Blob> page = storage.list(bucketName, BlobListOption.pageSize(1));
108+
List<Blob> blobs = ImmutableList.copyOf(page.iterateAll());
109+
110+
// read all bytes of each object
111+
List<BlobWithContent> actual =
112+
blobs.stream().map(this::readAll).collect(ImmutableList.toImmutableList());
113+
114+
List<Boolean> deletes =
115+
blobs.stream()
116+
.map(b -> storage.delete(b.getBlobId(), BlobSourceOption.generationMatch()))
117+
.collect(ImmutableList.toImmutableList());
118+
119+
assertAll(
120+
() -> {
121+
List<String> actualNames =
122+
actual.stream()
123+
.map(BlobWithContent::getInfo)
124+
.map(BlobInfo::getBlobId)
125+
.map(BlobId::getName)
126+
.collect(ImmutableList.toImmutableList());
127+
128+
assertThat(actualNames).isEqualTo(ImmutableList.of(info1.getName(), info2.getName()));
129+
},
130+
() -> {
131+
// if the content isn't equal carry it through
132+
List<BlobWithContent> contentNotEquals =
133+
actual.stream()
134+
.filter(bwc -> !Arrays.equals(bwc.getContent(), bytes))
135+
.collect(ImmutableList.toImmutableList());
136+
assertThat(contentNotEquals).isEmpty();
137+
},
138+
() -> assertThat(deletes.stream().anyMatch(isFalse())).isFalse());
139+
}
140+
}
141+
142+
private void uploadUsingWriter(BlobInfo info) throws IOException {
143+
try (WriteChannel w = storage.writer(info, BlobWriteOption.doesNotExist())) {
144+
// set our size to the smallest resumable size so we can send multiple requests
145+
w.setChunkSize(_256KiB);
146+
ByteStreams.copy(Channels.newChannel(new ByteArrayInputStream(bytes)), w);
147+
}
148+
}
149+
150+
private BlobWithContent readAll(BlobInfo info) {
151+
try (ReadChannel r = storage.reader(info.getBlobId(), BlobSourceOption.generationMatch());
152+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
153+
WritableByteChannel w = Channels.newChannel(baos)) {
154+
// only buffer up to half the object
155+
r.setChunkSize(_256KiB);
156+
ByteStreams.copy(r, w);
157+
return new BlobWithContent(info, baos.toByteArray());
158+
} catch (IOException e) {
159+
throw new RuntimeException(e);
160+
}
161+
}
162+
163+
private static Predicate<Boolean> isFalse() {
164+
return b -> !b;
165+
}
166+
167+
private static final class BlobWithContent {
168+
private final BlobInfo info;
169+
private final byte[] content;
170+
171+
private BlobWithContent(BlobInfo info, byte[] content) {
172+
this.info = info;
173+
this.content = content;
174+
}
175+
176+
public BlobInfo getInfo() {
177+
return info;
178+
}
179+
180+
public byte[] getContent() {
181+
return content;
182+
}
183+
}
184+
}

google-cloud-storage/src/test/resources/META-INF/native-image/com/google/cloud/storage/native-image.properties

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,23 @@
55
# build time. Initializing these classes explicitly at build time results in a
66
# successful build.
77
Args = \
8-
--initialize-at-build-time=com.google.cloud.conformance.storage.v1,\
9-
com.google.protobuf,\
10-
com.google.auth.oauth2,\
11-
com.google.cloud.storage.conformance.retry,\
12-
com.google.common.base.Charsets,\
13-
com.google.gson.stream.JsonReader,\
8+
--initialize-at-build-time=com.google.cloud.storage.it.StorageNativeCanaryTest,\
9+
com.fasterxml.jackson,\
10+
com.google.api.client.http,\
11+
com.google.api.client.json,\
1412
com.google.api.client.util,\
15-
com.google.api.client.http.javanet.NetHttpTransport,\
16-
com.google.api.client.http.HttpTransport,\
17-
com.google.api.client.json.JsonParser$1,\
18-
com.google.api.client.json.gson.GsonParser$1,\
19-
com.google.common.io.BaseEncoding,\
20-
com.google.common.math.IntMath$1,\
21-
com.google.common.collect.Platform,\
22-
com.google.gson.Gson,\
23-
com.google.common.truth,\
13+
com.google.auth.oauth2,\
14+
com.google.cloud.storage.it.runner,\
15+
com.google.common.base,\
2416
com.google.common.collect,\
25-
com.google.gson.internal.reflect,\
26-
com.google.gson.internal.bind,\
27-
com.google.gson.internal,\
28-
com.google.gson.internal.sql.SqlTypesSupport,\
29-
com.google.gson.FieldNamingPolicy$3,\
30-
com.google.gson.LongSerializationPolicy$2,\
17+
com.google.common.io,\
18+
com.google.common.math,\
19+
com.google.common.truth,\
20+
com.google.gson,\
21+
com.google.protobuf,\
22+
com.google.rpc,\
23+
io.grpc,\
24+
io.opencensus,\
3125
net.jqwik
3226

3327

google-cloud-storage/src/test/resources/META-INF/native-image/com/google/cloud/storage/reflect-config.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,4 @@
1414
"allDeclaredFields":true,
1515
"allDeclaredMethods":true,
1616
"methods":[{"name":"<init>","parameterTypes":[] }]}
17-
,
18-
{
19-
"name":"com.google.cloud.storage.conformance.retry.TestBench$RetryTestResource",
20-
"allDeclaredFields":true,
21-
"methods":[{"name":"<init>","parameterTypes":[] }]}
2217
]

0 commit comments

Comments
 (0)