diff --git a/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/testing/FakeStorageRpc.java b/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/testing/FakeStorageRpc.java index c772a56b3..b3eaa48d6 100644 --- a/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/testing/FakeStorageRpc.java +++ b/google-cloud-nio/src/main/java/com/google/cloud/storage/contrib/nio/testing/FakeStorageRpc.java @@ -34,24 +34,14 @@ import com.google.cloud.storage.spi.v1.StorageRpc; import com.google.cloud.storage.testing.StorageRpcTestBase; import com.google.common.base.Preconditions; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; +import java.io.*; import java.math.BigInteger; import java.net.URLDecoder; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.FileAlreadyExistsException; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -108,6 +98,8 @@ class FakeStorageRpc extends StorageRpcTestBase { private final boolean throwIfOption; + private static final Comparator lexicographicalStringComparator = String::compareTo; + /** @param throwIfOption if true, we throw when given any option */ public FakeStorageRpc(boolean throwIfOption) { this.throwIfOption = throwIfOption; @@ -142,6 +134,8 @@ public Tuple> list(String bucket, Map String preprefix = ""; String pageToken = null; long maxResults = Long.MAX_VALUE; + String startOffset = null; + String endOffset = null; for (Map.Entry e : options.entrySet()) { switch (e.getKey()) { case PAGE_TOKEN: @@ -162,6 +156,18 @@ public Tuple> list(String bucket, Map case MAX_RESULTS: maxResults = (Long) e.getValue(); break; + case START_OFF_SET: + startOffset = (String) e.getValue(); + if (startOffset.startsWith("/")) { + startOffset = startOffset.substring(1); + } + break; + case END_OFF_SET: + endOffset = (String) e.getValue(); + if (endOffset.startsWith("/")) { + endOffset = endOffset.substring(1); + } + break; case USER_PROJECT: // prevent unsupported operation break; @@ -177,6 +183,14 @@ public Tuple> list(String bucket, Map if (!so.getBucket().equals(bucket) || !so.getName().startsWith(prefix)) { continue; } + if (startOffset != null + && lexicographicalStringComparator.compare(so.getName(), startOffset) < 0) { + continue; + } + if (endOffset != null + && lexicographicalStringComparator.compare(so.getName(), endOffset) >= 0) { + continue; + } if (processedAsFolder(so, delimiter, prefix, folders)) { continue; } diff --git a/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/testing/LocalStorageHelperTest.java b/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/testing/LocalStorageHelperTest.java index 6cac05f8d..eaa1d2737 100644 --- a/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/testing/LocalStorageHelperTest.java +++ b/google-cloud-nio/src/test/java/com/google/cloud/storage/contrib/nio/testing/LocalStorageHelperTest.java @@ -17,21 +17,17 @@ package com.google.cloud.storage.contrib.nio.testing; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import com.google.api.client.util.Lists; +import com.google.api.gax.paging.Page; import com.google.cloud.WriteChannel; -import com.google.cloud.storage.Blob; -import com.google.cloud.storage.BlobId; -import com.google.cloud.storage.BlobInfo; -import com.google.cloud.storage.Storage; -import com.google.cloud.storage.StorageOptions; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; +import com.google.cloud.storage.*; +import java.io.*; import java.nio.ByteBuffer; import java.nio.file.Files; +import java.util.List; +import java.util.stream.IntStream; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -180,4 +176,108 @@ public void testCopyOperationOverwritesExistingFile() { assertThat(localStorageService.readAllBytes(BlobId.of(bucket, original))) .isEqualTo(replacementContent); } + + @Test + public void testListObjectsWithStartOffsetOnly() { + // Arrange + String bucket = "bucket"; + byte[] originalContent = "original content".getBytes(); + IntStream.range(1, 10) + .forEach( + i -> + localStorageService.create( + BlobInfo.newBuilder(bucket, String.valueOf(i)).build(), originalContent)); + // Act + Page pages = localStorageService.list(bucket, Storage.BlobListOption.startOffset("5")); + List blobs = Lists.newArrayList(pages.iterateAll()); + // Assert + assertEquals(6, blobs.size()); + } + + @Test + public void testListObjectsWithEndOffsetOnly() { + // Arrange + String bucket = "bucket"; + byte[] originalContent = "original content".getBytes(); + IntStream.range(1, 10) + .forEach( + i -> + localStorageService.create( + BlobInfo.newBuilder(bucket, String.valueOf(i)).build(), originalContent)); + // Act + Page pages = localStorageService.list(bucket, Storage.BlobListOption.endOffset("5")); + List blobs = Lists.newArrayList(pages.iterateAll()); + // Assert + assertEquals(4, blobs.size()); + } + + @Test + public void testListObjectsWithStartOffsetAndEndOffset() { + // Arrange + String bucket = "bucket"; + byte[] originalContent = "original content".getBytes(); + IntStream.range(1, 10) + .forEach( + i -> + localStorageService.create( + BlobInfo.newBuilder(bucket, String.valueOf(i)).build(), originalContent)); + // Act + Page pages = + localStorageService.list( + bucket, Storage.BlobListOption.startOffset("1"), Storage.BlobListOption.endOffset("5")); + List blobs = Lists.newArrayList(pages.iterateAll()); + // Assert + System.out.println(blobs); + assertEquals(4, blobs.size()); + } + + @Test + public void testListObjectsWithPrefixAndStartOffsetAndEndOffset() { + // Arrange + String bucket = "bucket"; + byte[] originalContent = "original content".getBytes(); + IntStream.range(1, 10) + .forEach( + i -> { + localStorageService.create( + BlobInfo.newBuilder(bucket, "abc/" + i).build(), originalContent); + localStorageService.create( + BlobInfo.newBuilder(bucket, "bcd/" + i).build(), originalContent); + }); + // Act + Page pages = + localStorageService.list( + bucket, + Storage.BlobListOption.startOffset("abc/1"), + Storage.BlobListOption.endOffset("abc/5"), + Storage.BlobListOption.prefix("abc/")); + List blobs = Lists.newArrayList(pages.iterateAll()); + // Assert + assertEquals(4, blobs.size()); + } + + @Test + public void testListObjectsWithPrefixAndStartOffsetAndEndOffsetWithFileSeparatorInBeginning() { + // Arrange + String bucket = "bucket"; + byte[] originalContent = "original content".getBytes(); + IntStream.range(1, 10) + .forEach( + i -> { + localStorageService.create( + BlobInfo.newBuilder(bucket, "abc/" + i).build(), originalContent); + localStorageService.create( + BlobInfo.newBuilder(bucket, "bcd/" + i).build(), originalContent); + }); + // Act + Page pages = + localStorageService.list( + bucket, + Storage.BlobListOption.startOffset("/abc/1"), + Storage.BlobListOption.endOffset("/abc/5"), + Storage.BlobListOption.prefix("/abc/")); + List blobs = Lists.newArrayList(pages.iterateAll()); + // Assert + assertEquals(4, blobs.size()); + } }