/* * Copyright 2014 Google Inc. All Rights Reserved. * * 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.hadoop.gcsio.integration; import static com.google.cloud.hadoop.gcsio.GoogleCloudStorage.MAX_RESULTS_UNLIMITED; import static com.google.cloud.hadoop.gcsio.integration.GoogleCloudStorageTestHelper.assertObjectContent; import static com.google.cloud.hadoop.gcsio.integration.GoogleCloudStorageTestHelper.writeObject; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import com.google.api.client.util.Clock; import com.google.cloud.hadoop.gcsio.CreateFileOptions; import com.google.cloud.hadoop.gcsio.CreateObjectOptions; import com.google.cloud.hadoop.gcsio.GoogleCloudStorage; import com.google.cloud.hadoop.gcsio.GoogleCloudStorage.ListPage; import com.google.cloud.hadoop.gcsio.GoogleCloudStorageItemInfo; import com.google.cloud.hadoop.gcsio.GoogleCloudStorageOptions; import com.google.cloud.hadoop.gcsio.GoogleCloudStorageReadOptions; import com.google.cloud.hadoop.gcsio.LaggedGoogleCloudStorage; import com.google.cloud.hadoop.gcsio.LaggedGoogleCloudStorage.ListVisibilityCalculator; import com.google.cloud.hadoop.gcsio.PerformanceCachingGoogleCloudStorage; import com.google.cloud.hadoop.gcsio.PerformanceCachingGoogleCloudStorageOptions; import com.google.cloud.hadoop.gcsio.StorageResourceId; import com.google.cloud.hadoop.gcsio.StringPaths; import com.google.cloud.hadoop.gcsio.UpdatableItemInfo; import com.google.cloud.hadoop.gcsio.VerificationAttributes; import com.google.cloud.hadoop.gcsio.integration.GoogleCloudStorageTestHelper.TestBucketHelper; import com.google.cloud.hadoop.gcsio.testing.InMemoryGoogleCloudStorage; import com.google.cloud.hadoop.util.AsyncWriteChannelOptions; import com.google.common.base.Equivalence; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.MapDifference; import com.google.common.collect.Maps; import com.google.common.hash.HashCode; import com.google.common.hash.Hashing; import com.google.common.primitives.Bytes; import com.google.common.primitives.Ints; import java.io.EOFException; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.SeekableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.AfterClass; import org.junit.Before; import org.junit.Ignore; 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 GoogleCloudStorageTest { // This string is used to prefix all bucket names that are created for GCS IO integration testing private static final String BUCKET_NAME_PREFIX = "gcsio-it"; private static final Supplier<TestBucketHelper> BUCKET_HELPER = Suppliers.memoize(() -> new TestBucketHelper(BUCKET_NAME_PREFIX)); private static final LoadingCache<GoogleCloudStorage, String> SHARED_BUCKETS = CacheBuilder.newBuilder() .build( new CacheLoader<GoogleCloudStorage, String>() { @Override public String load(GoogleCloudStorage gcs) throws Exception { return createUniqueBucket(gcs, "shared"); } }); private static String createUniqueBucket(GoogleCloudStorage gcs, String suffix) throws IOException { String bucketName = getUniqueBucketName(suffix) + "_" + gcs.hashCode(); gcs.create(bucketName); return bucketName; } private String createUniqueBucket(String suffix) throws IOException { return createUniqueBucket(rawStorage, suffix); } private static String getUniqueBucketName(String suffix) { return BUCKET_HELPER.get().getUniqueBucketName(suffix); } /** An Equivalence for byte arrays. */ public static final Equivalence<byte[]> BYTE_ARRAY_EQUIVALENCE = new Equivalence<byte[]>() { @Override protected boolean doEquivalent(byte[] bytes, byte[] bytes2) { return Arrays.equals(bytes, bytes2); } @Override protected int doHash(byte[] bytes) { return Arrays.hashCode(bytes); } }; // Test classes using JUnit4 runner must have only a single constructor. Since we // want to be able to pass in dependencies, we'll maintain this base class as // @Parameterized with @Parameters. @Parameters public static Collection<Object[]> getConstructorArguments() throws IOException { GoogleCloudStorage gcs = new InMemoryGoogleCloudStorage(); GoogleCloudStorage zeroLaggedGcs = new LaggedGoogleCloudStorage( new InMemoryGoogleCloudStorage(), Clock.SYSTEM, ListVisibilityCalculator.IMMEDIATELY_VISIBLE); GoogleCloudStorage performanceCachingGcs = new PerformanceCachingGoogleCloudStorage( new InMemoryGoogleCloudStorage(), PerformanceCachingGoogleCloudStorageOptions.DEFAULT); return Arrays.asList( new Object[] {gcs}, new Object[] {zeroLaggedGcs}, new Object[] {performanceCachingGcs}); } private final GoogleCloudStorage rawStorage; public GoogleCloudStorageTest(GoogleCloudStorage rawStorage) { this.rawStorage = rawStorage; } @Before public void setUp() { if (rawStorage instanceof PerformanceCachingGoogleCloudStorage) { ((PerformanceCachingGoogleCloudStorage) rawStorage).invalidateCache(); } } @AfterClass public static void cleanupBuckets() throws IOException { // Use any GCS object (from tested ones) for clean up BUCKET_HELPER.get().cleanup(Iterables.getLast(SHARED_BUCKETS.asMap().keySet())); } private String getSharedBucketName() { return SHARED_BUCKETS.getUnchecked(rawStorage); } @Test public void testCreateSuccessfulBucket() throws IOException { String bucketName = createUniqueBucket("create-successful"); // Verify the bucket exist by creating an object StorageResourceId objectToCreate = new StorageResourceId(bucketName, "CreateTestObject"); rawStorage.createEmptyObject(objectToCreate); } @Test public void testCreateExistingBucket() throws IOException { String bucketName = getSharedBucketName(); assertThrows(IOException.class, () -> rawStorage.create(bucketName)); } @Test public void testCreateInvalidBucket() throws IOException { // Buckets must start with a letter or number String bucketName = "--" + getUniqueBucketName("create-invalid"); assertThrows(IOException.class, () -> rawStorage.create(bucketName)); } @Test public void testCreateObject() throws IOException { String bucketName = getSharedBucketName(); // Verify the bucket exist by creating an object StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testCreateObject_Object"); byte[] objectBytes = writeObject(rawStorage, objectToCreate, /* objectSize= */ 512); assertObjectContent(rawStorage, objectToCreate, objectBytes); } @Test public void testCreateInvalidObject() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testCreateInvalidObject_InvalidObject\n"); assertThrows( IOException.class, () -> writeObject(rawStorage, objectToCreate, /* objectSize= */ 10)); } @Test public void testCreateZeroLengthObjectUsingCreate() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testCreateZeroLengthObjectUsingCreate_Object"); byte[] objectBytes = writeObject(rawStorage, objectToCreate, /* objectSize= */ 0); assertObjectContent(rawStorage, objectToCreate, objectBytes); } @Test public void testCreate1PageLengthObjectUsingCreate() throws IOException { String bucketName = getSharedBucketName(); int objectSize = AsyncWriteChannelOptions.PIPE_BUFFER_SIZE_DEFAULT; StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testCreate1PageLengthObjectUsingCreate_Object"); byte[] objectBytes = writeObject(rawStorage, objectToCreate, objectSize); assertObjectContent(rawStorage, objectToCreate, objectBytes); } @Test public void testCreate1PageLengthPlus1byteObjectUsingCreate() throws IOException { String bucketName = getSharedBucketName(); int objectSize = AsyncWriteChannelOptions.PIPE_BUFFER_SIZE_DEFAULT + 1; StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testCreate1PageLengthPlus1byteObjectUsingCreate_Object"); byte[] objectBytes = writeObject(rawStorage, objectToCreate, objectSize); assertObjectContent(rawStorage, objectToCreate, objectBytes); } @Test public void testCreateExistingObject() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testCreateExistingObject_Object"); writeObject(rawStorage, objectToCreate, /* objectSize= */ 128); byte[] overwriteBytesToWrite = writeObject(rawStorage, objectToCreate, /* objectSize= */ 256); assertObjectContent(rawStorage, objectToCreate, overwriteBytesToWrite); } @Test public void testCreateEmptyObject() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testCreateEmptyObject_Object"); rawStorage.createEmptyObject(objectToCreate); GoogleCloudStorageItemInfo itemInfo = rawStorage.getItemInfo(objectToCreate); assertThat(itemInfo.exists()).isTrue(); assertThat(itemInfo.getSize()).isEqualTo(0); } @Test public void testCreateEmptyObjects() throws IOException { String bucketName = getSharedBucketName(); List<StorageResourceId> storageResourceIds = Lists.newArrayList( new StorageResourceId(bucketName, "testCreateEmptyObjects_Object1"), new StorageResourceId(bucketName, "testCreateEmptyObjects_Object2")); rawStorage.createEmptyObjects(storageResourceIds); rawStorage .getItemInfos(storageResourceIds) .forEach( itemInfo -> { assertWithMessage("%s should be empty", itemInfo).that(itemInfo.exists()).isTrue(); assertWithMessage("%s should be empty", itemInfo) .that(itemInfo.getSize()) .isEqualTo(0); }); } @Test public void testCreateEmptyObjectsWithOptions() throws IOException { String bucketName = getSharedBucketName(); List<StorageResourceId> storageResourceIds = Lists.newArrayList( new StorageResourceId(bucketName, "testCreateEmptyObjectsWithOptions_Object1"), new StorageResourceId(bucketName, "testCreateEmptyObjectsWithOptions_Object2")); rawStorage.createEmptyObjects(storageResourceIds, CreateObjectOptions.DEFAULT); rawStorage .getItemInfos(storageResourceIds) .forEach( itemInfo -> { assertWithMessage("%s should be empty", itemInfo).that(itemInfo.exists()).isTrue(); assertWithMessage("%s should be empty", itemInfo) .that(itemInfo.getSize()) .isEqualTo(0); }); } @Test public void testGetOptions() { GoogleCloudStorageOptions options = rawStorage.getOptions(); assertThat(options.getAppName()).startsWith("GHFS/"); } @Test public void testOpenFileWithMatchingSize() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testOpenFileWithMatchingSize_Object"); byte[] objectBytes = writeObject(rawStorage, objectToCreate, /* objectSize= */ 512); try (SeekableByteChannel channel = rawStorage.open(objectToCreate)) { assertThat(channel.size()).isEqualTo(objectBytes.length); } } @Test public void testOpenFileWithMatchingSizeAndSpecifiedReadOptions() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId( bucketName, "testOpenFileWithMatchingSizeAndSpecifiedReadOptions_Object"); byte[] objectBytes = writeObject(rawStorage, objectToCreate, /* objectSize= */ 512); try (SeekableByteChannel channel = rawStorage.open(objectToCreate, GoogleCloudStorageReadOptions.DEFAULT)) { assertThat(channel.size()).isEqualTo(objectBytes.length); } } @Test public void testListObjectNames() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testListObjectNames_Object"); writeObject(rawStorage, objectToCreate, /* objectSize= */ 512); List<String> listedName = rawStorage.listObjectNames(bucketName, "testListObjectNames", "/"); assertThat(listedName).containsExactly(objectToCreate.getObjectName()); } @Test public void testListObjectInfo() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testListObjectInfo_Object"); writeObject(rawStorage, objectToCreate, /* objectSize= */ 512); List<GoogleCloudStorageItemInfo> listedObjects = rawStorage.listObjectInfo(bucketName, "testListObjectInfo_", "/"); assertThat(listedObjects).hasSize(1); assertThat(listedObjects.get(0).getObjectName()).isEqualTo(objectToCreate.getObjectName()); } @Test public void testlistObjectInfoPage() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testListObjectInfoPage_Object"); writeObject(rawStorage, objectToCreate, /* objectSize= */ 512); ListPage<GoogleCloudStorageItemInfo> listedObjectsPage = rawStorage.listObjectInfoPage(bucketName, "testListObjectInfoPage_Object", "/", ""); assertThat(listedObjectsPage.getNextPageToken()).isNull(); assertThat(listedObjectsPage.getItems()).hasSize(1); assertThat(listedObjectsPage.getItems().get(0).getObjectName()) .isEqualTo(objectToCreate.getObjectName()); } @Test public void testComposeObjectsMovesObjectToAnother() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId srcObject = new StorageResourceId(bucketName, "testListObjectMovesObjectToAnother_srcObject"); StorageResourceId dstObject = new StorageResourceId(bucketName, "testListObjectMovesObjectToAnother_dstObject"); writeObject(rawStorage, srcObject, /* objectSize= */ 512); GoogleCloudStorageItemInfo composedObject = rawStorage.composeObjects( ImmutableList.of(srcObject), dstObject, CreateObjectOptions.DEFAULT); assertThat(composedObject.exists()).isTrue(); assertThat(composedObject.getObjectName()).isEqualTo(dstObject.getObjectName()); } @Test public void testCreateWithOptions() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testCreateWithOptions_Object"); rawStorage.create(objectToCreate, CreateObjectOptions.DEFAULT).close(); GoogleCloudStorageItemInfo itemInfo = rawStorage.getItemInfo(objectToCreate); assertThat(itemInfo.exists()).isTrue(); assertThat(itemInfo.getObjectName()).isEqualTo(objectToCreate.getObjectName()); assertThat(itemInfo.getSize()).isEqualTo(0); } @Test public void testCreateEmptyExistingObject() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testCreateEmptyExistingObject_Object"); rawStorage.createEmptyObject(objectToCreate); GoogleCloudStorageItemInfo itemInfo = rawStorage.getItemInfo(objectToCreate); assertThat(itemInfo.exists()).isTrue(); assertThat(itemInfo.getSize()).isEqualTo(0); rawStorage.createEmptyObject(objectToCreate); GoogleCloudStorageItemInfo secondItemInfo = rawStorage.getItemInfo(objectToCreate); assertThat(secondItemInfo.exists()).isTrue(); assertThat(secondItemInfo.getSize()).isEqualTo(0); assertThat(secondItemInfo.getCreationTime()).isNotSameInstanceAs(itemInfo.getCreationTime()); } @Test public void testGetSingleItemInfo() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testGetSingleItemInfo_Object1"); rawStorage.createEmptyObject(objectToCreate); GoogleCloudStorageItemInfo itemInfo = rawStorage.getItemInfo(objectToCreate); assertThat(itemInfo.exists()).isTrue(); assertThat(itemInfo.getSize()).isEqualTo(0); StorageResourceId secondObjectToCreate = new StorageResourceId(bucketName, "testGetSingleItemInfo_Object2"); writeObject(rawStorage, secondObjectToCreate, /* objectSize= */ 100); GoogleCloudStorageItemInfo secondItemInfo = rawStorage.getItemInfo(secondObjectToCreate); assertThat(secondItemInfo.exists()).isTrue(); assertThat(secondItemInfo.getSize()).isEqualTo(100); assertThat(secondItemInfo.isBucket()).isFalse(); assertThat(secondItemInfo.isRoot()).isFalse(); GoogleCloudStorageItemInfo nonExistentItemInfo = rawStorage.getItemInfo( new StorageResourceId(bucketName, "testGetSingleItemInfo_IDontExist")); assertThat(nonExistentItemInfo.exists()).isFalse(); assertThat(nonExistentItemInfo.isBucket()).isFalse(); assertThat(nonExistentItemInfo.isRoot()).isFalse(); // Test bucket get item info GoogleCloudStorageItemInfo bucketInfo = rawStorage.getItemInfo(new StorageResourceId(bucketName)); assertThat(bucketInfo.exists()).isTrue(); assertThat(bucketInfo.isBucket()).isTrue(); GoogleCloudStorageItemInfo rootInfo = rawStorage.getItemInfo(StorageResourceId.ROOT); assertThat(rootInfo.exists()).isTrue(); assertThat(rootInfo.isRoot()).isTrue(); } @Test public void testGetMultipleItemInfo() throws IOException { String bucketName = getSharedBucketName(); List<StorageResourceId> objectsCreated = new ArrayList<>(); for (int i = 0; i < 3; i++) { StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testGetMultipleItemInfo_Object" + i); rawStorage.createEmptyObject(objectToCreate); objectsCreated.add(objectToCreate); } StorageResourceId bucketResourceId = new StorageResourceId(bucketName); StorageResourceId nonExistentResourceId = new StorageResourceId(bucketName, "testGetMultipleItemInfo_IDontExist"); List<StorageResourceId> allResources = Lists.newArrayList(); allResources.addAll(objectsCreated); allResources.add(nonExistentResourceId); allResources.add(bucketResourceId); List<GoogleCloudStorageItemInfo> allInfo = rawStorage.getItemInfos(allResources); for (int i = 0; i < objectsCreated.size(); i++) { StorageResourceId resourceId = objectsCreated.get(i); GoogleCloudStorageItemInfo info = allInfo.get(i); assertThat(info.getResourceId()).isEqualTo(resourceId); assertThat(info.getSize()).isEqualTo(0); assertWithMessage("Item should exist").that(info.exists()).isTrue(); assertThat(info.getCreationTime()).isNotEqualTo(0); assertThat(info.isBucket()).isFalse(); } GoogleCloudStorageItemInfo nonExistentItemInfo = allInfo.get(allInfo.size() - 2); assertThat(nonExistentItemInfo.exists()).isFalse(); GoogleCloudStorageItemInfo bucketInfo = allInfo.get(allInfo.size() - 1); assertThat(bucketInfo.exists()).isTrue(); assertThat(bucketInfo.isBucket()).isTrue(); } // TODO(user): Re-enable once a new method of inducing errors is devised. @Test @Ignore public void testGetMultipleItemInfoWithSomeInvalid() throws IOException { String bucketName = getSharedBucketName(); List<StorageResourceId> resourceIdList = new ArrayList<>(); StorageResourceId newObject = new StorageResourceId(bucketName, "testGetMultipleItemInfoWithSomeInvalid_Object"); resourceIdList.add(newObject); rawStorage.createEmptyObject(newObject); StorageResourceId invalidObject = new StorageResourceId(bucketName, "testGetMultipleItemInfoWithSomeInvalid_InvalidObject\n"); resourceIdList.add(invalidObject); IOException e = assertThrows(IOException.class, () -> rawStorage.getItemInfos(resourceIdList)); assertThat(e).hasMessageThat().isEqualTo("Error getting StorageObject"); } // TODO(user): Re-enable once a new method of inducing errors is devised. @Test @Ignore public void testOneInvalidGetItemInfo() throws IOException { String bucketName = getSharedBucketName(); IOException e = assertThrows( IOException.class, () -> rawStorage.getItemInfo( new StorageResourceId( bucketName, "testOneInvalidGetItemInfo_InvalidObject\n"))); assertThat(e).hasMessageThat().isEqualTo("Error accessing"); } @Test public void testSingleObjectDelete() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId resource = new StorageResourceId(bucketName, "testSingleObjectDelete_Object"); rawStorage.createEmptyObject(resource); GoogleCloudStorageItemInfo info = rawStorage.getItemInfo(resource); assertThat(info.exists()).isTrue(); rawStorage.deleteObjects(ImmutableList.of(resource)); GoogleCloudStorageItemInfo deletedInfo = rawStorage.getItemInfo(resource); assertThat(deletedInfo.exists()).isFalse(); } @Test public void testMultipleObjectDelete() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId resource = new StorageResourceId(bucketName, "testMultipleObjectDelete_Object1"); rawStorage.createEmptyObject(resource); StorageResourceId secondResource = new StorageResourceId(bucketName, "testMultipleObjectDelete_Object2"); rawStorage.createEmptyObject(secondResource); assertThat(rawStorage.getItemInfo(resource).exists()).isTrue(); assertThat(rawStorage.getItemInfo(secondResource).exists()).isTrue(); rawStorage.deleteObjects(ImmutableList.of(resource, secondResource)); assertThat(rawStorage.getItemInfo(resource).exists()).isFalse(); assertThat(rawStorage.getItemInfo(secondResource).exists()).isFalse(); } // TODO(user): Re-enable once a new method of inducing errors is devised. @Test @Ignore public void testSomeInvalidObjectsDelete() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId resource = new StorageResourceId(bucketName, "testSomeInvalidObjectsDelete_Object"); rawStorage.createEmptyObject(resource); // Don't actually create a GCS object for this resource. StorageResourceId secondResource = new StorageResourceId(bucketName, "testSomeInvalidObjectsDelete_IDontExist"); StorageResourceId invalidName = new StorageResourceId(bucketName, "testSomeInvalidObjectsDelete_InvalidObject\n"); assertThat(rawStorage.getItemInfo(resource).exists()).isTrue(); assertThat(rawStorage.getItemInfo(secondResource).exists()).isFalse(); IOException e = assertThrows( IOException.class, () -> rawStorage.deleteObjects(ImmutableList.of(resource, secondResource, invalidName))); assertThat(e).hasMessageThat().isEqualTo("Error deleting"); } @Test public void testDeleteNonExistingObject() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId resource = new StorageResourceId(bucketName, "testDeleteNonExistingObject_IDontExist"); rawStorage.deleteObjects(ImmutableList.of(resource)); } @Test public void testDeleteNonExistingBucket() throws IOException { // Composite exception thrown, not a FileNotFoundException. String bucketName = getUniqueBucketName("delete_ne_bucket"); assertThrows(IOException.class, () -> rawStorage.deleteBuckets(ImmutableList.of(bucketName))); } @Test public void testSingleDeleteBucket() throws IOException { String bucketName = createUniqueBucket("delete-single"); rawStorage.deleteBuckets(ImmutableList.of(bucketName)); GoogleCloudStorageItemInfo info = rawStorage.getItemInfo(new StorageResourceId(bucketName)); assertThat(info.exists()).isFalse(); // Create the bucket again to assure that the previous one was deleted... rawStorage.create(bucketName); } @Test public void testMultipleDeleteBucket() throws IOException { StorageResourceId bucket1 = new StorageResourceId(createUniqueBucket("delete-multi-1")); StorageResourceId bucket2 = new StorageResourceId(createUniqueBucket("delete-multi-2")); rawStorage .getItemInfos(ImmutableList.of(bucket1, bucket2)) .forEach(i -> assertWithMessage("Expected to exist:%n%s", i).that(i.exists()).isTrue()); rawStorage.deleteBuckets(ImmutableList.of(bucket1.getBucketName(), bucket2.getBucketName())); rawStorage .getItemInfos(ImmutableList.of(bucket1, bucket2)) .forEach( i -> assertWithMessage("Expected to not exist:%n%s", i).that(i.exists()).isFalse()); } @Test public void testSomeInvalidDeleteBucket() throws IOException { String bucketName1 = createUniqueBucket("delete-multi-valid-1"); String bucketName2 = createUniqueBucket("delete-multi-valid-2"); String invalidBucketName = "--" + getUniqueBucketName("delete-multi-invalid"); assertThrows( IOException.class, () -> rawStorage.deleteBuckets( ImmutableList.of(bucketName1, bucketName2, invalidBucketName))); List<GoogleCloudStorageItemInfo> infoList = rawStorage.getItemInfos( ImmutableList.of( new StorageResourceId(bucketName1), new StorageResourceId(bucketName2))); for (GoogleCloudStorageItemInfo info : infoList) { assertThat(info.exists()).isFalse(); } } @Test public void testListBucketInfo() throws IOException { String bucketName = getSharedBucketName(); // This has potential to become flaky... List<GoogleCloudStorageItemInfo> infoList = rawStorage.listBucketInfo(); assertWithMessage("At least one bucket should exist").that(infoList).isNotEmpty(); boolean bucketListed = false; for (GoogleCloudStorageItemInfo info : infoList) { assertThat(info.exists()).isTrue(); assertThat(info.isBucket()).isTrue(); assertThat(info.isRoot()).isFalse(); bucketListed |= info.getBucketName().equals(bucketName); } assertThat(bucketListed).isTrue(); } @Test public void testListBucketNames() throws IOException { String bucketName = getSharedBucketName(); // This has potential to become flaky... List<String> bucketNames = rawStorage.listBucketNames(); assertWithMessage("Bucket names should not be empty").that(bucketNames).isNotEmpty(); assertThat(bucketNames).contains(bucketName); } @Test public void testListObjectNamesLimited() throws IOException { String bucketName = getSharedBucketName(); String[] names = {"a", "b", "c", "d"}; for (String name : names) { StorageResourceId id = new StorageResourceId(bucketName, "testListObjectNamesLimited_" + name); rawStorage.createEmptyObject(id); } List<String> gcsNames = rawStorage.listObjectNames(bucketName, "testListObjectNamesLimited_", "/", 2); assertThat(gcsNames).hasSize(2); } @Test public void testListObjectInfoLimited() throws IOException { String bucketName = getSharedBucketName(); String[] names = {"x", "y", "z"}; for (String name : names) { StorageResourceId id = new StorageResourceId(bucketName, "testListObjectInfoLimited_" + name); rawStorage.createEmptyObject(id); } List<GoogleCloudStorageItemInfo> info = rawStorage.listObjectInfo(bucketName, "testListObjectInfoLimited_", "/", 2); assertThat(info).hasSize(2); } @Test public void testListObjectInfoWithDirectoryRepair() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId d1 = new StorageResourceId(bucketName, "testListObjectInfoWithDirectoryRepair_d1/"); rawStorage.createEmptyObject(d1); StorageResourceId o1 = new StorageResourceId(bucketName, "testListObjectInfoWithDirectoryRepair_d1/o1"); rawStorage.createEmptyObject(o1); // No empty d2/ prefix: StorageResourceId d3 = new StorageResourceId(bucketName, "testListObjectInfoWithDirectoryRepair_d2/d3/"); rawStorage.createEmptyObject(d3); StorageResourceId o2 = new StorageResourceId(bucketName, "testListObjectInfoWithDirectoryRepair_d2/d3/o2"); rawStorage.createEmptyObject(o2); GoogleCloudStorageItemInfo itemInfo = rawStorage.getItemInfo( new StorageResourceId(bucketName, "testListObjectInfoWithDirectoryRepair_d2/")); assertThat(itemInfo.exists()).isFalse(); List<GoogleCloudStorageItemInfo> rootInfo = rawStorage.listObjectInfo( bucketName, "testListObjectInfoWithDirectoryRepair_", "/", MAX_RESULTS_UNLIMITED); assertWithMessage("Infos not expected to be empty").that(rootInfo).hasSize(2); GoogleCloudStorageItemInfo d2Info = rawStorage.getItemInfo( new StorageResourceId(bucketName, "testListObjectInfoWithDirectoryRepair_d2/")); assertThat(d2Info.exists()) .isEqualTo(rawStorage instanceof PerformanceCachingGoogleCloudStorage); List<GoogleCloudStorageItemInfo> d2ItemInfo = rawStorage.listObjectInfo( bucketName, "testListObjectInfoWithDirectoryRepair_d2/d3/", "/", MAX_RESULTS_UNLIMITED); assertWithMessage("D2 item info not expected to be empty").that(d2ItemInfo.isEmpty()).isFalse(); // Testing GCS treating object names as opaque blobs List<GoogleCloudStorageItemInfo> blobNamesInfo = rawStorage.listObjectInfo( bucketName, "testListObjectInfoWithDirectoryRepair_", null, MAX_RESULTS_UNLIMITED); assertWithMessage("blobNamesInfo not expected to be empty") .that(blobNamesInfo.isEmpty()) .isFalse(); } @Test public void testCopySingleItem() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testCopySingleItem_SourceObject"); byte[] objectBytes = writeObject(rawStorage, objectToCreate, /* objectSize= */ 4096); StorageResourceId copiedResourceId = new StorageResourceId(bucketName, "testCopySingleItem_DestinationObject"); rawStorage.copy( bucketName, ImmutableList.of(objectToCreate.getObjectName()), bucketName, ImmutableList.of(copiedResourceId.getObjectName())); assertObjectContent(rawStorage, copiedResourceId, objectBytes); } @Test public void testCopyToDifferentBucket() throws IOException { String sourceBucketName = getSharedBucketName(); String destinationBucketName = createUniqueBucket("copy-destination"); StorageResourceId objectToCreate = new StorageResourceId(sourceBucketName, "testCopyToDifferentBucket_SourceObject"); byte[] objectBytes = writeObject(rawStorage, objectToCreate, /* objectSize= */ 4096); StorageResourceId copiedResourceId = new StorageResourceId(destinationBucketName, "testCopyToDifferentBucket_DestinationObject"); rawStorage.copy( sourceBucketName, ImmutableList.of(objectToCreate.getObjectName()), destinationBucketName, ImmutableList.of(copiedResourceId.getObjectName())); assertObjectContent(rawStorage, copiedResourceId, objectBytes); } @Test public void testCopySingleItemOverExistingItem() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCopy = new StorageResourceId(bucketName, "testCopySingleItemOverExistingItem_Object1"); byte[] objectBytes = writeObject(rawStorage, objectToCopy, /* objectSize= */ 4096); assertObjectContent(rawStorage, objectToCopy, objectBytes); StorageResourceId secondObject = new StorageResourceId(bucketName, "testCopySingleItemOverExistingItem_Object2"); byte[] secondObjectBytes = writeObject(rawStorage, secondObject, /* objectSize= */ 2046); assertObjectContent(rawStorage, secondObject, secondObjectBytes); rawStorage.copy( bucketName, ImmutableList.of(objectToCopy.getObjectName()), bucketName, ImmutableList.of(secondObject.getObjectName())); // Second object should now have the bytes of the first. assertObjectContent(rawStorage, secondObject, objectBytes); } @Test public void testCopySingleItemOverItself() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCopy = new StorageResourceId(bucketName, "testCopySingleItemOverItself_Object"); writeObject(rawStorage, objectToCopy, /* objectSize= */ 1024); IllegalArgumentException e = assertThrows( IllegalArgumentException.class, () -> rawStorage.copy( bucketName, ImmutableList.of(objectToCopy.getObjectName()), bucketName, ImmutableList.of(objectToCopy.getObjectName()))); assertThat(e).hasMessageThat().startsWith("Copy destination must be different"); } static class CopyObjectData { public final StorageResourceId sourceResourceId; public final StorageResourceId destinationResourceId; public final byte[] objectBytes; CopyObjectData( StorageResourceId sourceResourceId, StorageResourceId destinationResourceId, byte[] objectBytes) { this.sourceResourceId = sourceResourceId; this.destinationResourceId = destinationResourceId; this.objectBytes = objectBytes; } } @Test public void testCopyMultipleItems() throws IOException { String bucketName = getSharedBucketName(); final int copyObjectCount = 3; List<CopyObjectData> objectsToCopy = new ArrayList<>(); for (int i = 0; i < copyObjectCount; i++) { String sourceObjectName = "testCopyMultipleItems_SourceObject" + i; String destinationObjectName = "testCopyMultipleItems_DestinationObject" + i; StorageResourceId sourceId = new StorageResourceId(bucketName, sourceObjectName); byte[] objectBytes = writeObject(rawStorage, sourceId, 1024 * i); StorageResourceId destinationId = new StorageResourceId(bucketName, destinationObjectName); objectsToCopy.add(new CopyObjectData(sourceId, destinationId, objectBytes)); } List<String> sourceObjects = Lists.transform( objectsToCopy, copyObjectData -> copyObjectData.sourceResourceId.getObjectName()); List<String> destinationObjects = Lists.transform( objectsToCopy, copyObjectData -> copyObjectData.destinationResourceId.getObjectName()); rawStorage.copy(bucketName, sourceObjects, bucketName, destinationObjects); for (CopyObjectData copyObjectData : objectsToCopy) { assertObjectContent(rawStorage, copyObjectData.sourceResourceId, copyObjectData.objectBytes); assertObjectContent( rawStorage, copyObjectData.destinationResourceId, copyObjectData.objectBytes); } } @Test public void testCopyNonExistentItem() throws IOException { String bucketName = getSharedBucketName(); String notExistentName = "testCopyNonExistentItem_IDontExist"; assertThrows( FileNotFoundException.class, () -> rawStorage.copy( bucketName, ImmutableList.of(notExistentName), bucketName, ImmutableList.of("testCopyNonExistentItem_DestinationObject"))); } @Test public void testCopyMultipleItemsToSingleDestination() throws IOException { String bucketName = getSharedBucketName(); IllegalArgumentException e = assertThrows( IllegalArgumentException.class, () -> rawStorage.copy( bucketName, ImmutableList.of( "testCopyMultipleItemsToSingleDestination_SourceObject1", "testCopyMultipleItemsToSingleDestination_SourceObject2"), bucketName, ImmutableList.of( "testCopyMultipleItemsToSingleDestination_DestinationObject"))); assertThat(e).hasMessageThat().startsWith("Must supply same number of elements"); } @Test public void testOpen() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testOpen_Object"); byte[] objectBytes = writeObject(rawStorage, objectToCreate, /* objectSize= */ 100); assertObjectContent(rawStorage, objectToCreate, objectBytes); } @Test public void testOpenNonExistentItem() throws IOException { String bucketName = getSharedBucketName(); assertThrows( FileNotFoundException.class, () -> rawStorage.open(new StorageResourceId(bucketName, "testOpenNonExistentItem_Object"))); } @Test public void testOpenEmptyObject() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId resourceId = new StorageResourceId(bucketName, "testOpenEmptyObject_Object"); rawStorage.createEmptyObject(resourceId); assertObjectContent(rawStorage, resourceId, new byte[0]); } @Test public void testOpenLargeObject() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId resourceId = new StorageResourceId(bucketName, "testOpenLargeObject_Object"); int partitionsCount = 50; byte[] partition = writeObject(rawStorage, resourceId, /* partitionSize= */ 10 * 1024 * 1024, partitionsCount); assertObjectContent(rawStorage, resourceId, partition, partitionsCount); } @Test public void testPlusInObjectNames() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId resourceId = new StorageResourceId(bucketName, "testPlusInObjectNames_An+Object"); rawStorage.createEmptyObject(resourceId); assertObjectContent(rawStorage, resourceId, new byte[0]); } @Test public void testObjectPosition() throws IOException { final int totalBytes = 1200; String bucketName = getSharedBucketName(); StorageResourceId resourceId = new StorageResourceId(bucketName, "testObjectPosition_Object"); byte[] data = writeObject(rawStorage, resourceId, /* objectSize= */ totalBytes); byte[] readBackingArray = new byte[totalBytes]; ByteBuffer readBuffer = ByteBuffer.wrap(readBackingArray); try (SeekableByteChannel readChannel = rawStorage.open(resourceId)) { assertWithMessage("Expected new file to open at position 0") .that(readChannel.position()) .isEqualTo(0); assertWithMessage("Unexpected readChannel.size()") .that(readChannel.size()) .isEqualTo(totalBytes); readBuffer.limit(4); int bytesRead = readChannel.read(readBuffer); assertWithMessage("Unexpected number of bytes read").that(bytesRead).isEqualTo(4); assertWithMessage("Unexpected position after read()") .that(readChannel.position()) .isEqualTo(4); assertWithMessage("Unexpected readChannel.size()") .that(readChannel.size()) .isEqualTo(totalBytes); readChannel.position(4); assertWithMessage("Unexpected position after no-op") .that(readChannel.position()) .isEqualTo(4); readChannel.position(6); assertWithMessage("Unexpected position after explicit position(6)") .that(readChannel.position()) .isEqualTo(6); readChannel.position(data.length - 1); assertWithMessage("Unexpected position after seek to EOF - 1") .that(readChannel.position()) .isEqualTo(data.length - 1); readBuffer.clear(); bytesRead = readChannel.read(readBuffer); assertWithMessage("Expected to read 1 byte").that(bytesRead).isEqualTo(1); assertWithMessage("Unexpected data read for last byte") .that(readBackingArray[0]) .isEqualTo(data[data.length - 1]); bytesRead = readChannel.read(readBuffer); assertWithMessage("Expected to read -1 bytes for EOF marker").that(bytesRead).isEqualTo(-1); readChannel.position(0); assertWithMessage("Unexpected position after reset to 0") .that(readChannel.position()) .isEqualTo(0); assertThrows(EOFException.class, () -> readChannel.position(-1)); assertThrows(EOFException.class, () -> readChannel.position(totalBytes)); } } @Test public void testReadPartialObjects() throws IOException { final int segmentSize = 553; final int segmentCount = 5; String bucketName = getSharedBucketName(); StorageResourceId resourceId = new StorageResourceId(bucketName, "testReadPartialObjects_Object"); byte[] data = writeObject(rawStorage, resourceId, /* objectSize= */ segmentCount * segmentSize); byte[][] readSegments = new byte[segmentCount][segmentSize]; try (SeekableByteChannel readChannel = rawStorage.open(resourceId)) { for (int i = 0; i < segmentCount; i++) { ByteBuffer segmentBuffer = ByteBuffer.wrap(readSegments[i]); int bytesRead = readChannel.read(segmentBuffer); assertThat(bytesRead).isEqualTo(segmentSize); byte[] expectedSegment = Arrays.copyOfRange( data, i * segmentSize, /* from index */ (i * segmentSize) + segmentSize /* to index */); assertWithMessage("Unexpected segment data read.") .that(readSegments[i]) .isEqualTo(expectedSegment); } } } @Test public void testSpecialResourceIds() throws IOException { assertWithMessage("Unexpected ROOT item info returned") .that(rawStorage.getItemInfo(StorageResourceId.ROOT)) .isEqualTo(GoogleCloudStorageItemInfo.ROOT_INFO); assertThrows( IllegalArgumentException.class, () -> StringPaths.fromComponents(null, "objectName")); } @Test public void testChannelClosedException() throws IOException { final int totalBytes = 1200; String bucketName = getSharedBucketName(); StorageResourceId resourceId = new StorageResourceId(bucketName, "testChannelClosedException_Object"); writeObject(rawStorage, resourceId, /* objectSize= */ totalBytes); byte[] readArray = new byte[totalBytes]; SeekableByteChannel readableByteChannel = rawStorage.open(resourceId); ByteBuffer readBuffer = ByteBuffer.wrap(readArray); readBuffer.limit(5); readableByteChannel.read(readBuffer); assertThat(readableByteChannel.position()).isEqualTo(readBuffer.position()); readableByteChannel.close(); readBuffer.clear(); assertThrows(ClosedChannelException.class, () -> readableByteChannel.read(readBuffer)); } @Test @Ignore("Not implemented") public void testOperationsAfterCloseFail() {} @Test public void testMetadataIsWrittenWhenCreatingObjects() throws IOException { String bucketName = getSharedBucketName(); byte[] bytesToWrite = new byte[100]; GoogleCloudStorageTestHelper.fillBytes(bytesToWrite); Map<String, byte[]> metadata = ImmutableMap.of( "key1", "value1".getBytes(StandardCharsets.UTF_8), "key2", "value2".getBytes(StandardCharsets.UTF_8)); // Verify the bucket exist by creating an object StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testUpdateItemInfoUpdatesMetadata_Object"); try (WritableByteChannel channel = rawStorage.create(objectToCreate, new CreateObjectOptions(false, metadata))) { channel.write(ByteBuffer.wrap(bytesToWrite)); } // Verify metadata was set on create. GoogleCloudStorageItemInfo itemInfo = rawStorage.getItemInfo(objectToCreate); assertMapsEqual(metadata, itemInfo.getMetadata(), BYTE_ARRAY_EQUIVALENCE); } @Test public void testMetadataIsWrittenWhenCreatingEmptyObjects() throws IOException { String bucketName = getSharedBucketName(); Map<String, byte[]> metadata = ImmutableMap.of( "key1", "value1".getBytes(StandardCharsets.UTF_8), "key2", "value2".getBytes(StandardCharsets.UTF_8)); // Verify the bucket exist by creating an object StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testMetadataIsWrittenWhenCreatingEmptyObjects_Object"); rawStorage.createEmptyObject(objectToCreate, new CreateObjectOptions(false, metadata)); // Verify we get metadata from getItemInfo GoogleCloudStorageItemInfo itemInfo = rawStorage.getItemInfo(objectToCreate); assertMapsEqual(metadata, itemInfo.getMetadata(), BYTE_ARRAY_EQUIVALENCE); } @Test public void testUpdateItemInfoUpdatesMetadata() throws IOException { String bucketName = getSharedBucketName(); Map<String, byte[]> metadata = ImmutableMap.of( "key1", "value1".getBytes(StandardCharsets.UTF_8), "key2", "value2".getBytes(StandardCharsets.UTF_8)); StorageResourceId objectToCreate = new StorageResourceId(bucketName, "testUpdateItemInfoUpdatesMetadata_Object"); writeObject(rawStorage, objectToCreate, /* objectSize= */ 100); GoogleCloudStorageItemInfo itemInfo = rawStorage.getItemInfo(objectToCreate); assertWithMessage("initial metadata should be empty").that(itemInfo.getMetadata()).isEmpty(); // Verify we can update metadata: List<GoogleCloudStorageItemInfo> results = rawStorage.updateItems(ImmutableList.of(new UpdatableItemInfo(objectToCreate, metadata))); assertThat(results).hasSize(1); assertMapsEqual(metadata, results.get(0).getMetadata(), BYTE_ARRAY_EQUIVALENCE); // Verify we get metadata from getItemInfo itemInfo = rawStorage.getItemInfo(objectToCreate); assertMapsEqual(metadata, itemInfo.getMetadata(), BYTE_ARRAY_EQUIVALENCE); // Delete key1 from metadata: Map<String, byte[]> deletionMap = new HashMap<>(); deletionMap.put("key1", null); rawStorage.updateItems(ImmutableList.of(new UpdatableItemInfo(objectToCreate, deletionMap))); itemInfo = rawStorage.getItemInfo(objectToCreate); // Ensure that only key2:value2 still exists: assertMapsEqual( ImmutableMap.of("key2", "value2".getBytes(StandardCharsets.UTF_8)), itemInfo.getMetadata(), BYTE_ARRAY_EQUIVALENCE); } @Test public void testGetBeforeAndAfterCreateObject() throws IOException { StorageResourceId resourceId = new StorageResourceId(getSharedBucketName(), "testGetBeforeAndAfterCreateObject_Object"); assertThat(rawStorage.getItemInfo(resourceId).exists()).isFalse(); byte[] objectContent = writeObject(rawStorage, resourceId, /* objectSize= */ 512); assertObjectContent(rawStorage, resourceId, objectContent); GoogleCloudStorageItemInfo itemInfo = rawStorage.getItemInfo(resourceId); assertThat(itemInfo.exists()).isTrue(); assertThat(itemInfo.getSize()).isEqualTo(512); } @Test public void testGetMultipleBeforeAndAfterCreateObject() throws IOException { String bucketName = getSharedBucketName(); List<StorageResourceId> resourceIds = ImmutableList.of( new StorageResourceId(bucketName, "testGetMultipleBeforeAndAfterCreateObject_Object1"), new StorageResourceId(bucketName, "testGetMultipleBeforeAndAfterCreateObject_Object2"), new StorageResourceId(bucketName, "testGetMultipleBeforeAndAfterCreateObject_Object3")); for (GoogleCloudStorageItemInfo itemInfo : rawStorage.getItemInfos(resourceIds)) { assertThat(itemInfo.exists()).isFalse(); } rawStorage.createEmptyObjects(resourceIds); List<GoogleCloudStorageItemInfo> itemInfos = rawStorage.getItemInfos(resourceIds); for (int i = 0; i < resourceIds.size(); i++) { StorageResourceId resourceId = resourceIds.get(i); GoogleCloudStorageItemInfo itemInfo = itemInfos.get(i); assertThat(itemInfo.getResourceId()).isEqualTo(resourceId); assertThat(itemInfo.getSize()).isEqualTo(0); assertThat(itemInfo.exists()).isTrue(); assertThat(itemInfo.getCreationTime()).isNotEqualTo(0); assertThat(itemInfo.isBucket()).isFalse(); assertThat(itemInfo.isDirectory()).isFalse(); } } @Test public void testListObjectInfoBeforeAndAfterCreate() throws IOException { String bucketName = getSharedBucketName(); String objectName = "testListObjectInfoBeforeAndAfterCreate_"; List<StorageResourceId> resourceIds = ImmutableList.of( new StorageResourceId(bucketName, objectName + "x"), new StorageResourceId(bucketName, objectName + "y"), new StorageResourceId(bucketName, objectName + "z")); assertThat(rawStorage.listObjectInfo(bucketName, objectName, "/")).isEmpty(); rawStorage.createEmptyObjects(resourceIds); assertThat(rawStorage.listObjectInfo(bucketName, objectName, "/")).hasSize(3); } @Test public void testlistObjectInfoPageBeforeAndAfterCreate() throws IOException { String bucketName = getSharedBucketName(); String objectName = "testlistObjectInfoPageBeforeAndAfterCreate_Object"; StorageResourceId resourceId = new StorageResourceId(bucketName, objectName); ListPage<GoogleCloudStorageItemInfo> itemInfosPage = rawStorage.listObjectInfoPage(bucketName, objectName, "/", /* pageToken= */ null); assertThat(itemInfosPage.getNextPageToken()).isNull(); assertThat(itemInfosPage.getItems()).isEmpty(); writeObject(rawStorage, resourceId, /* objectSize= */ 512); itemInfosPage = rawStorage.listObjectInfoPage(bucketName, objectName, "/", /* pageToken= */ null); assertThat(itemInfosPage.getNextPageToken()).isNull(); assertThat(itemInfosPage.getItems()).hasSize(1); assertThat(itemInfosPage.getItems().get(0).getResourceId()).isEqualTo(resourceId); } @Test public void testOverwriteExistingObject() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId resourceId = new StorageResourceId(bucketName, "testOverwriteExistingObject_Object"); assertThat(rawStorage.getItemInfo(resourceId).exists()).isFalse(); writeObject(rawStorage, resourceId, /* objectSize= */ 128); GoogleCloudStorageItemInfo createdItemInfo = rawStorage.getItemInfo(resourceId); assertThat(createdItemInfo.exists()).isTrue(); assertThat(createdItemInfo.getSize()).isEqualTo(128); byte[] objectContent = writeObject(rawStorage, resourceId, /* objectSize= */ 256); GoogleCloudStorageItemInfo overwrittenItemInfo = rawStorage.getItemInfo(resourceId); assertThat(overwrittenItemInfo.exists()).isTrue(); assertThat(overwrittenItemInfo.getSize()).isEqualTo(256); assertObjectContent(rawStorage, resourceId, objectContent); } @Test public void testMoveSingleItem() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId srcResourceId = new StorageResourceId(bucketName, "testMoveSingleItem_src"); StorageResourceId dstResourceId = new StorageResourceId(bucketName, "testMoveSingleItem_dst"); assertThat(rawStorage.getItemInfo(srcResourceId).exists()).isFalse(); assertThat(rawStorage.getItemInfo(dstResourceId).exists()).isFalse(); byte[] objectContent = writeObject(rawStorage, srcResourceId, /* objectSize= */ 4096); rawStorage.copy( bucketName, ImmutableList.of(srcResourceId.getObjectName()), bucketName, ImmutableList.of(dstResourceId.getObjectName())); assertThat(rawStorage.getItemInfo(srcResourceId).exists()).isTrue(); assertThat(rawStorage.getItemInfo(dstResourceId).exists()).isTrue(); assertObjectContent(rawStorage, dstResourceId, objectContent); rawStorage.deleteObjects(ImmutableList.of(srcResourceId)); assertThat(rawStorage.getItemInfo(srcResourceId).exists()).isFalse(); assertThat(rawStorage.getItemInfo(dstResourceId).exists()).isTrue(); } @Test public void testCompose() throws Exception { String bucketName = getSharedBucketName(); StorageResourceId destinationObject = new StorageResourceId(bucketName, "testCompose_DestinationObject"); // Create source objects StorageResourceId sourceObject1 = new StorageResourceId(bucketName, "testCompose_SourceObject1"); byte[] content1 = writeObject(rawStorage, sourceObject1, /* objectSize= */ 50); StorageResourceId sourceObject2 = new StorageResourceId(bucketName, "testCompose_SourceObject2"); byte[] content2 = writeObject(rawStorage, sourceObject2, /* objectSize= */ 150); // Do the compose rawStorage.compose( bucketName, ImmutableList.of("testCompose_SourceObject1", "testCompose_SourceObject2"), destinationObject.getObjectName(), CreateFileOptions.DEFAULT_CONTENT_TYPE); assertObjectContent(rawStorage, destinationObject, Bytes.concat(content1, content2)); } @Test public void testObjectVerificationAttributes() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId testObject = new StorageResourceId(bucketName, "testObjectValidationAttributes_Object"); // Don't use hashes in object creation, just validate the round trip. This of course // could lead to flaky looking tests due to bit flip errors. byte[] objectBytes = writeObject(rawStorage, testObject, /* objectSize= */ 1024); GoogleCloudStorageItemInfo itemInfo = rawStorage.getItemInfo(testObject); HashCode originalMd5 = Hashing.md5().hashBytes(objectBytes); HashCode originalCrc32c = Hashing.crc32c().hashBytes(objectBytes); // Note that HashCode#asBytes returns a little-endian encoded array while // GCS uses big-endian. We avoid that by grabbing the int value of the CRC32c // and running it through Ints.toByteArray which encodes using big-endian. byte[] bigEndianCrc32c = Ints.toByteArray(originalCrc32c.asInt()); GoogleCloudStorageTestHelper.assertByteArrayEquals( originalMd5.asBytes(), itemInfo.getVerificationAttributes().getMd5hash()); // These string versions are slightly easier to debug (used when trying to // replicate GCS crc32c values in InMemoryGoogleCloudStorage). String originalCrc32cString = Integer.toHexString(Ints.fromByteArray(bigEndianCrc32c)); String newCrc32cString = Integer.toHexString(Ints.fromByteArray(itemInfo.getVerificationAttributes().getCrc32c())); assertThat(newCrc32cString).isEqualTo(originalCrc32cString); GoogleCloudStorageTestHelper.assertByteArrayEquals( bigEndianCrc32c, itemInfo.getVerificationAttributes().getCrc32c()); VerificationAttributes expectedAttributes = new VerificationAttributes(originalMd5.asBytes(), bigEndianCrc32c); assertThat(itemInfo.getVerificationAttributes()).isEqualTo(expectedAttributes); } @Test public void googleCloudStorageItemInfo_equals() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId object1 = new StorageResourceId(bucketName, "testEquals_Object_1"); StorageResourceId object2 = new StorageResourceId(bucketName, "testEquals_Object_2"); writeObject(rawStorage, object1, /* objectSize= */ 1024); writeObject(rawStorage, object2, /* objectSize= */ 1024); GoogleCloudStorageItemInfo itemInfo1 = rawStorage.getItemInfo(object1); GoogleCloudStorageItemInfo itemInfo2 = rawStorage.getItemInfo(object2); assertThat(itemInfo1.equals(itemInfo1)).isTrue(); assertThat(itemInfo1.equals(itemInfo2)).isFalse(); } @Test public void googleCloudStorageItemInfo_toString() throws IOException { String bucketName = getSharedBucketName(); StorageResourceId object1 = new StorageResourceId(bucketName, "testToString_Object_1"); StorageResourceId object2 = new StorageResourceId(bucketName, "testToString_Object_2"); writeObject(rawStorage, object1, /* objectSize= */ 1024); GoogleCloudStorageItemInfo itemInfo1 = rawStorage.getItemInfo(object1); GoogleCloudStorageItemInfo itemInfo2 = rawStorage.getItemInfo(object2); assertThat(itemInfo1.exists()).isTrue(); assertThat(itemInfo1.toString()).contains("created on:"); assertThat(itemInfo2.exists()).isFalse(); assertThat(itemInfo2.toString()).contains("exists: no"); } static <K, V> void assertMapsEqual( Map<K, V> expected, Map<K, V> result, Equivalence<V> valueEquivalence) { MapDifference<K, V> diff = Maps.difference(expected, result, valueEquivalence); if (!diff.areEqual()) { fail( String.format( "Maps differ. Entries differing: %s%nMissing entries: %s%nExtra entries: %s%n", diff.entriesDiffering(), diff.entriesOnlyOnLeft(), diff.entriesOnlyOnRight())); } } }