diff --git a/providers/google-cloud-storage/pom.xml b/providers/google-cloud-storage/pom.xml
index bd51497e41..203bb39c39 100644
--- a/providers/google-cloud-storage/pom.xml
+++ b/providers/google-cloud-storage/pom.xml
@@ -96,6 +96,12 @@
${jclouds.version}
test
+
+ org.apache.jclouds.driver
+ jclouds-okhttp
+ ${project.version}
+ test
+
ch.qos.logback
logback-classic
diff --git a/providers/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/domain/ListPageWithPrefixes.java b/providers/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/domain/ListPageWithPrefixes.java
index a7eb4cf790..dddb3ec2f8 100644
--- a/providers/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/domain/ListPageWithPrefixes.java
+++ b/providers/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/domain/ListPageWithPrefixes.java
@@ -38,7 +38,7 @@ public final class ListPageWithPrefixes extends ForwardingList implements
}
@ConstructorProperties({ "items", "nextPageToken", "prefixes" })
- ListPageWithPrefixes(List items, String nextPageToken, List prefixes) {
+ public ListPageWithPrefixes(List items, String nextPageToken, List prefixes) {
this.items = copyOf(items);
this.nextPageToken = nextPageToken;
this.prefixes = copyOf(prefixes);
diff --git a/providers/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/domain/templates/ObjectTemplate.java b/providers/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/domain/templates/ObjectTemplate.java
index 215ebc0ebc..b23b7c1707 100644
--- a/providers/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/domain/templates/ObjectTemplate.java
+++ b/providers/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/domain/templates/ObjectTemplate.java
@@ -37,8 +37,8 @@ public class ObjectTemplate {
private String contentType;
private String crc32c;
private String md5Hash;
- private Map metadata = Maps.newLinkedHashMap();
- private List acl = Lists.newArrayList();
+ private Map metadata;
+ private List acl;
public ObjectTemplate name(String name) {
this.name = name;
@@ -81,11 +81,17 @@ public class ObjectTemplate {
}
public ObjectTemplate customMetadata(Map metadata) {
+ if (this.metadata == null) {
+ this.metadata = Maps.newLinkedHashMap();
+ }
this.metadata.putAll(metadata);
return this;
}
public ObjectTemplate customMetadata(String key, String value) {
+ if (this.metadata == null) {
+ this.metadata = Maps.newLinkedHashMap();
+ }
this.metadata.put(key, value);
return this;
}
@@ -101,11 +107,17 @@ public class ObjectTemplate {
}
public ObjectTemplate addAcl(ObjectAccessControls acl) {
+ if (this.acl == null) {
+ this.acl = Lists.newArrayList();
+ }
this.acl.add(acl);
return this;
}
public ObjectTemplate acl(List acl) {
+ if (this.acl == null) {
+ this.acl = Lists.newArrayList();
+ }
this.acl.addAll(acl);
return this;
}
diff --git a/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/features/ObjectApiMockTest.java b/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/features/ObjectApiMockTest.java
new file mode 100644
index 0000000000..1693386962
--- /dev/null
+++ b/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/features/ObjectApiMockTest.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.jclouds.googlecloudstorage.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertFalse;
+import static com.google.common.base.Charsets.UTF_8;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import org.jclouds.googlecloudstorage.domain.DomainResourceReferences.DestinationPredefinedAcl;
+import org.jclouds.googlecloudstorage.domain.DomainResourceReferences.PredefinedAcl;
+import org.jclouds.googlecloudstorage.domain.DomainResourceReferences.Projection;
+import org.jclouds.googlecloudstorage.domain.templates.ComposeObjectTemplate;
+import org.jclouds.googlecloudstorage.domain.templates.ObjectTemplate;
+import org.jclouds.googlecloudstorage.internal.BaseGoogleCloudStorageApiMockTest;
+import org.jclouds.googlecloudstorage.options.ComposeObjectOptions;
+import org.jclouds.googlecloudstorage.options.CopyObjectOptions;
+import org.jclouds.googlecloudstorage.options.GetObjectOptions;
+import org.jclouds.googlecloudstorage.options.InsertObjectOptions;
+import org.jclouds.googlecloudstorage.options.ListObjectOptions;
+import org.jclouds.googlecloudstorage.parse.ParseGoogleCloudStorageObject;
+import org.jclouds.googlecloudstorage.parse.ParseGoogleCloudStorageObjectListTest;
+import org.jclouds.http.internal.PayloadEnclosingImpl;
+import org.jclouds.io.PayloadEnclosing;
+import org.testng.annotations.Test;
+
+import com.google.common.net.MediaType;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+
+@Test(groups = "unit", testName = "ObjectApiMockTest", singleThreaded = true)
+public class ObjectApiMockTest extends BaseGoogleCloudStorageApiMockTest {
+
+
+ public void exists() throws Exception {
+ server.enqueue(jsonResponse("/object_get.json"));
+
+ assertTrue(objectApi().objectExists("test", "file_name"));
+ assertSent(server, "GET", "/storage/v1/b/test/o/file_name", null);
+ }
+
+ public void exists_4xx() throws Exception {
+ server.enqueue(response404());
+
+ assertFalse(objectApi().objectExists("test", "file_name"));
+ assertSent(server, "GET", "/storage/v1/b/test/o/file_name", null);
+ }
+
+ public void get() throws Exception {
+ server.enqueue(jsonResponse("/object_get.json"));
+
+ assertEquals(objectApi().getObject("test", "file_name"),
+ new ParseGoogleCloudStorageObject().expected());
+ assertSent(server, "GET", "/storage/v1/b/test/o/file_name");
+ }
+
+ public void get_4xx() throws Exception {
+ server.enqueue(response404());
+
+ assertNull(objectApi().getObject("test", "file_name"));
+ assertSent(server, "GET", "/storage/v1/b/test/o/file_name");
+ }
+
+ public void get_with_options() throws Exception {
+ server.enqueue(jsonResponse("/object_get.json"));
+
+ GetObjectOptions options = new GetObjectOptions().ifGenerationMatch((long) 1000);
+
+ assertEquals(objectApi().getObject("test", "file_name", options),
+ new ParseGoogleCloudStorageObject().expected());
+ assertSent(server, "GET", "/storage/v1/b/test/o/file_name?ifGenerationMatch=1000");
+ }
+
+ public void simpleUpload() throws Exception {
+ server.enqueue(jsonResponse("/object_get.json"));
+
+ PayloadEnclosing p = new PayloadEnclosingImpl();
+ String testPayload = "this is a test payload for upload!";
+ p.setPayload(testPayload.getBytes());
+
+ InsertObjectOptions options = new InsertObjectOptions()
+ .name("new_object")
+ .predefinedAcl(PredefinedAcl.PUBLIC_READ_WRITE);
+
+ assertEquals(objectApi().simpleUpload("bucket_name", "text/plain",
+ p.getPayload().getContentMetadata().getContentLength(), p.getPayload(), options),
+ new ParseGoogleCloudStorageObject().expected());
+
+ RecordedRequest request = assertSent(server, "POST", "/upload/storage/v1/b/bucket_name/o" +
+ "?uploadType=media&name=new_object&predefinedAcl=publicReadWrite", null);
+ assertEquals(request.getHeader("Content-Type"), "text/plain");
+ assertEquals(new String(request.getBody(), UTF_8), testPayload);
+ }
+
+ public void delete() throws Exception {
+ server.enqueue(new MockResponse());
+
+ // TODO: Should this be returning True on not found?
+ assertTrue(objectApi().deleteObject("test", "object_name"));
+ assertSent(server, "DELETE", "/storage/v1/b/test/o/object_name", null);
+ }
+
+ public void list() throws Exception {
+ server.enqueue(jsonResponse("/object_list.json"));
+
+ assertEquals(objectApi().listObjects("test"),
+ new ParseGoogleCloudStorageObjectListTest().expected());
+ assertSent(server, "GET", "/storage/v1/b/test/o");
+ }
+
+ public void list_with_options() throws Exception {
+ server.enqueue(jsonResponse("/object_list.json"));
+ ListObjectOptions options = new ListObjectOptions()
+ .delimiter("-")
+ .prefix("test")
+ .versions(Boolean.TRUE)
+ .pageToken("asdf")
+ .maxResults(4)
+ .projection(Projection.FULL);
+ assertEquals(objectApi().listObjects("test", options),
+ new ParseGoogleCloudStorageObjectListTest().expected());
+ assertSent(server, "GET", "/storage/v1/b/test/o?" +
+ "delimiter=-&prefix=test&versions=true&pageToken=asdf&maxResults=4&projection=full");
+ }
+
+ public void update() throws Exception {
+ server.enqueue(jsonResponse("/object_get.json"));
+
+ ObjectTemplate template = new ObjectTemplate().name("file_name").size((long) 1000).crc32c("crc32c");
+
+ assertEquals(objectApi().updateObject("test", "file_name", template),
+ new ParseGoogleCloudStorageObject().expected());
+ assertSent(server, "PUT", "/storage/v1/b/test/o/file_name", APPLICATION_JSON,
+ "{" +
+ " \"name\": \"file_name\"," +
+ " \"size\": 1000," +
+ " \"crc32c\": \"crc32c\"" +
+ "}");
+ }
+
+ public void patch() throws Exception {
+ server.enqueue(jsonResponse("/object_get.json"));
+
+ ObjectTemplate template = new ObjectTemplate().name("file_name").size((long) 1000).crc32c("crc32c");
+
+ assertEquals(objectApi().patchObject("test", "file_name", template),
+ new ParseGoogleCloudStorageObject().expected());
+ assertSent(server, "PATCH", "/storage/v1/b/test/o/file_name", APPLICATION_JSON,
+ "{" +
+ " \"name\": \"file_name\"," +
+ " \"size\": 1000," +
+ " \"crc32c\": \"crc32c\"" +
+ "}");
+ }
+
+ public void compose() throws Exception {
+ server.enqueue(jsonResponse("/object_get.json"));
+
+ ObjectTemplate template = new ObjectTemplate().name("file_name").size((long) 1000).crc32c("crc32c");
+
+ ComposeObjectTemplate composeTemplate = ComposeObjectTemplate.create(
+ new ParseGoogleCloudStorageObjectListTest().expected(), template);
+
+ assertEquals(objectApi().composeObjects("destination_bucket", "destination_object", composeTemplate),
+ new ParseGoogleCloudStorageObject().expected());
+ assertSent(server, "POST", "/storage/v1/b/destination_bucket/o/destination_object/compose", APPLICATION_JSON,
+ stringFromResource("/object_compose_request.json"));
+ }
+
+ public void compose_with_options() throws Exception {
+ server.enqueue(jsonResponse("/object_get.json"));
+
+ ObjectTemplate template = new ObjectTemplate().name("file_name").size((long) 1000).crc32c("crc32c");
+ ComposeObjectTemplate composeTemplate = ComposeObjectTemplate.create(
+ new ParseGoogleCloudStorageObjectListTest().expected(), template);
+
+ ComposeObjectOptions options = new ComposeObjectOptions()
+ .destinationPredefinedAcl(DestinationPredefinedAcl.BUCKET_OWNER_FULLCONTROL)
+ .ifMetagenerationMatch((long) 15);
+
+ assertEquals(objectApi().composeObjects("destination_bucket", "destination_object", composeTemplate, options),
+ new ParseGoogleCloudStorageObject().expected());
+ assertSent(server, "POST", "/storage/v1/b/destination_bucket/o/destination_object/compose" +
+ "?destinationPredefinedAcl=bucketOwnerFullcontrol&ifMetagenerationMatch=15", APPLICATION_JSON,
+ stringFromResource("/object_compose_request.json"));
+ }
+
+ public void copy() throws Exception {
+ server.enqueue(jsonResponse("/object_get.json"));
+
+ assertEquals(objectApi().copyObject("destination_bucket", "destination_object", "source_bucket", "source_object"),
+ new ParseGoogleCloudStorageObject().expected());
+ assertSent(server, "POST", "/storage/v1/b/source_bucket/o/source_object/copyTo" +
+ "/b/destination_bucket/o/destination_object", APPLICATION_JSON);
+ }
+
+ public void copy_with_options() throws Exception {
+ server.enqueue(jsonResponse("/object_get.json"));
+
+ CopyObjectOptions options = new CopyObjectOptions().ifGenerationMatch((long) 50);
+
+ assertEquals(objectApi().copyObject("destination_bucket", "destination_object", "source_bucket", "source_object", options),
+ new ParseGoogleCloudStorageObject().expected());
+ assertSent(server, "POST", "/storage/v1/b/source_bucket/o/source_object/copyTo" +
+ "/b/destination_bucket/o/destination_object?ifGenerationMatch=50", APPLICATION_JSON);
+ }
+
+ public void multipartUpload() throws Exception {
+ server.enqueue(jsonResponse("/object_get.json"));
+
+ PayloadEnclosing p = new PayloadEnclosingImpl();
+ String testPayload = "this is a test payload for upload!";
+ p.setPayload(testPayload.getBytes());
+
+ ObjectTemplate template = new ObjectTemplate().name("file_name").size((long) 1000).crc32c("crc32c").contentType(MediaType.ANY_TEXT_TYPE);
+
+ assertEquals(objectApi().multipartUpload("bucket_name", template, p.getPayload()),
+ new ParseGoogleCloudStorageObject().expected());
+
+ RecordedRequest request = assertSent(server, "POST", "/upload/storage/v1/b/bucket_name/o?uploadType=multipart", null);
+ assertTrue(new String(request.getBody(), UTF_8).contains(testPayload));
+
+ assertTrue(new String(request.getBody(), UTF_8).contains(testPayload));
+ //TODO: this should be a more robust assertion about the formatting of the payload
+ }
+
+
+ public ObjectApi objectApi(){
+ return api().getObjectApi();
+ }
+}
diff --git a/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageApiMockTest.java b/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageApiMockTest.java
new file mode 100644
index 0000000000..c30314530a
--- /dev/null
+++ b/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageApiMockTest.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.jclouds.googlecloudstorage.internal;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static com.google.common.base.Throwables.propagate;
+import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.jclouds.googlecloud.config.GoogleCloudProperties.CREDENTIAL_TYPE;
+import static org.jclouds.googlecloud.config.GoogleCloudProperties.PROJECT_NAME;
+import static org.jclouds.oauth.v2.config.CredentialType.BEARER_TOKEN_CREDENTIALS;
+import static org.jclouds.util.Strings2.toStringAndClose;
+import static org.testng.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.jclouds.ContextBuilder;
+import org.jclouds.concurrent.config.ExecutorServiceModule;
+import org.jclouds.googlecloudstorage.GoogleCloudStorageApi;
+import org.jclouds.googlecloudstorage.GoogleCloudStorageProviderMetadata;
+import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.JsonParser;
+import com.google.inject.AbstractModule;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+
+/**
+ * Tests need to run {@code singleThreaded = true} as otherwise tests will clash on the server field.
+ * Sharing the server field means less code to write.
+ */
+public class BaseGoogleCloudStorageApiMockTest {
+
+ protected final String identity = "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com";
+ protected final String credential = "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M"; // Fake Bearer Token
+
+ protected MockWebServer server;
+
+ protected GoogleCloudStorageApi api() {
+ return builder().buildApi(GoogleCloudStorageApi.class);
+ }
+
+
+ protected ContextBuilder builder() {
+ Properties overrides = new Properties();
+ overrides.put(PROJECT_NAME, "party");
+ overrides.put(CREDENTIAL_TYPE, BEARER_TOKEN_CREDENTIALS.toString());
+ return ContextBuilder.newBuilder(new GoogleCloudStorageProviderMetadata())
+ .credentials(identity, credential)
+ .endpoint(url(""))
+ .overrides(overrides)
+ .modules(modules);
+ }
+
+ private final Set modules = ImmutableSet
+ .of(new ExecutorServiceModule(sameThreadExecutor()), new OkHttpCommandExecutorServiceModule());
+
+
+ final AtomicInteger suffix = new AtomicInteger();
+
+ @BeforeMethod
+ public void start() throws IOException {
+ suffix.set(0);
+ server = new MockWebServer();
+ server.play();
+ }
+
+ protected String url(String path) {
+ return server.getUrl(path).toString();
+ }
+
+ @AfterMethod(alwaysRun = true)
+ public void stop() throws IOException {
+ server.shutdown();
+ }
+
+ protected MockResponse jsonResponse(String resource) {
+ return new MockResponse().addHeader("Content-Type", "application/json").setBody(stringFromResource(resource));
+ }
+
+ protected MockResponse response404(){
+ return new MockResponse().setStatus("HTTP/1.1 404 Not Found");
+ }
+
+ protected String stringFromResource(String resourceName) {
+ try {
+ return toStringAndClose(getClass().getResourceAsStream(resourceName));
+ } catch (IOException e) {
+ throw propagate(e);
+ }
+ }
+
+ protected RecordedRequest assertSent(MockWebServer server, String method, String path) throws InterruptedException {
+ return assertSent(server, method, path, APPLICATION_JSON);
+ }
+ protected RecordedRequest assertSent(MockWebServer server, String method, String path, String type) throws InterruptedException {
+ RecordedRequest request = server.takeRequest();
+ assertEquals(request.getMethod(), method);
+ assertEquals(request.getPath(), path);
+ if (type != null){
+ assertEquals(request.getHeader("Accept"), type);
+ }
+ assertEquals(request.getHeader("Authorization"), "Bearer " + credential);
+ return request;
+ }
+
+ protected RecordedRequest assertSent(MockWebServer server, String method, String path, String type, String json)
+ throws InterruptedException {
+ RecordedRequest request = assertSent(server, method, path, type);
+ assertEquals(request.getHeader("Content-Type"), APPLICATION_JSON);
+ assertEquals(parser.parse(new String(request.getBody(), UTF_8)), parser.parse(json));
+ return request;
+ }
+
+ /** So that we can ignore formatting. */
+ private final JsonParser parser = new JsonParser();
+}
diff --git a/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageParseTest.java b/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageParseTest.java
index 9209269bbf..52820e69d0 100644
--- a/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageParseTest.java
+++ b/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/internal/BaseGoogleCloudStorageParseTest.java
@@ -16,6 +16,9 @@
*/
package org.jclouds.googlecloudstorage.internal;
+import java.util.Date;
+
+import org.jclouds.date.internal.SimpleDateFormatDateService;
import org.jclouds.googlecloudstorage.config.GoogleCloudStorageParserModule;
import org.jclouds.json.BaseItemParserTest;
import org.jclouds.json.config.GsonModule;
@@ -28,4 +31,8 @@ public abstract class BaseGoogleCloudStorageParseTest extends BaseItemParserT
@Override protected Injector injector() {
return Guice.createInjector(new GsonModule(), new GoogleCloudStorageParserModule());
}
+
+ protected static Date parse(String iso8601) {
+ return new SimpleDateFormatDateService().iso8601DateParse(iso8601);
+ }
}
diff --git a/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/parse/ParseGoogleCloudStorageObject.java b/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/parse/ParseGoogleCloudStorageObject.java
new file mode 100644
index 0000000000..811a928a84
--- /dev/null
+++ b/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/parse/ParseGoogleCloudStorageObject.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.jclouds.googlecloudstorage.parse;
+
+import java.net.URI;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.googlecloudstorage.domain.DomainResourceReferences.StorageClass;
+import org.jclouds.googlecloudstorage.domain.Owner;
+import org.jclouds.googlecloudstorage.domain.GoogleCloudStorageObject;
+import org.jclouds.googlecloudstorage.internal.BaseGoogleCloudStorageParseTest;
+import org.testng.annotations.Test;
+
+@Test(groups = "unit", testName = "ParseGoogleCloudStorageObject")
+public class ParseGoogleCloudStorageObject extends BaseGoogleCloudStorageParseTest {
+
+ @Override
+ public String resource() {
+ return "/object_get.json";
+ }
+
+ @Override
+ @Consumes(MediaType.APPLICATION_JSON)
+ public GoogleCloudStorageObject expected() {
+ return GoogleCloudStorageObject.create(
+ "test/file_name/1000", //id
+ URI.create("https://www.googleapis.com/storage/v1/b/test/o/file_name"), //selfLink
+ "etag", // etag
+ "file_name", // name
+ "test", // bucket
+ (long) 1000, //generation
+ (long) 8, // metageneration
+ "application/x-tar", // contentType
+ parse("2014-09-27T00:01:44.819"), // updated
+ null, // timeDeleted
+ StorageClass.STANDARD, // storageClass
+ (long) 1000, //size,
+ "md5Hash", // md5Hash
+ URI.create("https://www.googleapis.com/download/storage/v1/b/test/o/file_name?generation=1000&alt=media"), // mediaLink
+ null, // metadata
+ null, // contentEncoding
+ null, // contentDisposition,
+ null, // contentLanguage
+ null, // cacheControl
+ null, // acl
+ Owner.create("entity", "entityId"), // owner,
+ "crc32c", // crc32c,
+ null); //componentCount
+ }
+}
diff --git a/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/parse/ParseGoogleCloudStorageObjectListTest.java b/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/parse/ParseGoogleCloudStorageObjectListTest.java
new file mode 100644
index 0000000000..081da69f0d
--- /dev/null
+++ b/providers/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/parse/ParseGoogleCloudStorageObjectListTest.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.jclouds.googlecloudstorage.parse;
+
+import java.net.URI;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.googlecloudstorage.domain.DomainResourceReferences.StorageClass;
+import org.jclouds.googlecloudstorage.domain.ListPageWithPrefixes;
+import org.jclouds.googlecloudstorage.domain.Owner;
+import org.jclouds.googlecloudstorage.domain.GoogleCloudStorageObject;
+import org.jclouds.googlecloudstorage.internal.BaseGoogleCloudStorageParseTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+
+@Test(groups = "unit", testName = "ParseGoogleCloudStorageObjectListTest")
+public class ParseGoogleCloudStorageObjectListTest extends BaseGoogleCloudStorageParseTest> {
+
+ @Override
+ public String resource() {
+ return "/object_list.json";
+ }
+
+ GoogleCloudStorageObject object1 = GoogleCloudStorageObject.create(
+ "test/file_name/1000", //id
+ URI.create("https://www.googleapis.com/storage/v1/b/test/o/file_name"), //selfLink
+ "etag", // etag
+ "file_name", // name
+ "test", // bucket
+ (long) 1000, //generation
+ (long) 8, // metageneration
+ "application/x-tar", // contentType
+ parse("2014-09-27T00:01:44.819"), // updated
+ null, // timeDeleted
+ StorageClass.STANDARD, // storageClass
+ (long) 1000, //size,
+ "md5Hash", // md5Hash
+ URI.create("https://www.googleapis.com/download/storage/v1/b/test/o/file_name?generation=1000&alt=media"), // mediaLink
+ null, // metadata
+ null, // contentEncoding
+ null, // contentDisposition,
+ null, // contentLanguage
+ null, // cacheControl
+ null, // acl
+ Owner.create("entity", "entityId"), // owner,
+ "crc32c", // crc32c,
+ null); //componentCount
+
+ GoogleCloudStorageObject object2 = GoogleCloudStorageObject.create(
+ "test/file_name2/1000", //id
+ URI.create("https://www.googleapis.com/storage/v1/b/test/o/file_name2"), //selfLink
+ "etag", // etag
+ "file_name2", // name
+ "test", // bucket
+ (long) 1001, //generation
+ (long) 9, // metageneration
+ "image/png", // contentType
+ parse("2014-09-27T00:01:44.819"), // updated
+ null, // timeDeleted
+ StorageClass.STANDARD, // storageClass
+ (long) 10, //size,
+ "md5Hash", // md5Hash
+ URI.create("https://www.googleapis.com/download/storage/v1/b/test/o/file_name2?generation=1001&alt=media"), // mediaLink
+ null, // metadata
+ null, // contentEncoding
+ null, // contentDisposition,
+ null, // contentLanguage
+ null, // cacheControl
+ null, // acl
+ Owner.create("entity", "entityId"), // owner,
+ "crc32c", // crc32c,
+ null); //componentCount
+
+ @Override
+ @Consumes(MediaType.APPLICATION_JSON)
+ public ListPageWithPrefixes expected() {
+ List items = ImmutableList.of(object1, object2);
+ return new ListPageWithPrefixes(items, null, null);
+ }
+}
diff --git a/providers/google-cloud-storage/src/test/resources/object_compose_request.json b/providers/google-cloud-storage/src/test/resources/object_compose_request.json
new file mode 100644
index 0000000000..93cbfc7b2c
--- /dev/null
+++ b/providers/google-cloud-storage/src/test/resources/object_compose_request.json
@@ -0,0 +1,54 @@
+{
+ "sourceObjects": [
+ {
+ "id": "test/file_name/1000",
+ "selfLink": "https://www.googleapis.com/storage/v1/b/test/o/file_name",
+ "etag": "etag",
+ "name": "file_name",
+ "bucket": "test",
+ "generation": 1000,
+ "metageneration": 8,
+ "contentType": "application/x-tar",
+ "updated": "2014-09-27T00:01:44.819Z",
+ "storageClass": "STANDARD",
+ "size": 1000,
+ "md5Hash": "md5Hash",
+ "mediaLink": "https://www.googleapis.com/download/storage/v1/b/test/o/file_name?generation=1000&alt=media",
+ "metadata": {},
+ "acl": [],
+ "owner": {
+ "entity": "entity",
+ "entityId": "entityId"
+ },
+ "crc32c": "crc32c"
+ },
+ {
+ "id": "test/file_name2/1000",
+ "selfLink": "https://www.googleapis.com/storage/v1/b/test/o/file_name2",
+ "etag": "etag",
+ "name": "file_name2",
+ "bucket": "test",
+ "generation": 1001,
+ "metageneration": 9,
+ "contentType": "image/png",
+ "updated": "2014-09-27T00:01:44.819Z",
+ "storageClass": "STANDARD",
+ "size": 10,
+ "md5Hash": "md5Hash",
+ "mediaLink": "https://www.googleapis.com/download/storage/v1/b/test/o/file_name2?generation=1001&alt=media",
+ "metadata": {},
+ "acl": [],
+ "owner": {
+ "entity": "entity",
+ "entityId": "entityId"
+ },
+ "crc32c": "crc32c"
+ }
+ ],
+ "destination": {
+ "name": "file_name",
+ "size": 1000,
+ "crc32c": "crc32c"
+ },
+ "kind": "storage/composeRequest"
+}
\ No newline at end of file
diff --git a/providers/google-cloud-storage/src/test/resources/object_get.json b/providers/google-cloud-storage/src/test/resources/object_get.json
new file mode 100644
index 0000000000..1c4d5f85ab
--- /dev/null
+++ b/providers/google-cloud-storage/src/test/resources/object_get.json
@@ -0,0 +1,21 @@
+{
+ "kind": "storage#object",
+ "id": "test/file_name/1000",
+ "selfLink": "https://www.googleapis.com/storage/v1/b/test/o/file_name",
+ "name": "file_name",
+ "bucket": "test",
+ "generation": "1000",
+ "metageneration": "8",
+ "contentType": "application/x-tar",
+ "updated": "2014-09-27T00:01:44.819",
+ "storageClass": "STANDARD",
+ "size": "1000",
+ "md5Hash": "md5Hash",
+ "mediaLink": "https://www.googleapis.com/download/storage/v1/b/test/o/file_name?generation=1000&alt=media",
+ "owner": {
+ "entity": "entity",
+ "entityId": "entityId"
+ },
+ "crc32c": "crc32c",
+ "etag": "etag"
+}
\ No newline at end of file
diff --git a/providers/google-cloud-storage/src/test/resources/object_list.json b/providers/google-cloud-storage/src/test/resources/object_list.json
new file mode 100644
index 0000000000..a0830c576e
--- /dev/null
+++ b/providers/google-cloud-storage/src/test/resources/object_list.json
@@ -0,0 +1,47 @@
+{
+ "kind": "storage#objects",
+ "items": [
+ {
+ "kind": "storage#object",
+ "id": "test/file_name/1000",
+ "selfLink": "https://www.googleapis.com/storage/v1/b/test/o/file_name",
+ "name": "file_name",
+ "bucket": "test",
+ "generation": "1000",
+ "metageneration": "8",
+ "contentType": "application/x-tar",
+ "updated": "2014-09-27T00:01:44.819",
+ "storageClass": "STANDARD",
+ "size": "1000",
+ "md5Hash": "md5Hash",
+ "mediaLink": "https://www.googleapis.com/download/storage/v1/b/test/o/file_name?generation=1000&alt=media",
+ "owner": {
+ "entity": "entity",
+ "entityId": "entityId"
+ },
+ "crc32c": "crc32c",
+ "etag": "etag"
+ },
+ {
+ "kind": "storage#object",
+ "id": "test/file_name2/1000",
+ "selfLink": "https://www.googleapis.com/storage/v1/b/test/o/file_name2",
+ "name": "file_name2",
+ "bucket": "test",
+ "generation": "1001",
+ "metageneration": "9",
+ "contentType": "image/png",
+ "updated": "2014-09-27T00:01:44.819",
+ "storageClass": "STANDARD",
+ "size": "10",
+ "md5Hash": "md5Hash",
+ "mediaLink": "https://www.googleapis.com/download/storage/v1/b/test/o/file_name2?generation=1001&alt=media",
+ "owner": {
+ "entity": "entity",
+ "entityId": "entityId"
+ },
+ "crc32c": "crc32c",
+ "etag": "etag"
+ }
+ ]
+}
\ No newline at end of file