From cd7cf1abbbf4b8dbfc07a2a0eba2149c4d216247 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 21 Mar 2016 16:22:40 -0400 Subject: [PATCH 1/5] Make some final changes to the GCS NIO API This change prepares GCS NIO to be merged into master, by making some last minute API changes. - Documentation has been added to many methods to clarify the preferred method for instantiating the library, which is the non-SPI version. - Unit tests have been updated to not rely on the SPI, because there's no way to guarantee clean isolation of SPI usage across tests. We'll be relying on integration testing to test the SPI interface. - The unit testing methodology has changed somewhat. FakeStorageRpc should be a private final field on the test class so, if desired, we'll be able to have the tests dip directly into fake memory. - IOException has been added back to the throws of file system close, in case we decide to implement the "close all owned channels" thing into that method in the future. - The getters on the configuration class have been made package-private, since there's no foreseeable reason they would be needed by the user. - Injectable constructors have been added for Dagger 2 users. In a future change, a README.md file will be added to replace the documentation in the package-info.java file. --- gcloud-java-contrib/gcloud-java-nio/pom.xml | 6 + .../nio/CloudStorageConfiguration.java | 60 +- .../contrib/nio/CloudStorageFileSystem.java | 96 +-- .../nio/CloudStorageFileSystemProvider.java | 80 +-- .../nio/CloudStorageConfigurationTest.java | 4 +- .../CloudStorageFileAttributeViewTest.java | 97 +-- .../nio/CloudStorageFileAttributesTest.java | 208 +++--- .../CloudStorageFileSystemProviderTest.java | 669 ++++++++++-------- .../nio/CloudStorageFileSystemTest.java | 45 +- .../contrib/nio/CloudStorageOptionsTest.java | 106 +-- .../contrib/nio/CloudStoragePathTest.java | 220 +++--- .../nio/CloudStorageReadChannelTest.java | 2 - .../storage/contrib/nio/NioTestHelper.java | 63 ++ .../storage/testing/FakeStorageRpc.java | 40 +- .../storage/testing/LocalGcsHelper.java | 68 -- 15 files changed, 934 insertions(+), 830 deletions(-) create mode 100644 gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/NioTestHelper.java delete mode 100644 gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/LocalGcsHelper.java diff --git a/gcloud-java-contrib/gcloud-java-nio/pom.xml b/gcloud-java-contrib/gcloud-java-nio/pom.xml index b3c2a99eea10..0b8af3353eea 100644 --- a/gcloud-java-contrib/gcloud-java-nio/pom.xml +++ b/gcloud-java-contrib/gcloud-java-nio/pom.xml @@ -49,6 +49,12 @@ 1.1 provided + + com.google.auto.factory + auto-factory + 1.0-beta3 + provided + junit junit diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java index 5d92c8e2063e..ad3ed2b98428 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java @@ -19,43 +19,26 @@ import static com.google.common.base.Preconditions.checkArgument; import com.google.auto.value.AutoValue; +import com.google.common.base.Strings; import java.util.Map; +import javax.annotation.Nullable; + /** * Configuration for {@link CloudStorageFileSystem} instances. */ @AutoValue public abstract class CloudStorageConfiguration { - /** - * Returns path of current working directory. This defaults to the root directory. - */ - public abstract String workingDirectory(); - - /** - * Returns {@code true} if we shouldn't throw an exception when encountering object names - * containing superfluous slashes, e.g. {@code a//b}. - */ - public abstract boolean permitEmptyPathComponents(); - - /** - * Returns {@code true} if '/' prefix on absolute object names should be removed before I/O. - * - *

If you disable this feature, please take into consideration that all paths created from a - * URI will have the leading slash. - */ - public abstract boolean stripPrefixSlash(); - - /** - * Returns {@code true} if paths with a trailing slash should be treated as fake directories. - */ - public abstract boolean usePseudoDirectories(); + private static final CloudStorageConfiguration DEFAULT = builder().build(); /** - * Returns block size (in bytes) used when talking to the GCS HTTP server. + * Returns default GCS NIO configuration. */ - public abstract int blockSize(); + public static CloudStorageConfiguration getDefault() { + return DEFAULT; + } /** * Creates a new builder, initialized with the following settings: @@ -70,22 +53,29 @@ public static Builder builder() { return new Builder(); } + abstract String workingDirectory(); + abstract boolean permitEmptyPathComponents(); + abstract boolean stripPrefixSlash(); + abstract boolean usePseudoDirectories(); + abstract int blockSize(); + /** * Builder for {@link CloudStorageConfiguration}. */ public static final class Builder { private String workingDirectory = UnixPath.ROOT; - private boolean permitEmptyPathComponents = false; + private boolean permitEmptyPathComponents; private boolean stripPrefixSlash = true; private boolean usePseudoDirectories = true; private int blockSize = CloudStorageFileSystem.BLOCK_SIZE_DEFAULT; /** - * Changes current working directory for new filesystem. This cannot be changed once it's - * been set. You'll need to create another {@link CloudStorageFileSystem} object. + * Changes current working directory for new filesystem. This defaults to the root directory. + * The working directory cannot be changed once it's been set. You'll need to create another + * {@link CloudStorageFileSystem} object. * - * @throws IllegalArgumentException if {@code path} is not absolute. + * @throws IllegalArgumentException if {@code path} is not absolute */ public Builder workingDirectory(String path) { checkArgument(UnixPath.getPath(false, path).isAbsolute(), "not absolute: %s", path); @@ -95,7 +85,7 @@ public Builder workingDirectory(String path) { /** * Configures whether or not we should throw an exception when encountering object names - * containing superfluous slashes, e.g. {@code a//b} + * containing superfluous slashes, e.g. {@code a//b}. */ public Builder permitEmptyPathComponents(boolean value) { permitEmptyPathComponents = value; @@ -146,15 +136,13 @@ public CloudStorageConfiguration build() { Builder() {} } - public static final CloudStorageConfiguration DEFAULT = builder().build(); - - static CloudStorageConfiguration fromMap(Map env) { + static CloudStorageConfiguration fromMap(@Nullable String workingDirectory, Map env) { Builder builder = builder(); + if (!Strings.isNullOrEmpty(workingDirectory)) { + builder.workingDirectory(workingDirectory); + } for (Map.Entry entry : env.entrySet()) { switch (entry.getKey()) { - case "workingDirectory": - builder.workingDirectory((String) entry.getValue()); - break; case "permitEmptyPathComponents": builder.permitEmptyPathComponents((Boolean) entry.getValue()); break; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java index aafb1c786ec9..02ec5bc2326c 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -19,8 +19,11 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.auto.factory.AutoFactory; +import com.google.auto.factory.Provided; +import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; -import com.google.gcloud.storage.StorageOptions; +import com.google.common.collect.Iterables; import java.io.IOException; import java.net.URI; @@ -32,11 +35,12 @@ import java.nio.file.WatchService; import java.nio.file.attribute.FileTime; import java.nio.file.attribute.UserPrincipalLookupService; +import java.nio.file.spi.FileSystemProvider; import java.util.Objects; import java.util.Set; -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; +import javax.annotation.CheckReturnValue; +import javax.annotation.concurrent.ThreadSafe; /** * Google Cloud Storage {@link FileSystem} implementation. @@ -46,49 +50,48 @@ * @see * Bucket and Object Naming Guidelines */ -@Immutable +@ThreadSafe public final class CloudStorageFileSystem extends FileSystem { /** - * Returns Google Cloud Storage {@link FileSystem} object for {@code bucket}. - * - *

NOTE: You may prefer to use Java's standard API instead:

   {@code
-   *
-   *   FileSystem fs = FileSystems.getFileSystem(URI.create("gs://bucket"));}
- * - *

However some systems and build environments might be flaky when it comes to Java SPI. This - * is because services are generally runtime dependencies and depend on a META-INF file being - * present in your jar (generated by Google Auto at compile-time). In such cases, this method - * provides a simpler alternative. - * - * @see #forBucket(String, CloudStorageConfiguration) - * @see java.nio.file.FileSystems#getFileSystem(URI) + * Invokes {@link #forBucket(String, CloudStorageConfiguration)} with + * {@link CloudStorageConfiguration#getDefault()}. */ + @CheckReturnValue public static CloudStorageFileSystem forBucket(String bucket) { - return forBucket(bucket, CloudStorageConfiguration.DEFAULT); + return forBucket(bucket, CloudStorageConfiguration.getDefault()); } /** - * Creates new file system instance for {@code bucket}, with customizable settings. + * Returns Google Cloud Storage {@link FileSystem} object for {@code bucket}. * - * @see #forBucket(String) - */ - public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfiguration config) { - return forBucket(bucket, config, null); - } - - /** - * Creates a new filesystem for a particular bucket, with customizable settings and storage - * options. + *

GCS file system objects are basically free. You can create as many as you want, even if you + * have multiple instances for the same bucket. There's no actual system resources associated + * with this object. Therefore calling {@link #close()} on the returned value is optional. + * + *

Note: It is also possible to instantiate this class via Java's Service Provider + * Interface (SPI), e.g. {@code FileSystems.getFileSystem(URI.create("gs://bucket"))}. We + * discourage you from using the SPI if possible, for the reasons documented in + * {@link CloudStorageFileSystemProvider#newFileSystem(URI, java.util.Map)} * - * @see #forBucket(String) + * @see #forBucket(String, CloudStorageConfiguration) + * @see java.nio.file.FileSystems#getFileSystem(java.net.URI) */ - public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfiguration config, - @Nullable StorageOptions storageOptions) { - checkArgument(!bucket.startsWith(URI_SCHEME + ":"), - "Bucket name must not have schema: %s", bucket); - return new CloudStorageFileSystem(new CloudStorageFileSystemProvider(storageOptions), - bucket, checkNotNull(config)); + @CheckReturnValue + public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfiguration config) { + checkNotNull(config); + checkArgument( + !bucket.startsWith(URI_SCHEME + ":"), "Bucket name must not have schema: %s", bucket); + return new CloudStorageFileSystem( + // XXX: This is a kludge to get the provider instance from the SPI. This is necessary since + // the behavior of NIO changes quite a bit if the provider instances aren't the same. + (CloudStorageFileSystemProvider) + Iterables.getOnlyElement( + Iterables.filter( + FileSystemProvider.installedProviders(), + Predicates.instanceOf(CloudStorageFileSystemProvider.class))), + config, + bucket); } public static final String URI_SCHEME = "gs"; @@ -98,12 +101,15 @@ public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfig public static final FileTime FILE_TIME_UNKNOWN = FileTime.fromMillis(0); public static final ImmutableSet SUPPORTED_VIEWS = ImmutableSet.of(BASIC_VIEW, GCS_VIEW); - private final CloudStorageFileSystemProvider provider; private final String bucket; + private final CloudStorageFileSystemProvider provider; private final CloudStorageConfiguration config; + @AutoFactory CloudStorageFileSystem( - CloudStorageFileSystemProvider provider, String bucket, CloudStorageConfiguration config) { + @Provided CloudStorageFileSystemProvider provider, + @Provided CloudStorageConfiguration config, + String bucket) { checkArgument(!bucket.isEmpty(), "bucket"); this.provider = provider; this.bucket = bucket; @@ -142,13 +148,20 @@ public CloudStoragePath getPath(String first, String... more) { } /** - * Does nothing. + * Does nothing currently. This method might be updated in the future to close all channels + * associated with this file system object. However it's unlikely that even then, calling this + * method will become mandatory. */ @Override - public void close() {} + public void close() throws IOException { + // TODO(jean-philippe-martin,jart): Synchronously close all active channels associated with this + // FileSystem instance on close, per NIO documentation. But we + // probably shouldn't bother unless a legitimate reason can be + // found to implement this behavior. + } /** - * Returns {@code true}. + * Returns {@code true}, even if you previously called the {@link #close()} method. */ @Override public boolean isOpen() { @@ -176,6 +189,9 @@ public Iterable getRootDirectories() { return ImmutableSet.of(CloudStoragePath.getPath(this, UnixPath.ROOT)); } + /** + * Returns nothing because GCS doesn't have disk partitions of limited size, or anything similar. + */ @Override public Iterable getFileStores() { return ImmutableSet.of(); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java index 4c07a5eb22fe..01e5a010568f 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java @@ -26,9 +26,7 @@ import static com.google.gcloud.storage.contrib.nio.CloudStorageUtil.stripPathFromUri; import com.google.auto.service.AutoService; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; -import com.google.common.base.Throwables; import com.google.common.primitives.Ints; import com.google.gcloud.storage.Acl; import com.google.gcloud.storage.BlobId; @@ -69,50 +67,33 @@ import java.util.Objects; import java.util.Set; -import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; +import javax.inject.Inject; +import javax.inject.Singleton; /** * Google Cloud Storage {@link FileSystemProvider} implementation. */ +@Singleton @ThreadSafe @AutoService(FileSystemProvider.class) public final class CloudStorageFileSystemProvider extends FileSystemProvider { private final Storage storage; - // used only when we create a new instance of CloudStorageFileSystemProvider. - private static StorageOptions defaultStorageOptions; - - /** - * Sets default options that are only used by the constructor. - */ - @VisibleForTesting - public static void setGCloudOptions(StorageOptions newStorageOptions) { - defaultStorageOptions = newStorageOptions; + @Inject + CloudStorageFileSystemProvider(Storage storage) { + this.storage = storage; } /** - * Default constructor which should only be called by Java SPI. + * Constructs a new instance with the default options. * - * @see java.nio.file.FileSystems#getFileSystem(URI) - * @see CloudStorageFileSystem#forBucket(String) + *

Note: This should only be called by the Java SPI. Please use + * {@link CloudStorageFileSystem#forBucket(String, CloudStorageConfiguration)} instead. */ public CloudStorageFileSystemProvider() { - this(defaultStorageOptions); - } - - CloudStorageFileSystemProvider(@Nullable StorageOptions explicitOptions) { - // explicit options have priority over default options. - if (explicitOptions == null) { - if (defaultStorageOptions == null) { - this.storage = StorageOptions.defaultInstance().service(); - } else { - this.storage = defaultStorageOptions.service(); - } - } else { - this.storage = explicitOptions.service(); - } + this(StorageOptions.defaultInstance().service()); } @Override @@ -121,7 +102,7 @@ public String getScheme() { } /** - * Returns Cloud Storage file system, provided a URI with no path, e.g. {@code gs://bucket}. + * Calls {@link #newFileSystem(URI, Map)} with an empty configuration map. */ @Override public CloudStorageFileSystem getFileSystem(URI uri) { @@ -129,7 +110,21 @@ public CloudStorageFileSystem getFileSystem(URI uri) { } /** - * Returns Cloud Storage file system, provided a URI with no path, e.g. {@code gs://bucket}. + * Returns Cloud Storage file system, provided a URI with no path. + * + *

Note: This method should be invoked indirectly via the SPI by calling + * {@link java.nio.file.FileSystems#newFileSystem(URI, Map) FileSystems.newFileSystem()}. However + * we recommend that you don't use the SPI if possible; the recommended approach is to write a + * dependency injection module that calls the statically-linked type-safe version of this method, + * which is: {@link CloudStorageFileSystem#forBucket(String, CloudStorageConfiguration)}. Please + * see that method for further documentation on creating GCS file systems. + * + * @param uri bucket and current working directory, e.g. {@code gs://bucket} + * @param env map of configuration options, whose keys correspond to the method names of + * {@link CloudStorageConfiguration.Builder}. However you are not allowed to set the working + * directory, as that should be provided in the {@code uri} + * @throws IllegalArgumentException if {@code uri} specifies a user, query, fragment, or scheme is + * not {@value CloudStorageFileSystem#URI_SCHEME} */ @Override public CloudStorageFileSystem newFileSystem(URI uri, Map env) { @@ -142,14 +137,14 @@ public CloudStorageFileSystem newFileSystem(URI uri, Map env) { !isNullOrEmpty(uri.getHost()), "%s:// URIs must have a host: %s", URI_SCHEME, uri); checkArgument( uri.getPort() == -1 - && isNullOrEmpty(uri.getPath()) && isNullOrEmpty(uri.getQuery()) && isNullOrEmpty(uri.getFragment()) && isNullOrEmpty(uri.getUserInfo()), "GCS FileSystem URIs mustn't have: port, userinfo, path, query, or fragment: %s", uri); checkBucket(uri.getHost()); - return new CloudStorageFileSystem(this, uri.getHost(), CloudStorageConfiguration.fromMap(env)); + return new CloudStorageFileSystem( + this, CloudStorageConfiguration.fromMap(uri.getPath(), env), uri.getHost()); } @Override @@ -575,23 +570,16 @@ public String toString() { } private IOException asIOException(StorageException oops) { + // RPC API can only throw StorageException, but CloudStorageFileSystemProvider + // can only throw IOException. Square peg, round hole. + // TODO: research if other codes should be translated similarly. if (oops.code() == 404) { return new NoSuchFileException(oops.reason()); } - // TODO: research if other codes should be translated to IOException. - - // RPC API can only throw StorageException, but CloudStorageFileSystemProvider - // can only throw IOException. Square peg, round hole. Throwable cause = oops.getCause(); - try { - if (cause instanceof FileAlreadyExistsException) { - throw new FileAlreadyExistsException(((FileAlreadyExistsException) cause).getReason()); - } - // fallback - Throwables.propagateIfInstanceOf(oops.getCause(), IOException.class); - } catch (IOException okEx) { - return okEx; + if (cause instanceof FileAlreadyExistsException) { + return (FileAlreadyExistsException) cause; } - return new IOException(oops.getMessage(), oops); + return new IOException("Storage operation failed", oops); } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfigurationTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfigurationTest.java index f3382188cc7c..9c98a9bd967d 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfigurationTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfigurationTest.java @@ -55,8 +55,8 @@ public void testBuilder() { public void testFromMap() { CloudStorageConfiguration config = CloudStorageConfiguration.fromMap( + "/omg", new ImmutableMap.Builder() - .put("workingDirectory", "/omg") .put("permitEmptyPathComponents", true) .put("stripPrefixSlash", false) .put("usePseudoDirectories", false) @@ -72,6 +72,6 @@ public void testFromMap() { @Test public void testFromMap_badKey_throwsIae() { thrown.expect(IllegalArgumentException.class); - CloudStorageConfiguration.fromMap(ImmutableMap.of("lol", "/omg")); + CloudStorageConfiguration.fromMap(null, ImmutableMap.of("lol", "/omg")); } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java index 9d9a65690bd8..e3bd7574c9c4 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributeViewTest.java @@ -22,9 +22,8 @@ import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; -import com.google.gcloud.storage.testing.LocalGcsHelper; +import com.google.gcloud.storage.testing.FakeStorageRpc; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -32,11 +31,10 @@ import org.junit.runners.JUnit4; import java.io.IOException; -import java.net.URI; +import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.attribute.FileTime; /** @@ -49,67 +47,76 @@ public class CloudStorageFileAttributeViewTest { @Rule public final ExpectedException thrown = ExpectedException.none(); - private Path path; - - @Before - public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); - path = Paths.get(URI.create("gs://red/water")); - } + private final FakeStorageRpc storage = new FakeStorageRpc(true); + private final NioTestHelper helper = new NioTestHelper(storage); @Test public void testReadAttributes() throws IOException { - Files.write(path, HAPPY, withCacheControl("potato")); - CloudStorageFileAttributeView lazyAttributes = - Files.getFileAttributeView(path, CloudStorageFileAttributeView.class); - assertThat(lazyAttributes.readAttributes().cacheControl().get()).isEqualTo("potato"); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Files.write(path, HAPPY, withCacheControl("potato")); + CloudStorageFileAttributeView lazyAttributes = + Files.getFileAttributeView(path, CloudStorageFileAttributeView.class); + assertThat(lazyAttributes.readAttributes().cacheControl().get()).isEqualTo("potato"); + } } @Test public void testReadAttributes_notFound_throwsNoSuchFileException() throws IOException { - CloudStorageFileAttributeView lazyAttributes = - Files.getFileAttributeView(path, CloudStorageFileAttributeView.class); - thrown.expect(NoSuchFileException.class); - lazyAttributes.readAttributes(); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + CloudStorageFileAttributeView lazyAttributes = + Files.getFileAttributeView(path, CloudStorageFileAttributeView.class); + thrown.expect(NoSuchFileException.class); + lazyAttributes.readAttributes(); + } } @Test public void testReadAttributes_pseudoDirectory() throws IOException { - Path dir = Paths.get(URI.create("gs://red/rum/")); - CloudStorageFileAttributeView lazyAttributes = - Files.getFileAttributeView(dir, CloudStorageFileAttributeView.class); - assertThat(lazyAttributes.readAttributes()) - .isInstanceOf(CloudStoragePseudoDirectoryAttributes.class); + try (FileSystem fs = helper.forBucket("red")) { + Path dir = fs.getPath("/rum/"); + CloudStorageFileAttributeView lazyAttributes = + Files.getFileAttributeView(dir, CloudStorageFileAttributeView.class); + assertThat(lazyAttributes.readAttributes()) + .isInstanceOf(CloudStoragePseudoDirectoryAttributes.class); + } } @Test public void testName() throws IOException { - Files.write(path, HAPPY, withCacheControl("potato")); - CloudStorageFileAttributeView lazyAttributes = - Files.getFileAttributeView(path, CloudStorageFileAttributeView.class); - assertThat(lazyAttributes.name()).isEqualTo("gcs"); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Files.write(path, HAPPY, withCacheControl("potato")); + CloudStorageFileAttributeView lazyAttributes = + Files.getFileAttributeView(path, CloudStorageFileAttributeView.class); + assertThat(lazyAttributes.name()).isEqualTo("gcs"); + } } @Test - public void testEquals_equalsTester() { - new EqualsTester() - .addEqualityGroup( - Files.getFileAttributeView( - Paths.get(URI.create("gs://red/rum")), CloudStorageFileAttributeView.class), - Files.getFileAttributeView( - Paths.get(URI.create("gs://red/rum")), CloudStorageFileAttributeView.class)) - .addEqualityGroup( - Files.getFileAttributeView( - Paths.get(URI.create("gs://red/lol/dog")), CloudStorageFileAttributeView.class)) - .testEquals(); + public void testEquals_equalsTester() throws IOException { + try (FileSystem fs = helper.forBucket("red")) { + new EqualsTester() + .addEqualityGroup( + Files.getFileAttributeView(fs.getPath("/rum"), CloudStorageFileAttributeView.class), + Files.getFileAttributeView(fs.getPath("/rum"), CloudStorageFileAttributeView.class)) + .addEqualityGroup( + Files.getFileAttributeView( + fs.getPath("/lol/dog"), CloudStorageFileAttributeView.class)) + .testEquals(); + } } @Test - public void testNullness() throws NoSuchMethodException, SecurityException { - new NullPointerTester() - .ignore(CloudStorageFileAttributeView.class.getMethod("equals", Object.class)) - .setDefault(FileTime.class, FileTime.fromMillis(0)) - .testAllPublicInstanceMethods( - Files.getFileAttributeView(path, CloudStorageFileAttributeView.class)); + public void testNullness() throws NoSuchMethodException, SecurityException, IOException { + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + new NullPointerTester() + .ignore(CloudStorageFileAttributeView.class.getMethod("equals", Object.class)) + .setDefault(FileTime.class, FileTime.fromMillis(0)) + .testAllPublicInstanceMethods( + Files.getFileAttributeView(path, CloudStorageFileAttributeView.class)); + } } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java index 38ee4020dbcb..deafc6b387f4 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java @@ -28,18 +28,16 @@ import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; import com.google.gcloud.storage.Acl; -import com.google.gcloud.storage.testing.LocalGcsHelper; +import com.google.gcloud.storage.testing.FakeStorageRpc; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.IOException; -import java.net.URI; +import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; /** * Unit tests for {@link CloudStorageFileAttributes}. @@ -49,137 +47,177 @@ public class CloudStorageFileAttributesTest { private static final byte[] HAPPY = "(✿◕ ‿◕ )ノ".getBytes(UTF_8); - private Path path; - private Path dir; - - @Before - public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); - path = Paths.get(URI.create("gs://bucket/randompath")); - dir = Paths.get(URI.create("gs://bucket/randompath/")); - } + private final FakeStorageRpc storage = new FakeStorageRpc(true); + private final NioTestHelper helper = new NioTestHelper(storage); @Test public void testCacheControl() throws IOException { - Files.write(path, HAPPY, withCacheControl("potato")); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get()) - .isEqualTo("potato"); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Files.write(path, HAPPY, withCacheControl("potato")); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get()) + .isEqualTo("potato"); + } } @Test public void testMimeType() throws IOException { - Files.write(path, HAPPY, withMimeType("text/potato")); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).mimeType().get()) - .isEqualTo("text/potato"); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Files.write(path, HAPPY, withMimeType("text/potato")); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).mimeType().get()) + .isEqualTo("text/potato"); + } } @Test public void testAcl() throws IOException { - Acl acl = Acl.of(new Acl.User("serf@example.com"), Acl.Role.READER); - Files.write(path, HAPPY, withAcl(acl)); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).acl().get()) - .contains(acl); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Acl acl = Acl.of(new Acl.User("serf@example.com"), Acl.Role.READER); + Files.write(path, HAPPY, withAcl(acl)); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).acl().get()) + .contains(acl); + } } @Test public void testContentDisposition() throws IOException { - Files.write(path, HAPPY, withContentDisposition("crash call")); - assertThat( - Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get()) - .isEqualTo("crash call"); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Files.write(path, HAPPY, withContentDisposition("crash call")); + assertThat( + Files.readAttributes( + path, CloudStorageFileAttributes.class).contentDisposition().get()) + .isEqualTo("crash call"); + } } @Test public void testContentEncoding() throws IOException { - Files.write(path, HAPPY, withContentEncoding("my content encoding")); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get()) - .isEqualTo("my content encoding"); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Files.write(path, HAPPY, withContentEncoding("my content encoding")); + assertThat( + Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get()) + .isEqualTo("my content encoding"); + } } @Test public void testUserMetadata() throws IOException { - Files.write(path, HAPPY, withUserMetadata("green", "bean")); - assertThat( - Files.readAttributes(path, CloudStorageFileAttributes.class) - .userMetadata() - .get("green")) - .isEqualTo("bean"); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Files.write(path, HAPPY, withUserMetadata("green", "bean")); + assertThat( + Files.readAttributes(path, CloudStorageFileAttributes.class) + .userMetadata() + .get("green")) + .isEqualTo("bean"); + } } @Test public void testIsDirectory() throws IOException { - Files.write(path, HAPPY); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isDirectory()) - .isFalse(); - assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isDirectory()).isTrue(); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Path dir = fs.getPath("/rum/"); + Files.write(path, HAPPY); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isDirectory()) + .isFalse(); + assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isDirectory()) + .isTrue(); + } } @Test public void testIsRegularFile() throws IOException { - Files.write(path, HAPPY); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isRegularFile()) - .isTrue(); - assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isRegularFile()) - .isFalse(); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Path dir = fs.getPath("/rum/"); + Files.write(path, HAPPY); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isRegularFile()) + .isTrue(); + assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isRegularFile()) + .isFalse(); + } } @Test public void testIsOther() throws IOException { - Files.write(path, HAPPY); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isOther()).isFalse(); - assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isOther()).isFalse(); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Path dir = fs.getPath("/rum/"); + Files.write(path, HAPPY); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isOther()).isFalse(); + assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isOther()).isFalse(); + } } @Test public void testIsSymbolicLink() throws IOException { - Files.write(path, HAPPY); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isSymbolicLink()) - .isFalse(); - assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isSymbolicLink()) - .isFalse(); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Path dir = fs.getPath("/rum/"); + Files.write(path, HAPPY); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).isSymbolicLink()) + .isFalse(); + assertThat(Files.readAttributes(dir, CloudStorageFileAttributes.class).isSymbolicLink()) + .isFalse(); + } } @Test public void testEquals_equalsTester() throws IOException { - Files.write(path, HAPPY, withMimeType("text/plain")); - CloudStorageFileAttributes a1 = Files.readAttributes(path, CloudStorageFileAttributes.class); - CloudStorageFileAttributes a2 = Files.readAttributes(path, CloudStorageFileAttributes.class); - Files.write(path, HAPPY, withMimeType("text/potato")); - CloudStorageFileAttributes b1 = Files.readAttributes(path, CloudStorageFileAttributes.class); - CloudStorageFileAttributes b2 = Files.readAttributes(path, CloudStorageFileAttributes.class); - new EqualsTester().addEqualityGroup(a1, a2).addEqualityGroup(b1, b2).testEquals(); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Files.write(path, HAPPY, withMimeType("text/plain")); + CloudStorageFileAttributes a1 = Files.readAttributes(path, CloudStorageFileAttributes.class); + CloudStorageFileAttributes a2 = Files.readAttributes(path, CloudStorageFileAttributes.class); + Files.write(path, HAPPY, withMimeType("text/potato")); + CloudStorageFileAttributes b1 = Files.readAttributes(path, CloudStorageFileAttributes.class); + CloudStorageFileAttributes b2 = Files.readAttributes(path, CloudStorageFileAttributes.class); + new EqualsTester().addEqualityGroup(a1, a2).addEqualityGroup(b1, b2).testEquals(); + } } @Test public void testFilekey() throws IOException { - Files.write(path, HAPPY, withMimeType("text/plain")); - Path path2 = Paths.get(URI.create("gs://bucket/anotherrandompath")); - Files.write(path2, HAPPY, withMimeType("text/plain")); - - // diff files cannot have same filekey - CloudStorageFileAttributes a1 = Files.readAttributes(path, CloudStorageFileAttributes.class); - CloudStorageFileAttributes a2 = Files.readAttributes(path2, CloudStorageFileAttributes.class); - assertThat(a1.fileKey()).isNotEqualTo(a2.fileKey()); - - // same for directories - CloudStorageFileAttributes b1 = Files.readAttributes(dir, CloudStorageFileAttributes.class); - CloudStorageFileAttributes b2 = - Files.readAttributes( - Paths.get(URI.create("gs://bucket/jacket/")), CloudStorageFileAttributes.class); - assertThat(a1.fileKey()).isNotEqualTo(b1.fileKey()); - assertThat(b1.fileKey()).isNotEqualTo(b2.fileKey()); + try (FileSystem fs = helper.forBucket("red")) { + Path dir = fs.getPath("/rum/"); + Path path = fs.getPath("/water"); + Files.write(path, HAPPY, withMimeType("text/plain")); + Path path2 = fs.getPath("/anotherrandompath"); + Files.write(path2, HAPPY, withMimeType("text/plain")); + + // diff files cannot have same filekey + CloudStorageFileAttributes a1 = Files.readAttributes(path, CloudStorageFileAttributes.class); + CloudStorageFileAttributes a2 = Files.readAttributes(path2, CloudStorageFileAttributes.class); + assertThat(a1.fileKey()).isNotEqualTo(a2.fileKey()); + + // same for directories + CloudStorageFileAttributes b1 = Files.readAttributes(dir, CloudStorageFileAttributes.class); + CloudStorageFileAttributes b2 = + Files.readAttributes(fs.getPath("/jacket/"), CloudStorageFileAttributes.class); + assertThat(a1.fileKey()).isNotEqualTo(b1.fileKey()); + assertThat(b1.fileKey()).isNotEqualTo(b2.fileKey()); + } } @Test public void testNullness() throws IOException, NoSuchMethodException, SecurityException { - Files.write(path, HAPPY); - CloudStorageFileAttributes pathAttributes = - Files.readAttributes(path, CloudStorageFileAttributes.class); - CloudStorageFileAttributes dirAttributes = - Files.readAttributes(dir, CloudStorageFileAttributes.class); - NullPointerTester tester = new NullPointerTester(); - tester.ignore(CloudStorageObjectAttributes.class.getMethod("equals", Object.class)); - tester.testAllPublicInstanceMethods(pathAttributes); - tester.testAllPublicInstanceMethods(dirAttributes); + try (FileSystem fs = helper.forBucket("red")) { + Path path = fs.getPath("/water"); + Path dir = fs.getPath("/rum/"); + Files.write(path, HAPPY); + CloudStorageFileAttributes pathAttributes = + Files.readAttributes(path, CloudStorageFileAttributes.class); + CloudStorageFileAttributes dirAttributes = + Files.readAttributes(dir, CloudStorageFileAttributes.class); + NullPointerTester tester = new NullPointerTester(); + tester.ignore(CloudStorageObjectAttributes.class.getMethod("equals", Object.class)); + tester.testAllPublicInstanceMethods(pathAttributes); + tester.testAllPublicInstanceMethods(dirAttributes); + } } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java index fca592a99587..5c1938e12318 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java @@ -17,12 +17,13 @@ package com.google.gcloud.storage.contrib.nio; import static com.google.common.truth.Truth.assertThat; -import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.forBucket; import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withCacheControl; import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withContentDisposition; import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withContentEncoding; import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withMimeType; import static com.google.gcloud.storage.contrib.nio.CloudStorageOptions.withUserMetadata; +import static com.google.gcloud.storage.contrib.nio.NioTestHelper.permitEmptyPathComponents; +import static com.google.gcloud.storage.contrib.nio.NioTestHelper.usePseudoDirectories; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; @@ -33,9 +34,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.testing.NullPointerTester; -import com.google.gcloud.storage.testing.LocalGcsHelper; +import com.google.gcloud.storage.testing.FakeStorageRpc; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -58,7 +58,6 @@ import java.nio.file.NoSuchFileException; import java.nio.file.OpenOption; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.List; @@ -94,26 +93,28 @@ public class CloudStorageFileSystemProviderTest { @Rule public final ExpectedException thrown = ExpectedException.none(); - @Before - public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); - } + private final FakeStorageRpc storage = new FakeStorageRpc(true); + private final NioTestHelper helper = new NioTestHelper(storage); @Test public void testSize() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/wat")); - Files.write(path, SINGULARITY.getBytes(UTF_8)); - assertThat(Files.size(path)).isEqualTo(SINGULARITY.getBytes(UTF_8).length); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/wat"); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + assertThat(Files.size(path)).isEqualTo(SINGULARITY.getBytes(UTF_8).length); + } } @Test public void testSize_trailingSlash_returnsFakePseudoDirectorySize() throws IOException { - assertThat(Files.size(Paths.get(URI.create("gs://bucket/wat/")))).isEqualTo(1); + try (FileSystem fs = helper.forBucket("bucket")) { + assertThat(Files.size(fs.getPath("/wat/"))).isEqualTo(1); + } } @Test public void testSize_trailingSlash_disablePseudoDirectories() throws IOException { - try (CloudStorageFileSystem fs = forBucket("doodle", usePseudoDirectories(false))) { + try (CloudStorageFileSystem fs = helper.forBucket("doodle", usePseudoDirectories(false))) { Path path = fs.getPath("wat/"); byte[] rapture = SINGULARITY.getBytes(UTF_8); Files.write(path, rapture); @@ -123,190 +124,227 @@ public void testSize_trailingSlash_disablePseudoDirectories() throws IOException @Test public void testReadAllBytes() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/wat")); - Files.write(path, SINGULARITY.getBytes(UTF_8)); - assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/wat"); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); + } } @Test public void testReadAllBytes_trailingSlash() throws IOException { - thrown.expect(CloudStoragePseudoDirectoryException.class); - Files.readAllBytes(Paths.get(URI.create("gs://bucket/wat/"))); + try (FileSystem fs = helper.forBucket("bucket")) { + thrown.expect(CloudStoragePseudoDirectoryException.class); + Files.readAllBytes(fs.getPath("/wat/")); + } } @Test public void testNewByteChannelRead() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/wat")); - byte[] data = SINGULARITY.getBytes(UTF_8); - Files.write(path, data); - try (ReadableByteChannel input = Files.newByteChannel(path)) { - ByteBuffer buffer = ByteBuffer.allocate(data.length); - assertThat(input.read(buffer)).isEqualTo(data.length); - assertThat(new String(buffer.array(), UTF_8)).isEqualTo(SINGULARITY); - buffer.rewind(); - assertThat(input.read(buffer)).isEqualTo(-1); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/wat"); + byte[] data = SINGULARITY.getBytes(UTF_8); + Files.write(path, data); + try (ReadableByteChannel input = Files.newByteChannel(path)) { + ByteBuffer buffer = ByteBuffer.allocate(data.length); + assertThat(input.read(buffer)).isEqualTo(data.length); + assertThat(new String(buffer.array(), UTF_8)).isEqualTo(SINGULARITY); + buffer.rewind(); + assertThat(input.read(buffer)).isEqualTo(-1); + } } } @Test public void testNewByteChannelRead_seeking() throws IOException { - Path path = Paths.get(URI.create("gs://lol/cat")); - Files.write(path, "helloworld".getBytes(UTF_8)); - try (SeekableByteChannel input = Files.newByteChannel(path)) { - ByteBuffer buffer = ByteBuffer.allocate(5); - input.position(5); - assertThat(input.position()).isEqualTo(5); - assertThat(input.read(buffer)).isEqualTo(5); - assertThat(input.position()).isEqualTo(10); - assertThat(new String(buffer.array(), UTF_8)).isEqualTo("world"); - buffer.rewind(); - assertThat(input.read(buffer)).isEqualTo(-1); - input.position(0); - assertThat(input.position()).isEqualTo(0); - assertThat(input.read(buffer)).isEqualTo(5); - assertThat(input.position()).isEqualTo(5); - assertThat(new String(buffer.array(), UTF_8)).isEqualTo("hello"); + try (FileSystem fs = helper.forBucket("lol")) { + Path path = fs.getPath("/cat"); + Files.write(path, "helloworld".getBytes(UTF_8)); + try (SeekableByteChannel input = Files.newByteChannel(path)) { + ByteBuffer buffer = ByteBuffer.allocate(5); + input.position(5); + assertThat(input.position()).isEqualTo(5); + assertThat(input.read(buffer)).isEqualTo(5); + assertThat(input.position()).isEqualTo(10); + assertThat(new String(buffer.array(), UTF_8)).isEqualTo("world"); + buffer.rewind(); + assertThat(input.read(buffer)).isEqualTo(-1); + input.position(0); + assertThat(input.position()).isEqualTo(0); + assertThat(input.read(buffer)).isEqualTo(5); + assertThat(input.position()).isEqualTo(5); + assertThat(new String(buffer.array(), UTF_8)).isEqualTo("hello"); + } } } @Test public void testNewByteChannelRead_seekBeyondSize_reportsEofOnNextRead() throws IOException { - Path path = Paths.get(URI.create("gs://lol/cat")); - Files.write(path, "hellocat".getBytes(UTF_8)); - try (SeekableByteChannel input = Files.newByteChannel(path)) { - ByteBuffer buffer = ByteBuffer.allocate(5); - input.position(10); - assertThat(input.read(buffer)).isEqualTo(-1); - input.position(11); - assertThat(input.read(buffer)).isEqualTo(-1); - assertThat(input.size()).isEqualTo(8); + try (FileSystem fs = helper.forBucket("lol")) { + Path path = fs.getPath("/cat"); + Files.write(path, "hellocat".getBytes(UTF_8)); + try (SeekableByteChannel input = Files.newByteChannel(path)) { + ByteBuffer buffer = ByteBuffer.allocate(5); + input.position(10); + assertThat(input.read(buffer)).isEqualTo(-1); + input.position(11); + assertThat(input.read(buffer)).isEqualTo(-1); + assertThat(input.size()).isEqualTo(8); + } } } @Test public void testNewByteChannelRead_trailingSlash() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/wat/")); - thrown.expect(CloudStoragePseudoDirectoryException.class); - Files.newByteChannel(path); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/wat/"); + thrown.expect(CloudStoragePseudoDirectoryException.class); + Files.newByteChannel(path); + } } @Test public void testNewByteChannelRead_notFound() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/wednesday")); - thrown.expect(NoSuchFileException.class); - Files.newByteChannel(path); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/wednesday"); + thrown.expect(NoSuchFileException.class); + Files.newByteChannel(path); + } } @Test public void testNewByteChannelWrite() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/tests")); - try (SeekableByteChannel output = Files.newByteChannel(path, WRITE)) { - assertThat(output.position()).isEqualTo(0); - assertThat(output.size()).isEqualTo(0); - ByteBuffer buffer = ByteBuffer.wrap("filec".getBytes(UTF_8)); - assertThat(output.write(buffer)).isEqualTo(5); - assertThat(output.position()).isEqualTo(5); - assertThat(output.size()).isEqualTo(5); - buffer = ByteBuffer.wrap("onten".getBytes(UTF_8)); - assertThat(output.write(buffer)).isEqualTo(5); - assertThat(output.position()).isEqualTo(10); - assertThat(output.size()).isEqualTo(10); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/tests"); + try (SeekableByteChannel output = Files.newByteChannel(path, WRITE)) { + assertThat(output.position()).isEqualTo(0); + assertThat(output.size()).isEqualTo(0); + ByteBuffer buffer = ByteBuffer.wrap("filec".getBytes(UTF_8)); + assertThat(output.write(buffer)).isEqualTo(5); + assertThat(output.position()).isEqualTo(5); + assertThat(output.size()).isEqualTo(5); + buffer = ByteBuffer.wrap("onten".getBytes(UTF_8)); + assertThat(output.write(buffer)).isEqualTo(5); + assertThat(output.position()).isEqualTo(10); + assertThat(output.size()).isEqualTo(10); + } + assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo("fileconten"); } - assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo("fileconten"); } @Test public void testNewInputStream() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/wat")); - Files.write(path, SINGULARITY.getBytes(UTF_8)); - try (InputStream input = Files.newInputStream(path)) { - byte[] data = new byte[SINGULARITY.getBytes(UTF_8).length]; - input.read(data); - assertThat(new String(data, UTF_8)).isEqualTo(SINGULARITY); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/wat"); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + try (InputStream input = Files.newInputStream(path)) { + byte[] data = new byte[SINGULARITY.getBytes(UTF_8).length]; + input.read(data); + assertThat(new String(data, UTF_8)).isEqualTo(SINGULARITY); + } } } @Test public void testNewInputStream_trailingSlash() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/wat/")); - thrown.expect(CloudStoragePseudoDirectoryException.class); - try (InputStream input = Files.newInputStream(path)) { - input.read(); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/wat/"); + thrown.expect(CloudStoragePseudoDirectoryException.class); + try (InputStream input = Files.newInputStream(path)) { + input.read(); + } } } @Test public void testNewInputStream_notFound() throws IOException { - Path path = Paths.get(URI.create("gs://cry/wednesday")); - thrown.expect(NoSuchFileException.class); - try (InputStream input = Files.newInputStream(path)) { - input.read(); + try (FileSystem fs = helper.forBucket("cry")) { + Path path = fs.getPath("/wednesday"); + thrown.expect(NoSuchFileException.class); + try (InputStream input = Files.newInputStream(path)) { + input.read(); + } } } @Test public void testNewOutputStream() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/wat")); - Files.write(path, SINGULARITY.getBytes(UTF_8)); - try (OutputStream output = Files.newOutputStream(path)) { - output.write(SINGULARITY.getBytes(UTF_8)); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/wat"); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + try (OutputStream output = Files.newOutputStream(path)) { + output.write(SINGULARITY.getBytes(UTF_8)); + } + assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); } - assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); } @Test public void testNewOutputStream_truncateByDefault() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/wat")); - Files.write(path, SINGULARITY.getBytes(UTF_8)); - Files.write(path, "hello".getBytes(UTF_8)); - try (OutputStream output = Files.newOutputStream(path)) { - output.write(SINGULARITY.getBytes(UTF_8)); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/wat"); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + Files.write(path, "hello".getBytes(UTF_8)); + try (OutputStream output = Files.newOutputStream(path)) { + output.write(SINGULARITY.getBytes(UTF_8)); + } + assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); } - assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); } @Test public void testNewOutputStream_truncateExplicitly() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/wat")); - Files.write(path, SINGULARITY.getBytes(UTF_8)); - Files.write(path, "hello".getBytes(UTF_8)); - try (OutputStream output = Files.newOutputStream(path, TRUNCATE_EXISTING)) { - output.write(SINGULARITY.getBytes(UTF_8)); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/wat"); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + Files.write(path, "hello".getBytes(UTF_8)); + try (OutputStream output = Files.newOutputStream(path, TRUNCATE_EXISTING)) { + output.write(SINGULARITY.getBytes(UTF_8)); + } + assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); } - assertThat(new String(Files.readAllBytes(path), UTF_8)).isEqualTo(SINGULARITY); } @Test public void testNewOutputStream_trailingSlash() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/wat/")); - thrown.expect(CloudStoragePseudoDirectoryException.class); - Files.newOutputStream(path); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/wat/"); + thrown.expect(CloudStoragePseudoDirectoryException.class); + Files.newOutputStream(path); + } } @Test public void testNewOutputStream_createNew() throws IOException { - Path path = Paths.get(URI.create("gs://cry/wednesday")); - Files.newOutputStream(path, CREATE_NEW); + try (FileSystem fs = helper.forBucket("cry")) { + Path path = fs.getPath("/wednesday"); + Files.newOutputStream(path, CREATE_NEW); + } } @Test public void testNewOutputStream_createNew_alreadyExists() throws IOException { - Path path = Paths.get(URI.create("gs://cry/wednesday")); - Files.write(path, SINGULARITY.getBytes(UTF_8)); - thrown.expect(FileAlreadyExistsException.class); - Files.newOutputStream(path, CREATE_NEW); + try (FileSystem fs = helper.forBucket("cry")) { + Path path = fs.getPath("/wednesday"); + Files.write(path, SINGULARITY.getBytes(UTF_8)); + thrown.expect(FileAlreadyExistsException.class); + Files.newOutputStream(path, CREATE_NEW); + } } @Test public void testWrite_objectNameWithExtraSlashes_throwsIae() throws IOException { - Path path = Paths.get(URI.create("gs://double/slash//yep")); - thrown.expect(IllegalArgumentException.class); - Files.write(path, FILE_CONTENTS, UTF_8); + try (FileSystem fs = helper.forBucket("double")) { + Path path = fs.getPath("/slash//yep"); + thrown.expect(IllegalArgumentException.class); + Files.write(path, FILE_CONTENTS, UTF_8); + } } @Test public void testWrite_objectNameWithExtraSlashes_canBeNormalized() throws IOException { - try (CloudStorageFileSystem fs = forBucket("greenbean", permitEmptyPathComponents(false))) { + try (CloudStorageFileSystem fs = + helper.forBucket("greenbean", permitEmptyPathComponents(false))) { Path path = fs.getPath("adipose//yep").normalize(); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); @@ -316,7 +354,8 @@ public void testWrite_objectNameWithExtraSlashes_canBeNormalized() throws IOExce @Test public void testWrite_objectNameWithExtraSlashes_permitEmptyPathComponents() throws IOException { - try (CloudStorageFileSystem fs = forBucket("greenbean", permitEmptyPathComponents(true))) { + try (CloudStorageFileSystem fs = + helper.forBucket("greenbean", permitEmptyPathComponents(true))) { Path path = fs.getPath("adipose//yep"); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); @@ -326,16 +365,18 @@ public void testWrite_objectNameWithExtraSlashes_permitEmptyPathComponents() thr @Test public void testWrite_absoluteObjectName_prefixSlashGetsRemoved() throws IOException { - Path path = Paths.get(URI.create("gs://greenbean/adipose/yep")); - Files.write(path, FILE_CONTENTS, UTF_8); - assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); - assertThat(Files.exists(path)).isTrue(); + try (CloudStorageFileSystem fs = helper.forBucket("greenbean")) { + Path path = fs.getPath("/adipose/yep"); + Files.write(path, FILE_CONTENTS, UTF_8); + assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); + assertThat(Files.exists(path)).isTrue(); + } } @Test public void testWrite_absoluteObjectName_disableStrip_slashGetsPreserved() throws IOException { try (CloudStorageFileSystem fs = - forBucket( + helper.forBucket( "greenbean", CloudStorageConfiguration.builder().stripPrefixSlash(false).build())) { Path path = fs.getPath("/adipose/yep"); Files.write(path, FILE_CONTENTS, UTF_8); @@ -346,85 +387,101 @@ public void testWrite_absoluteObjectName_disableStrip_slashGetsPreserved() throw @Test public void testWrite() throws IOException { - Path path = Paths.get(URI.create("gs://greenbean/adipose")); - Files.write(path, FILE_CONTENTS, UTF_8); - assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); + try (FileSystem fs = helper.forBucket("greenbean")) { + Path path = fs.getPath("/adipose"); + Files.write(path, FILE_CONTENTS, UTF_8); + assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); + } } @Test public void testWriteOnClose() throws IOException { - Path path = Paths.get(URI.create("gs://greenbean/adipose")); - try (SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.WRITE)) { - // writing lots of contents to defeat channel-internal buffering. - for (int i = 0; i < 9999; i++) { - for (String s : FILE_CONTENTS) { - chan.write(ByteBuffer.wrap(s.getBytes(UTF_8))); + try (FileSystem fs = helper.forBucket("greenbean")) { + Path path = fs.getPath("/adipose"); + try (SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.WRITE)) { + // writing lots of contents to defeat channel-internal buffering. + for (int i = 0; i < 9999; i++) { + for (String s : FILE_CONTENTS) { + chan.write(ByteBuffer.wrap(s.getBytes(UTF_8))); + } + } + try { + Files.size(path); + // we shouldn't make it to this line. Not using thrown.expect because + // I still want to run a few lines after the exception. + assertThat(false).isTrue(); + } catch (NoSuchFileException nsf) { + // that's what we wanted, we're good. } } - try { - Files.size(path); - // we shouldn't make it to this line. Not using thrown.expect because - // I still want to run a few lines after the exception. - assertThat(false).isTrue(); - } catch (NoSuchFileException nsf) { - // that's what we wanted, we're good. - } + // channel now closed, the file should be there and with the new contents. + assertThat(Files.exists(path)).isTrue(); + assertThat(Files.size(path)).isGreaterThan(100L); } - // channel now closed, the file should be there and with the new contents. - assertThat(Files.exists(path)).isTrue(); - assertThat(Files.size(path)).isGreaterThan(100L); } @Test public void testWrite_trailingSlash() throws IOException { - thrown.expect(CloudStoragePseudoDirectoryException.class); - Files.write(Paths.get(URI.create("gs://greenbean/adipose/")), FILE_CONTENTS, UTF_8); + try (FileSystem fs = helper.forBucket("greenbean")) { + thrown.expect(CloudStoragePseudoDirectoryException.class); + Files.write(fs.getPath("/adipose/"), FILE_CONTENTS, UTF_8); + } } @Test public void testExists() throws IOException { - assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion")))).isFalse(); - Files.write(Paths.get(URI.create("gs://military/fashion")), "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); - assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion")))).isTrue(); + try (FileSystem fs = helper.forBucket("military")) { + assertThat(Files.exists(fs.getPath("/fashion"))).isFalse(); + Files.write(fs.getPath("/fashion"), "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + assertThat(Files.exists(fs.getPath("/fashion"))).isTrue(); + } } @Test - public void testExists_trailingSlash() { - assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion/")))).isTrue(); - assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion/.")))).isTrue(); - assertThat(Files.exists(Paths.get(URI.create("gs://military/fashion/..")))).isTrue(); + public void testExists_trailingSlash() throws IOException { + try (FileSystem fs = helper.forBucket("military")) { + assertThat(Files.exists(fs.getPath("/fashion/"))).isTrue(); + assertThat(Files.exists(fs.getPath("/fashion/."))).isTrue(); + assertThat(Files.exists(fs.getPath("/fashion/.."))).isTrue(); + } } @Test - public void testExists_trailingSlash_disablePseudoDirectories() { - try (CloudStorageFileSystem fs = forBucket("military", usePseudoDirectories(false))) { + public void testExists_trailingSlash_disablePseudoDirectories() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("military", usePseudoDirectories(false))) { assertThat(Files.exists(fs.getPath("fashion/"))).isFalse(); } } @Test public void testDelete() throws IOException { - Files.write(Paths.get(URI.create("gs://love/fashion")), "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); - assertThat(Files.exists(Paths.get(URI.create("gs://love/fashion")))).isTrue(); - Files.delete(Paths.get(URI.create("gs://love/fashion"))); - assertThat(Files.exists(Paths.get(URI.create("gs://love/fashion")))).isFalse(); + try (FileSystem fs = helper.forBucket("love")) { + Files.write(fs.getPath("/fashion"), "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + assertThat(Files.exists(fs.getPath("/fashion"))).isTrue(); + Files.delete(fs.getPath("/fashion")); + assertThat(Files.exists(fs.getPath("/fashion"))).isFalse(); + } } @Test public void testDelete_dotDirNotNormalized_throwsIae() throws IOException { - thrown.expect(IllegalArgumentException.class); - Files.delete(Paths.get(URI.create("gs://love/fly/../passion"))); + try (FileSystem fs = helper.forBucket("love")) { + thrown.expect(IllegalArgumentException.class); + Files.delete(fs.getPath("/fly/../passion")); + } } @Test public void testDelete_trailingSlash() throws IOException { - thrown.expect(CloudStoragePseudoDirectoryException.class); - Files.delete(Paths.get(URI.create("gs://love/passion/"))); + try (FileSystem fs = helper.forBucket("love")) { + thrown.expect(CloudStoragePseudoDirectoryException.class); + Files.delete(fs.getPath("/passion/")); + } } @Test public void testDelete_trailingSlash_disablePseudoDirectories() throws IOException { - try (CloudStorageFileSystem fs = forBucket("pumpkin", usePseudoDirectories(false))) { + try (CloudStorageFileSystem fs = helper.forBucket("pumpkin", usePseudoDirectories(false))) { Path path = fs.getPath("wat/"); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.exists(path)); @@ -435,106 +492,134 @@ public void testDelete_trailingSlash_disablePseudoDirectories() throws IOExcepti @Test public void testDelete_notFound() throws IOException { - thrown.expect(NoSuchFileException.class); - Files.delete(Paths.get(URI.create("gs://loveh/passionehu"))); + try (FileSystem fs = helper.forBucket("loveh")) { + thrown.expect(NoSuchFileException.class); + Files.delete(fs.getPath("/passionehu")); + } } @Test public void testDeleteIfExists() throws IOException { - Files.write(Paths.get(URI.create("gs://love/passionz")), "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); - assertThat(Files.deleteIfExists(Paths.get(URI.create("gs://love/passionz")))).isTrue(); + try (FileSystem fs = helper.forBucket("love")) { + Files.write(fs.getPath("/passionz"), "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + assertThat(Files.deleteIfExists(fs.getPath("/passionz"))).isTrue(); + } } @Test public void testDeleteIfExists_trailingSlash() throws IOException { - thrown.expect(CloudStoragePseudoDirectoryException.class); - Files.deleteIfExists(Paths.get(URI.create("gs://love/passion/"))); + try (FileSystem fs = helper.forBucket("love")) { + thrown.expect(CloudStoragePseudoDirectoryException.class); + Files.deleteIfExists(fs.getPath("/passion/")); + } } @Test - public void testCopy() throws IOException { - Path source = Paths.get(URI.create("gs://military/fashion.show")); - Path target = Paths.get(URI.create("gs://greenbean/adipose")); - Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); - Files.copy(source, target); - assertThat(new String(Files.readAllBytes(target), UTF_8)).isEqualTo("(✿◕ ‿◕ )ノ"); - assertThat(Files.exists(source)).isTrue(); - assertThat(Files.exists(target)).isTrue(); + public void testCopyAcrossBuckets() throws IOException { + try (FileSystem fsMilitary = helper.forBucket("military"); + FileSystem fsGreenbean = helper.forBucket("greenbean")) { + Path source = fsMilitary.getPath("/fashion.show"); + Path target = fsGreenbean.getPath("/adipose"); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + Files.copy(source, target); + assertThat(new String(Files.readAllBytes(target), UTF_8)).isEqualTo("(✿◕ ‿◕ )ノ"); + assertThat(Files.exists(source)).isTrue(); + assertThat(Files.exists(target)).isTrue(); + } } @Test public void testCopy_sourceMissing_throwsNoSuchFileException() throws IOException { - thrown.expect(NoSuchFileException.class); - Files.copy( - Paths.get(URI.create("gs://military/fashion.show")), - Paths.get(URI.create("gs://greenbean/adipose"))); + try (FileSystem fsMilitary = helper.forBucket("military"); + FileSystem fsGreenbean = helper.forBucket("greenbean")) { + thrown.expect(NoSuchFileException.class); + Files.copy(fsMilitary.getPath("/fashion.show"), fsGreenbean.getPath("/adipose")); + } } @Test public void testCopy_targetExists_throwsFileAlreadyExistsException() throws IOException { - Path source = Paths.get(URI.create("gs://military/fashion.show")); - Path target = Paths.get(URI.create("gs://greenbean/adipose")); - Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); - Files.write(target, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); - thrown.expect(FileAlreadyExistsException.class); - Files.copy(source, target); + try (FileSystem fsMilitary = helper.forBucket("military"); + FileSystem fsGreenbean = helper.forBucket("greenbean")) { + Path source = fsMilitary.getPath("/fashion.show"); + Path target = fsGreenbean.getPath("/adipose"); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + Files.write(target, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + thrown.expect(FileAlreadyExistsException.class); + Files.copy(source, target); + } } @Test public void testCopyReplace_targetExists_works() throws IOException { - Path source = Paths.get(URI.create("gs://military/fashion.show")); - Path target = Paths.get(URI.create("gs://greenbean/adipose")); - Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); - Files.write(target, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); - Files.copy(source, target, REPLACE_EXISTING); + try (FileSystem fsMilitary = helper.forBucket("military"); + FileSystem fsGreenbean = helper.forBucket("greenbean")) { + Path source = fsMilitary.getPath("/fashion.show"); + Path target = fsGreenbean.getPath("/adipose"); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + Files.write(target, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + Files.copy(source, target, REPLACE_EXISTING); + } } @Test public void testCopy_directory_doesNothing() throws IOException { - Path source = Paths.get(URI.create("gs://military/fundir/")); - Path target = Paths.get(URI.create("gs://greenbean/loldir/")); - Files.copy(source, target); + try (FileSystem fs = helper.forBucket("greenbean", usePseudoDirectories(true))) { + Path source = fs.getPath("/fundir/"); + Path target = fs.getPath("/loldir/"); + Files.copy(source, target); + } } @Test public void testCopy_atomic_throwsUnsupported() throws IOException { - Path source = Paths.get(URI.create("gs://military/fashion.show")); - Path target = Paths.get(URI.create("gs://greenbean/adipose")); - Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); - thrown.expect(UnsupportedOperationException.class); - Files.copy(source, target, ATOMIC_MOVE); + try (FileSystem fsMilitary = helper.forBucket("military"); + FileSystem fsGreenbean = helper.forBucket("greenbean")) { + Path source = fsMilitary.getPath("/fashion.show"); + Path target = fsGreenbean.getPath("/adipose"); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + thrown.expect(UnsupportedOperationException.class); + Files.copy(source, target, ATOMIC_MOVE); + } } @Test public void testMove() throws IOException { - Path source = Paths.get(URI.create("gs://military/fashion.show")); - Path target = Paths.get(URI.create("gs://greenbean/adipose")); - Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); - Files.move(source, target); - assertThat(new String(Files.readAllBytes(target), UTF_8)).isEqualTo("(✿◕ ‿◕ )ノ"); - assertThat(Files.exists(source)).isFalse(); - assertThat(Files.exists(target)).isTrue(); + try (FileSystem fs = helper.forBucket("greenbean")) { + Path source = fs.getPath("/fashion.show"); + Path target = fs.getPath("/adipose"); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + Files.move(source, target); + assertThat(new String(Files.readAllBytes(target), UTF_8)).isEqualTo("(✿◕ ‿◕ )ノ"); + assertThat(Files.exists(source)).isFalse(); + assertThat(Files.exists(target)).isTrue(); + } } @Test public void testCreateDirectory() throws IOException { - Path path = Paths.get(URI.create("gs://greenbean/dir/")); - Files.createDirectory(path); - assertThat(Files.exists(path)).isTrue(); + try (FileSystem fsGreenbean = helper.forBucket("greenbean")) { + Path path = fsGreenbean.getPath("/dir/"); + Files.createDirectory(path); + assertThat(Files.exists(path)).isTrue(); + } } @Test public void testMove_atomicMove_notSupported() throws IOException { - Path source = Paths.get(URI.create("gs://military/fashion.show")); - Path target = Paths.get(URI.create("gs://greenbean/adipose")); - Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); - thrown.expect(AtomicMoveNotSupportedException.class); - Files.move(source, target, ATOMIC_MOVE); + try (FileSystem fsMilitary = helper.forBucket("military"); + FileSystem fsGreenbean = helper.forBucket("greenbean")) { + Path source = fsMilitary.getPath("/fashion.show"); + Path target = fsGreenbean.getPath("/adipose"); + Files.write(source, "(✿◕ ‿◕ )ノ".getBytes(UTF_8)); + thrown.expect(AtomicMoveNotSupportedException.class); + Files.move(source, target, ATOMIC_MOVE); + } } @Test public void testIsDirectory() throws IOException { - try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://doodle"))) { + try (FileSystem fs = helper.forBucket("doodle")) { assertThat(Files.isDirectory(fs.getPath(""))).isTrue(); assertThat(Files.isDirectory(fs.getPath("/"))).isTrue(); assertThat(Files.isDirectory(fs.getPath("."))).isTrue(); @@ -547,88 +632,99 @@ public void testIsDirectory() throws IOException { } @Test - public void testIsDirectory_trailingSlash_alwaysTrue() { - assertThat(Files.isDirectory(Paths.get(URI.create("gs://military/fundir/")))).isTrue(); + public void testIsDirectory_trailingSlash_alwaysTrue() throws IOException { + try (FileSystem fs = helper.forBucket("military")) { + assertThat(Files.isDirectory(fs.getPath("/fundir/"))).isTrue(); + } } @Test - public void testIsDirectory_trailingSlash_pseudoDirectoriesDisabled_false() { - try (CloudStorageFileSystem fs = forBucket("doodle", usePseudoDirectories(false))) { + public void testIsDirectory_trailingSlash_pseudoDirectoriesDisabled_false() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle", usePseudoDirectories(false))) { assertThat(Files.isDirectory(fs.getPath("fundir/"))).isFalse(); } } @Test public void testCopy_withCopyAttributes_preservesAttributes() throws IOException { - Path source = Paths.get(URI.create("gs://military/fashion.show")); - Path target = Paths.get(URI.create("gs://greenbean/adipose")); - Files.write( - source, - "(✿◕ ‿◕ )ノ".getBytes(UTF_8), - withMimeType("text/lolcat"), - withCacheControl("public; max-age=666"), - withContentEncoding("foobar"), - withContentDisposition("my-content-disposition"), - withUserMetadata("answer", "42")); - Files.copy(source, target, COPY_ATTRIBUTES); - - CloudStorageFileAttributes attributes = - Files.readAttributes(target, CloudStorageFileAttributes.class); - assertThat(attributes.mimeType()).hasValue("text/lolcat"); - assertThat(attributes.cacheControl()).hasValue("public; max-age=666"); - assertThat(attributes.contentEncoding()).hasValue("foobar"); - assertThat(attributes.contentDisposition()).hasValue("my-content-disposition"); - assertThat(attributes.userMetadata().containsKey("answer")).isTrue(); - assertThat(attributes.userMetadata().get("answer")).isEqualTo("42"); + try (FileSystem fsMilitary = helper.forBucket("military"); + FileSystem fsGreenbean = helper.forBucket("greenbean")) { + Path source = fsMilitary.getPath("/fashion.show"); + Path target = fsGreenbean.getPath("/adipose"); + Files.write( + source, + "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withMimeType("text/lolcat"), + withCacheControl("public; max-age=666"), + withContentEncoding("foobar"), + withContentDisposition("my-content-disposition"), + withUserMetadata("answer", "42")); + Files.copy(source, target, COPY_ATTRIBUTES); + + CloudStorageFileAttributes attributes = + Files.readAttributes(target, CloudStorageFileAttributes.class); + assertThat(attributes.mimeType()).hasValue("text/lolcat"); + assertThat(attributes.cacheControl()).hasValue("public; max-age=666"); + assertThat(attributes.contentEncoding()).hasValue("foobar"); + assertThat(attributes.contentDisposition()).hasValue("my-content-disposition"); + assertThat(attributes.userMetadata().containsKey("answer")).isTrue(); + assertThat(attributes.userMetadata().get("answer")).isEqualTo("42"); + } } @Test public void testCopy_withoutOptions_doesntPreservesAttributes() throws IOException { - Path source = Paths.get(URI.create("gs://military/fashion.show")); - Path target = Paths.get(URI.create("gs://greenbean/adipose")); - Files.write( - source, - "(✿◕ ‿◕ )ノ".getBytes(UTF_8), - withMimeType("text/lolcat"), - withCacheControl("public; max-age=666"), - withUserMetadata("answer", "42")); - Files.copy(source, target); - - CloudStorageFileAttributes attributes = - Files.readAttributes(target, CloudStorageFileAttributes.class); - String mimeType = attributes.mimeType().orNull(); - String cacheControl = attributes.cacheControl().orNull(); - assertThat(mimeType).isNotEqualTo("text/lolcat"); - assertThat(cacheControl).isNull(); - assertThat(attributes.userMetadata().containsKey("answer")).isFalse(); + try (FileSystem fsMilitary = helper.forBucket("military"); + FileSystem fsGreenbean = helper.forBucket("greenbean")) { + Path source = fsMilitary.getPath("/fashion.show"); + Path target = fsGreenbean.getPath("/adipose"); + Files.write( + source, + "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withMimeType("text/lolcat"), + withCacheControl("public; max-age=666"), + withUserMetadata("answer", "42")); + Files.copy(source, target); + + CloudStorageFileAttributes attributes = + Files.readAttributes(target, CloudStorageFileAttributes.class); + String mimeType = attributes.mimeType().orNull(); + String cacheControl = attributes.cacheControl().orNull(); + assertThat(mimeType).isNotEqualTo("text/lolcat"); + assertThat(cacheControl).isNull(); + assertThat(attributes.userMetadata().containsKey("answer")).isFalse(); + } } @Test public void testCopy_overwriteAttributes() throws IOException { - Path source = Paths.get(URI.create("gs://military/fashion.show")); - Path target1 = Paths.get(URI.create("gs://greenbean/adipose")); - Path target2 = Paths.get(URI.create("gs://greenbean/round")); - Files.write( - source, - "(✿◕ ‿◕ )ノ".getBytes(UTF_8), - withMimeType("text/lolcat"), - withCacheControl("public; max-age=666")); - Files.copy(source, target1, COPY_ATTRIBUTES); - Files.copy(source, target2, COPY_ATTRIBUTES, withMimeType("text/palfun")); - - CloudStorageFileAttributes attributes = - Files.readAttributes(target1, CloudStorageFileAttributes.class); - assertThat(attributes.mimeType()).hasValue("text/lolcat"); - assertThat(attributes.cacheControl()).hasValue("public; max-age=666"); - - attributes = Files.readAttributes(target2, CloudStorageFileAttributes.class); - assertThat(attributes.mimeType()).hasValue("text/palfun"); - assertThat(attributes.cacheControl()).hasValue("public; max-age=666"); + try (FileSystem fsMilitary = helper.forBucket("military"); + FileSystem fsGreenbean = helper.forBucket("greenbean")) { + Path source = fsMilitary.getPath("/fashion.show"); + Path target1 = fsGreenbean.getPath("/adipose"); + Path target2 = fsGreenbean.getPath("/round"); + Files.write( + source, + "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withMimeType("text/lolcat"), + withCacheControl("public; max-age=666")); + Files.copy(source, target1, COPY_ATTRIBUTES); + Files.copy(source, target2, COPY_ATTRIBUTES, withMimeType("text/palfun")); + + CloudStorageFileAttributes attributes = + Files.readAttributes(target1, CloudStorageFileAttributes.class); + assertThat(attributes.mimeType()).hasValue("text/lolcat"); + assertThat(attributes.cacheControl()).hasValue("public; max-age=666"); + + attributes = Files.readAttributes(target2, CloudStorageFileAttributes.class); + assertThat(attributes.mimeType()).hasValue("text/palfun"); + assertThat(attributes.cacheControl()).hasValue("public; max-age=666"); + } } @Test public void testNullness() throws IOException, NoSuchMethodException, SecurityException { - try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://blood"))) { + try (FileSystem fs = helper.forBucket("blood")) { NullPointerTester tester = new NullPointerTester(); tester.ignore(CloudStorageFileSystemProvider.class.getMethod("equals", Object.class)); tester.setDefault(URI.class, URI.create("gs://blood")); @@ -643,19 +739,14 @@ public void testNullness() throws IOException, NoSuchMethodException, SecurityEx } @Test - public void testProviderEquals() { - Path path1 = Paths.get(URI.create("gs://bucket/tuesday")); - Path path2 = Paths.get(URI.create("gs://blood/wednesday")); - Path path3 = Paths.get("tmp"); - assertThat(path1.getFileSystem().provider()).isEqualTo(path2.getFileSystem().provider()); - assertThat(path1.getFileSystem().provider()).isNotEqualTo(path3.getFileSystem().provider()); - } - - private static CloudStorageConfiguration permitEmptyPathComponents(boolean value) { - return CloudStorageConfiguration.builder().permitEmptyPathComponents(value).build(); - } - - private static CloudStorageConfiguration usePseudoDirectories(boolean value) { - return CloudStorageConfiguration.builder().usePseudoDirectories(value).build(); + public void testProviderEquals() throws IOException { + try (FileSystem fs1 = helper.forBucket("bucket"); + FileSystem fs2 = helper.forBucket("blood")) { + Path path1 = fs1.getPath("/tuesday"); + Path path2 = fs2.getPath("/wednesday"); + Path path3 = FileSystems.getDefault().getPath("tmp"); + assertThat(path1.getFileSystem().provider()).isEqualTo(path2.getFileSystem().provider()); + assertThat(path1.getFileSystem().provider()).isNotEqualTo(path3.getFileSystem().provider()); + } } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java index aacdb24268c6..dc943a1d63df 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java @@ -21,20 +21,15 @@ import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; -import com.google.gcloud.storage.StorageOptions; -import com.google.gcloud.storage.testing.LocalGcsHelper; +import com.google.gcloud.storage.testing.FakeStorageRpc; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.IOException; -import java.net.URI; import java.nio.file.FileSystem; -import java.nio.file.FileSystems; import java.nio.file.Files; -import java.nio.file.Paths; /** * Unit tests for {@link CloudStorageFileSystem}. @@ -52,14 +47,12 @@ public class CloudStorageFileSystemTest { + "The Heart-ache, and the thousand Natural shocks\n" + "That Flesh is heir to? 'Tis a consummation\n"; - @Before - public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); - } + private final FakeStorageRpc storage = new FakeStorageRpc(true); + private final NioTestHelper helper = new NioTestHelper(storage); @Test public void testGetPath() throws IOException { - try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) { + try (FileSystem fs = helper.forBucket("bucket")) { assertThat(fs.getPath("/angel").toString()).isEqualTo("/angel"); assertThat(fs.getPath("/angel").toUri().toString()).isEqualTo("gs://bucket/angel"); } @@ -67,39 +60,38 @@ public void testGetPath() throws IOException { @Test public void testWrite() throws IOException { - try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) { + try (FileSystem fs = helper.forBucket("bucket")) { Files.write(fs.getPath("/angel"), ALONE.getBytes(UTF_8)); + assertThat(new String(Files.readAllBytes(fs.getPath("/angel")), UTF_8)).isEqualTo(ALONE); } - assertThat(new String(Files.readAllBytes(Paths.get(URI.create("gs://bucket/angel"))), UTF_8)) - .isEqualTo(ALONE); } @Test public void testRead() throws IOException { - Files.write(Paths.get(URI.create("gs://bucket/angel")), ALONE.getBytes(UTF_8)); - try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) { + try (FileSystem fs = helper.forBucket("bucket")) { + Files.write(fs.getPath("/angel"), ALONE.getBytes(UTF_8)); assertThat(new String(Files.readAllBytes(fs.getPath("/angel")), UTF_8)).isEqualTo(ALONE); } } @Test public void testExists_false() throws IOException { - try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://bucket"))) { + try (FileSystem fs = helper.forBucket("bucket")) { assertThat(Files.exists(fs.getPath("/angel"))).isFalse(); } } @Test public void testExists_true() throws IOException { - Files.write(Paths.get(URI.create("gs://bucket/angel")), ALONE.getBytes(UTF_8)); - try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) { + try (FileSystem fs = helper.forBucket("bucket")) { + Files.write(fs.getPath("/angel"), ALONE.getBytes(UTF_8)); assertThat(Files.exists(fs.getPath("/angel"))).isTrue(); } } @Test public void testGetters() throws IOException { - try (FileSystem fs = CloudStorageFileSystem.forBucket("bucket")) { + try (FileSystem fs = helper.forBucket("bucket")) { assertThat(fs.isOpen()).isTrue(); assertThat(fs.isReadOnly()).isFalse(); assertThat(fs.getRootDirectories()).containsExactly(fs.getPath("/")); @@ -111,10 +103,10 @@ public void testGetters() throws IOException { @Test public void testEquals() throws IOException { - try (FileSystem bucket1 = CloudStorageFileSystem.forBucket("bucket"); - FileSystem bucket2 = FileSystems.getFileSystem(URI.create("gs://bucket")); - FileSystem doge1 = CloudStorageFileSystem.forBucket("doge"); - FileSystem doge2 = FileSystems.getFileSystem(URI.create("gs://doge"))) { + try (FileSystem bucket1 = helper.forBucket("bucket"); + FileSystem bucket2 = helper.forBucket("bucket"); + FileSystem doge1 = helper.forBucket("doge"); + FileSystem doge2 = helper.forBucket("doge")) { new EqualsTester() .addEqualityGroup(bucket1, bucket2) .addEqualityGroup(doge1, doge2) @@ -124,12 +116,11 @@ public void testEquals() throws IOException { @Test public void testNullness() throws IOException, NoSuchMethodException, SecurityException { - try (FileSystem fs = FileSystems.getFileSystem(URI.create("gs://bucket"))) { + try (FileSystem fs = helper.forBucket("bucket")) { NullPointerTester tester = new NullPointerTester() .ignore(CloudStorageFileSystem.class.getMethod("equals", Object.class)) - .setDefault(CloudStorageConfiguration.class, CloudStorageConfiguration.DEFAULT) - .setDefault(StorageOptions.class, LocalGcsHelper.options()); + .setDefault(CloudStorageConfiguration.class, CloudStorageConfiguration.getDefault()); tester.testAllPublicStaticMethods(CloudStorageFileSystem.class); tester.testAllPublicInstanceMethods(fs); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java index 411bf64170a1..7afaac0ff503 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java @@ -28,18 +28,16 @@ import com.google.common.testing.NullPointerTester; import com.google.gcloud.storage.Acl; -import com.google.gcloud.storage.testing.LocalGcsHelper; +import com.google.gcloud.storage.testing.FakeStorageRpc; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.IOException; -import java.net.URI; +import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; /** * Unit tests for {@link CloudStorageOptions}. @@ -47,77 +45,89 @@ @RunWith(JUnit4.class) public class CloudStorageOptionsTest { - @Before - public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); - } + private final FakeStorageRpc storage = new FakeStorageRpc(true); + private final NioTestHelper helper = new NioTestHelper(storage); @Test public void testWithoutCaching() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withoutCaching()); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get()) - .isEqualTo("no-cache"); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/path"); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withoutCaching()); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get()) + .isEqualTo("no-cache"); + } } @Test public void testCacheControl() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withCacheControl("potato")); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get()) - .isEqualTo("potato"); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/path"); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withCacheControl("potato")); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).cacheControl().get()) + .isEqualTo("potato"); + } } @Test public void testWithAcl() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/path")); - Acl acl = Acl.of(new Acl.User("king@example.com"), Acl.Role.OWNER); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withAcl(acl)); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).acl().get()) - .contains(acl); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/path"); + Acl acl = Acl.of(new Acl.User("king@example.com"), Acl.Role.OWNER); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withAcl(acl)); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).acl().get()) + .contains(acl); + } } @Test public void testWithContentDisposition() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentDisposition("bubbly fun")); - assertThat( - Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get()) - .isEqualTo("bubbly fun"); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/path"); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentDisposition("bubbly fun")); + assertThat( + Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get()) + .isEqualTo("bubbly fun"); + } } @Test public void testWithContentEncoding() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentEncoding("gzip")); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get()) - .isEqualTo("gzip"); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/path"); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentEncoding("gzip")); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get()) + .isEqualTo("gzip"); + } } @Test public void testWithUserMetadata() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write( - path, - "(✿◕ ‿◕ )ノ".getBytes(UTF_8), - withUserMetadata("nolo", "contendere"), - withUserMetadata("eternal", "sadness")); - assertThat( - Files.readAttributes(path, CloudStorageFileAttributes.class).userMetadata().get("nolo")) - .isEqualTo("contendere"); - assertThat( - Files.readAttributes(path, CloudStorageFileAttributes.class) - .userMetadata() - .get("eternal")) - .isEqualTo("sadness"); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/path"); + Files.write( + path, + "(✿◕ ‿◕ )ノ".getBytes(UTF_8), + withUserMetadata("nolo", "contendere"), + withUserMetadata("eternal", "sadness")); + assertThat( + Files.readAttributes(path, CloudStorageFileAttributes.class).userMetadata().get("nolo")) + .isEqualTo("contendere"); + assertThat( + Files.readAttributes(path, CloudStorageFileAttributes.class) + .userMetadata() + .get("eternal")) + .isEqualTo("sadness"); + } } @Test public void testWithMimeType_string() throws IOException { - Path path = Paths.get(URI.create("gs://bucket/path")); - Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/plain")); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).mimeType().get()) - .isEqualTo("text/plain"); + try (FileSystem fs = helper.forBucket("bucket")) { + Path path = fs.getPath("/path"); + Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withMimeType("text/plain")); + assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).mimeType().get()) + .isEqualTo("text/plain"); + } } @Test diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStoragePathTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStoragePathTest.java index a5c440c23772..e1a6db4bcb69 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStoragePathTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStoragePathTest.java @@ -17,14 +17,15 @@ package com.google.gcloud.storage.contrib.nio; import static com.google.common.truth.Truth.assertThat; -import static com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem.forBucket; +import static com.google.gcloud.storage.contrib.nio.NioTestHelper.permitEmptyPathComponents; +import static com.google.gcloud.storage.contrib.nio.NioTestHelper.stripPrefixSlash; +import static com.google.gcloud.storage.contrib.nio.NioTestHelper.workingDirectory; import com.google.common.collect.Iterables; import com.google.common.testing.EqualsTester; import com.google.common.testing.NullPointerTester; -import com.google.gcloud.storage.testing.LocalGcsHelper; +import com.google.gcloud.storage.testing.FakeStorageRpc; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -32,7 +33,6 @@ import org.junit.runners.JUnit4; import java.io.IOException; -import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; @@ -46,75 +46,74 @@ public class CloudStoragePathTest { @Rule public final ExpectedException thrown = ExpectedException.none(); - @Before - public void before() { - CloudStorageFileSystemProvider.setGCloudOptions(LocalGcsHelper.options()); - } + private final FakeStorageRpc storage = new FakeStorageRpc(true); + private final NioTestHelper helper = new NioTestHelper(storage); @Test - public void testCreate_neverRemoveExtraSlashes() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testCreate_neverRemoveExtraSlashes() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("lol//cat").toString()).isEqualTo("lol//cat"); assertThat((Object) fs.getPath("lol//cat")).isEqualTo(fs.getPath("lol//cat")); } } @Test - public void testCreate_preservesTrailingSlash() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testCreate_preservesTrailingSlash() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("lol/cat/").toString()).isEqualTo("lol/cat/"); assertThat((Object) fs.getPath("lol/cat/")).isEqualTo(fs.getPath("lol/cat/")); } } @Test - public void testGetGcsFilename_empty_notAllowed() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testGetGcsFilename_empty_notAllowed() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); fs.getPath("").getBlobId(); } } @Test - public void testGetGcsFilename_stripsPrefixSlash() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testGetGcsFilename_stripsPrefixSlash() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("/hi").getBlobId().name()).isEqualTo("hi"); } } @Test - public void testGetGcsFilename_overrideStripPrefixSlash_doesntStripPrefixSlash() { - try (CloudStorageFileSystem fs = forBucket("doodle", stripPrefixSlash(false))) { + public void testGetGcsFilename_overrideStripPrefixSlash_doesntStripPrefixSlash() + throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle", stripPrefixSlash(false))) { assertThat(fs.getPath("/hi").getBlobId().name()).isEqualTo("/hi"); } } @Test - public void testGetGcsFilename_extraSlashes_throwsIae() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testGetGcsFilename_extraSlashes_throwsIae() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); fs.getPath("a//b").getBlobId().name(); } } @Test - public void testGetGcsFilename_overridepermitEmptyPathComponents() { - try (CloudStorageFileSystem fs = forBucket("doodle", permitEmptyPathComponents(true))) { + public void testGetGcsFilename_overridepermitEmptyPathComponents() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle", permitEmptyPathComponents(true))) { assertThat(fs.getPath("a//b").getBlobId().name()).isEqualTo("a//b"); } } @Test - public void testGetGcsFilename_freaksOutOnExtraSlashesAndDotDirs() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testGetGcsFilename_freaksOutOnExtraSlashesAndDotDirs() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); fs.getPath("a//b/..").getBlobId().name(); } } @Test - public void testNameCount() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testNameCount() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("").getNameCount()).isEqualTo(1); assertThat(fs.getPath("/").getNameCount()).isEqualTo(0); assertThat(fs.getPath("/hi/").getNameCount()).isEqualTo(1); @@ -124,8 +123,8 @@ public void testNameCount() { } @Test - public void testGetName() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testGetName() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("").getName(0).toString()).isEqualTo(""); assertThat(fs.getPath("/hi").getName(0).toString()).isEqualTo("hi"); assertThat(fs.getPath("hi/there").getName(1).toString()).isEqualTo("there"); @@ -133,24 +132,24 @@ public void testGetName() { } @Test - public void testGetName_negative_throwsIae() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testGetName_negative_throwsIae() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); fs.getPath("angel").getName(-1); } } @Test - public void testGetName_overflow_throwsIae() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testGetName_overflow_throwsIae() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); fs.getPath("angel").getName(1); } } @Test - public void testIterator() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testIterator() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(Iterables.get(fs.getPath("/dog/mog"), 0).toString()).isEqualTo("dog"); assertThat(Iterables.get(fs.getPath("/dog/mog"), 1).toString()).isEqualTo("mog"); assertThat(Iterables.size(fs.getPath("/"))).isEqualTo(0); @@ -160,8 +159,8 @@ public void testIterator() { } @Test - public void testNormalize() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testNormalize() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("/").normalize().toString()).isEqualTo("/"); assertThat(fs.getPath("a/x/../b/x/..").normalize().toString()).isEqualTo("a/b/"); assertThat(fs.getPath("/x/x/../../♡").normalize().toString()).isEqualTo("/♡"); @@ -170,38 +169,38 @@ public void testNormalize() { } @Test - public void testNormalize_dot_becomesBlank() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testNormalize_dot_becomesBlank() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("").normalize().toString()).isEqualTo(""); assertThat(fs.getPath(".").normalize().toString()).isEqualTo(""); } } @Test - public void testNormalize_trailingSlash_isPreserved() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testNormalize_trailingSlash_isPreserved() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("o/").normalize().toString()).isEqualTo("o/"); } } @Test - public void testNormalize_doubleDot_becomesBlank() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testNormalize_doubleDot_becomesBlank() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("..").normalize().toString()).isEqualTo(""); assertThat(fs.getPath("../..").normalize().toString()).isEqualTo(""); } } @Test - public void testNormalize_extraSlashes_getRemoved() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testNormalize_extraSlashes_getRemoved() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("//life///b/good//").normalize().toString()).isEqualTo("/life/b/good/"); } } @Test - public void testToRealPath_hasDotDir_throwsIae() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testToRealPath_hasDotDir_throwsIae() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { fs.getPath("a/hi./b").toRealPath(); fs.getPath("a/.hi/b").toRealPath(); thrown.expect(IllegalArgumentException.class); @@ -211,8 +210,8 @@ public void testToRealPath_hasDotDir_throwsIae() { } @Test - public void testToRealPath_hasDotDotDir_throwsIae() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testToRealPath_hasDotDotDir_throwsIae() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { fs.getPath("a/hi../b").toRealPath(); fs.getPath("a/..hi/b").toRealPath(); thrown.expect(IllegalArgumentException.class); @@ -222,8 +221,8 @@ public void testToRealPath_hasDotDotDir_throwsIae() { } @Test - public void testToRealPath_extraSlashes_throwsIae() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testToRealPath_extraSlashes_throwsIae() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("extra slashes"); fs.getPath("a//b").toRealPath(); @@ -231,16 +230,17 @@ public void testToRealPath_extraSlashes_throwsIae() { } @Test - public void testToRealPath_overridePermitEmptyPathComponents_extraSlashes_slashesRemain() { - try (CloudStorageFileSystem fs = forBucket("doodle", permitEmptyPathComponents(true))) { + public void testToRealPath_overridePermitEmptyPathComponents_extraSlashes_slashesRemain() + throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle", permitEmptyPathComponents(true))) { assertThat(fs.getPath("/life///b/./good/").toRealPath().toString()) .isEqualTo("life///b/./good/"); } } @Test - public void testToRealPath_permitEmptyPathComponents_doesNotNormalize() { - try (CloudStorageFileSystem fs = forBucket("doodle", permitEmptyPathComponents(true))) { + public void testToRealPath_permitEmptyPathComponents_doesNotNormalize() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle", permitEmptyPathComponents(true))) { assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("a"); assertThat(fs.getPath("a//b").toRealPath().toString()).isEqualTo("a//b"); assertThat(fs.getPath("a//./b//..").toRealPath().toString()).isEqualTo("a//./b//.."); @@ -248,45 +248,45 @@ public void testToRealPath_permitEmptyPathComponents_doesNotNormalize() { } @Test - public void testToRealPath_withWorkingDirectory_makesAbsolute() { - try (CloudStorageFileSystem fs = forBucket("doodle", workingDirectory("/lol"))) { + public void testToRealPath_withWorkingDirectory_makesAbsolute() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle", workingDirectory("/lol"))) { assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("lol/a"); } } @Test - public void testToRealPath_disableStripPrefixSlash_makesPathAbsolute() { - try (CloudStorageFileSystem fs = forBucket("doodle", stripPrefixSlash(false))) { + public void testToRealPath_disableStripPrefixSlash_makesPathAbsolute() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle", stripPrefixSlash(false))) { assertThat(fs.getPath("a").toRealPath().toString()).isEqualTo("/a"); assertThat(fs.getPath("/a").toRealPath().toString()).isEqualTo("/a"); } } @Test - public void testToRealPath_trailingSlash_getsPreserved() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testToRealPath_trailingSlash_getsPreserved() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("a/b/").toRealPath().toString()).isEqualTo("a/b/"); } } @Test - public void testNormalize_empty_returnsEmpty() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testNormalize_empty_returnsEmpty() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("").normalize().toString()).isEqualTo(""); } } @Test - public void testNormalize_preserveTrailingSlash() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testNormalize_preserveTrailingSlash() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("a/b/../c/").normalize().toString()).isEqualTo("a/c/"); assertThat(fs.getPath("a/b/./c/").normalize().toString()).isEqualTo("a/b/c/"); } } @Test - public void testGetParent_preserveTrailingSlash() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testGetParent_preserveTrailingSlash() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("a/b/c").getParent().toString()).isEqualTo("a/b/"); assertThat(fs.getPath("a/b/c/").getParent().toString()).isEqualTo("a/b/"); assertThat((Object) fs.getPath("").getParent()).isNull(); @@ -297,16 +297,16 @@ public void testGetParent_preserveTrailingSlash() { } @Test - public void testGetRoot() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testGetRoot() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("/hello").getRoot().toString()).isEqualTo("/"); assertThat((Object) fs.getPath("hello").getRoot()).isNull(); } } @Test - public void testRelativize() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testRelativize() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat( fs.getPath("/foo/bar/lol/cat").relativize(fs.getPath("/foo/a/b/../../c")).toString()) .isEqualTo("../../../a/b/../../c"); @@ -314,8 +314,8 @@ public void testRelativize() { } @Test - public void testRelativize_providerMismatch() { - try (CloudStorageFileSystem gcs = forBucket("doodle")) { + public void testRelativize_providerMismatch() throws IOException { + try (CloudStorageFileSystem gcs = helper.forBucket("doodle")) { thrown.expect(ProviderMismatchException.class); gcs.getPath("/etc").relativize(FileSystems.getDefault().getPath("/dog")); } @@ -323,63 +323,63 @@ public void testRelativize_providerMismatch() { @Test @SuppressWarnings("ReturnValueIgnored") // testing that an Exception is thrown - public void testRelativize_providerMismatch2() { - try (CloudStorageFileSystem gcs = forBucket("doodle")) { + public void testRelativize_providerMismatch2() throws IOException { + try (CloudStorageFileSystem gcs = helper.forBucket("doodle")) { thrown.expect(ProviderMismatchException.class); gcs.getPath("/dog").relativize(FileSystems.getDefault().getPath("/etc")); } } @Test - public void testResolve() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testResolve() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("/hi").resolve("there").toString()).isEqualTo("/hi/there"); assertThat(fs.getPath("hi").resolve("there").toString()).isEqualTo("hi/there"); } } @Test - public void testResolve_providerMismatch() { - try (CloudStorageFileSystem gcs = forBucket("doodle")) { + public void testResolve_providerMismatch() throws IOException { + try (CloudStorageFileSystem gcs = helper.forBucket("doodle")) { thrown.expect(ProviderMismatchException.class); gcs.getPath("etc").resolve(FileSystems.getDefault().getPath("/dog")); } } @Test - public void testIsAbsolute() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testIsAbsolute() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("/hi").isAbsolute()).isTrue(); assertThat(fs.getPath("hi").isAbsolute()).isFalse(); } } @Test - public void testToAbsolutePath() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testToAbsolutePath() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat((Object) fs.getPath("/hi").toAbsolutePath()).isEqualTo(fs.getPath("/hi")); assertThat((Object) fs.getPath("hi").toAbsolutePath()).isEqualTo(fs.getPath("/hi")); } } @Test - public void testToAbsolutePath_withWorkingDirectory() { - try (CloudStorageFileSystem fs = forBucket("doodle", workingDirectory("/lol"))) { + public void testToAbsolutePath_withWorkingDirectory() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle", workingDirectory("/lol"))) { assertThat(fs.getPath("a").toAbsolutePath().toString()).isEqualTo("/lol/a"); } } @Test - public void testGetFileName() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testGetFileName() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("/hi/there").getFileName().toString()).isEqualTo("there"); assertThat(fs.getPath("military/fashion/show").getFileName().toString()).isEqualTo("show"); } } @Test - public void testCompareTo() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testCompareTo() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("/hi/there").compareTo(fs.getPath("/hi/there"))).isEqualTo(0); assertThat(fs.getPath("/hi/there").compareTo(fs.getPath("/hi/therf"))).isEqualTo(-1); assertThat(fs.getPath("/hi/there").compareTo(fs.getPath("/hi/therd"))).isEqualTo(1); @@ -387,8 +387,8 @@ public void testCompareTo() { } @Test - public void testStartsWith() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testStartsWith() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi/there"))).isTrue(); assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi/therf"))).isFalse(); assertThat(fs.getPath("/hi/there").startsWith(fs.getPath("/hi"))).isTrue(); @@ -400,8 +400,8 @@ public void testStartsWith() { } @Test - public void testEndsWith() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testEndsWith() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("there"))).isTrue(); assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("therf"))).isFalse(); assertThat(fs.getPath("/hi/there").endsWith(fs.getPath("/blag/therf"))).isFalse(); @@ -418,8 +418,8 @@ public void testEndsWith() { @Test public void testResolve_willWorkWithRecursiveCopy() throws IOException { // See: http://stackoverflow.com/a/10068306 - try (FileSystem fsSource = FileSystems.getFileSystem(URI.create("gs://hello")); - FileSystem fsTarget = FileSystems.getFileSystem(URI.create("gs://cat"))) { + try (FileSystem fsSource = helper.forBucket("hello"); + FileSystem fsTarget = helper.forBucket("cat")) { Path targetPath = fsTarget.getPath("/some/folder/"); Path relSrcPath = fsSource.getPath("file.txt"); assertThat((Object) targetPath.resolve(relSrcPath)) @@ -430,8 +430,8 @@ public void testResolve_willWorkWithRecursiveCopy() throws IOException { @Test public void testRelativize_willWorkWithRecursiveCopy() throws IOException { // See: http://stackoverflow.com/a/10068306 - try (FileSystem fsSource = FileSystems.getFileSystem(URI.create("gs://hello")); - FileSystem fsTarget = FileSystems.getFileSystem(URI.create("gs://cat"))) { + try (FileSystem fsSource = helper.forBucket("hello"); + FileSystem fsTarget = helper.forBucket("cat")) { Path targetPath = fsTarget.getPath("/some/folder/"); Path sourcePath = fsSource.getPath("/sloth/"); Path file = fsSource.getPath("/sloth/file.txt"); @@ -441,8 +441,8 @@ public void testRelativize_willWorkWithRecursiveCopy() throws IOException { } @Test - public void testToFile_unsupported() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testToFile_unsupported() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { Path path = fs.getPath("/lol"); thrown.expect(UnsupportedOperationException.class); path.toFile(); @@ -450,8 +450,8 @@ public void testToFile_unsupported() { } @Test - public void testEquals() { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testEquals() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { new EqualsTester() // These are obviously equal. .addEqualityGroup(fs.getPath("/hello/cat"), fs.getPath("/hello/cat")) @@ -466,8 +466,8 @@ public void testEquals() { } @Test - public void testEquals_currentDirectoryIsTakenIntoConsideration() { - try (CloudStorageFileSystem fs = forBucket("doodle", workingDirectory("/hello"))) { + public void testEquals_currentDirectoryIsTakenIntoConsideration() throws IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle", workingDirectory("/hello"))) { new EqualsTester() .addEqualityGroup(fs.getPath("cat"), fs.getPath("/hello/cat")) .addEqualityGroup(fs.getPath(""), fs.getPath("/hello")) @@ -476,8 +476,8 @@ public void testEquals_currentDirectoryIsTakenIntoConsideration() { } @Test - public void testNullness() throws NoSuchMethodException, SecurityException { - try (CloudStorageFileSystem fs = forBucket("doodle")) { + public void testNullness() throws NoSuchMethodException, SecurityException, IOException { + try (CloudStorageFileSystem fs = helper.forBucket("doodle")) { NullPointerTester tester = new NullPointerTester(); tester.ignore(CloudStoragePath.class.getMethod("equals", Object.class)); tester.setDefault(Path.class, fs.getPath("sup")); @@ -485,16 +485,4 @@ public void testNullness() throws NoSuchMethodException, SecurityException { tester.testAllPublicInstanceMethods(fs.getPath("sup")); } } - - private static CloudStorageConfiguration stripPrefixSlash(boolean value) { - return CloudStorageConfiguration.builder().stripPrefixSlash(value).build(); - } - - private static CloudStorageConfiguration permitEmptyPathComponents(boolean value) { - return CloudStorageConfiguration.builder().permitEmptyPathComponents(value).build(); - } - - private static CloudStorageConfiguration workingDirectory(String value) { - return CloudStorageConfiguration.builder().workingDirectory(value).build(); - } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannelTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannelTest.java index be6cf58e24a8..82940095d972 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannelTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageReadChannelTest.java @@ -22,14 +22,12 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import com.google.gcloud.ReadChannel; import com.google.gcloud.storage.Blob; import com.google.gcloud.storage.BlobId; -import com.google.gcloud.storage.BlobInfo; import com.google.gcloud.storage.Storage; import org.junit.Before; diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/NioTestHelper.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/NioTestHelper.java new file mode 100644 index 000000000000..703038f6a9ba --- /dev/null +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/NioTestHelper.java @@ -0,0 +1,63 @@ +package com.google.gcloud.storage.contrib.nio; + +import com.google.gcloud.spi.ServiceRpcFactory; +import com.google.gcloud.storage.Storage; +import com.google.gcloud.storage.StorageOptions; +import com.google.gcloud.storage.spi.StorageRpc; + +/** + * Helper for creating NIO file system instances without using the SPI. + * + *

There's no way to create perfect isolation between unit tests when using the Java SPI, because + * it stores a list of loaded provider instances. We instead rely on integration tests to test our + * usage of the SPI. + */ +final class NioTestHelper { + + // Within a unit test we use the same provider for multiple file systems. Having a different + // provider instance causes the behavior of many operations, such as Files.copy, to change + // dramatically. + private final CloudStorageFileSystemProvider provider; + + NioTestHelper(StorageRpc storage) { + this.provider = new CloudStorageFileSystemProvider(makeStorage(storage)); + } + + CloudStorageFileSystem forBucket(String bucket) { + return forBucket(bucket, CloudStorageConfiguration.getDefault()); + } + + CloudStorageFileSystem forBucket(String bucket, CloudStorageConfiguration config) { + return new CloudStorageFileSystem(provider, config, bucket); + } + + private static Storage makeStorage(final StorageRpc storageRpc) { + return StorageOptions.builder() + .projectId("dummy-project-for-testing") + .serviceRpcFactory( + new ServiceRpcFactory() { + @Override + public StorageRpc create(StorageOptions options) { + return storageRpc; + } + }) + .build() + .service(); + } + + static CloudStorageConfiguration stripPrefixSlash(boolean value) { + return CloudStorageConfiguration.builder().stripPrefixSlash(value).build(); + } + + static CloudStorageConfiguration permitEmptyPathComponents(boolean value) { + return CloudStorageConfiguration.builder().permitEmptyPathComponents(value).build(); + } + + static CloudStorageConfiguration workingDirectory(String value) { + return CloudStorageConfiguration.builder().workingDirectory(value).build(); + } + + static CloudStorageConfiguration usePseudoDirectories(boolean value) { + return CloudStorageConfiguration.builder().usePseudoDirectories(value).build(); + } +} diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java index b874a467f962..ee163f74f712 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java @@ -33,20 +33,19 @@ import javax.annotation.concurrent.NotThreadSafe; /** - * A bare-bones in-memory implementation of Storage, meant for testing. - * See LocalGcsHelper. - * - * This class is NOT thread-safe. + * Bare-bones in-memory implementation of {@link Storage} for testing. */ @NotThreadSafe -public class FakeStorageRpc implements StorageRpc { +public final class FakeStorageRpc implements StorageRpc { // fullname -> metadata - Map stuff = new HashMap<>(); + private final Map stuff = new HashMap<>(); + // fullname -> contents - Map contents = new HashMap<>(); + private final Map contents = new HashMap<>(); + // fullname -> future contents that will be visible on close. - Map futureContents = new HashMap<>(); + private final Map futureContents = new HashMap<>(); private final boolean throwIfOption; @@ -57,12 +56,6 @@ public FakeStorageRpc(boolean throwIfOption) { this.throwIfOption = throwIfOption; } - // remove all files - void reset() { - stuff = new HashMap<>(); - contents = new HashMap<>(); - } - @Override public Bucket create(Bucket bucket, Map options) throws StorageException { throw new UnsupportedOperationException(); @@ -205,12 +198,9 @@ public String open(StorageObject object, Map options) throws StorageE String key = fullname(object); boolean mustNotExist = false; for (Option option : options.keySet()) { - if (option instanceof StorageRpc.Option) { - // this is a bit of a hack, since we don't implement generations. - if ((StorageRpc.Option) option == Option.IF_GENERATION_MATCH - && ((Long) options.get(option)).longValue() == 0L) { - mustNotExist = true; - } + // this is a bit of a hack, since we don't implement generations. + if (option == Option.IF_GENERATION_MATCH && ((Long) options.get(option)).longValue() == 0L) { + mustNotExist = true; } } if (mustNotExist && stuff.containsKey(key)) { @@ -254,12 +244,10 @@ public RewriteResponse openRewrite(RewriteRequest rewriteRequest) throws Storage boolean mustNotExist = false; for (Option option : rewriteRequest.targetOptions.keySet()) { - if (option instanceof StorageRpc.Option) { - // this is a bit of a hack, since we don't implement generations. - if ((StorageRpc.Option) option == Option.IF_GENERATION_MATCH - && ((Long) rewriteRequest.targetOptions.get(option)).longValue() == 0L) { - mustNotExist = true; - } + // this is a bit of a hack, since we don't implement generations. + if (option == Option.IF_GENERATION_MATCH + && ((Long) rewriteRequest.targetOptions.get(option)).longValue() == 0L) { + mustNotExist = true; } } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/LocalGcsHelper.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/LocalGcsHelper.java deleted file mode 100644 index 7749ad160096..000000000000 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/LocalGcsHelper.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2015 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.gcloud.storage.testing; - -import com.google.gcloud.spi.ServiceRpcFactory; -import com.google.gcloud.storage.spi.StorageRpc; -import com.google.gcloud.storage.StorageOptions; - -/** - * Utility to create an in-memory storage configuration for testing. Storage options can be - * obtained via the {@link #options()} method. Returned options will point to FakeStorageRpc. - */ -public class LocalGcsHelper { - - // used for testing. Will throw if you pass it an option. - private static final FakeStorageRpc instance = new FakeStorageRpc(true); - - /** - * Returns a {@link StorageOptions} that use the static FakeStorageRpc instance, - * and resets it first so you start from a clean slate. - * That instance will throw if you pass it any option. * - */ - public static StorageOptions options() { - instance.reset(); - return StorageOptions.builder() - .projectId("dummy-project-for-testing") - .serviceRpcFactory( - new ServiceRpcFactory() { - @Override - public StorageRpc create(StorageOptions options) { - return instance; - } - }) - .build(); - } - - /** - * Returns a {@link StorageOptions} that creates a new FakeStorageRpc instance - * with the given option. - */ - public static StorageOptions customOptions(final boolean throwIfOptions) { - return StorageOptions.builder() - .projectId("dummy-project-for-testing") - .serviceRpcFactory( - new ServiceRpcFactory() { - @Override - public StorageRpc create(StorageOptions options) { - return new FakeStorageRpc(throwIfOptions); - } - }) - .build(); - } - -} From 2ce11cbaae0d1681a6f957b0b8e0a20401cb8374 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 29 Mar 2016 15:45:39 -0400 Subject: [PATCH 2/5] Address review comments --- gcloud-java-contrib/gcloud-java-nio/pom.xml | 29 +++++++++--- .../nio/CloudStorageConfiguration.java | 6 ++- .../contrib/nio/CloudStorageFileSystem.java | 44 ++++++++++++------- .../nio/CloudStorageFileSystemProvider.java | 21 ++++----- .../nio/CloudStorageFileAttributesTest.java | 5 ++- .../CloudStorageFileSystemProviderTest.java | 4 +- .../nio/CloudStorageFileSystemTest.java | 3 +- .../contrib/nio/CloudStorageOptionsTest.java | 11 +++-- .../storage/contrib/nio/NioTestHelper.java | 14 +++--- .../storage/contrib/nio/it/ITGcsNio.java | 42 ++++++++---------- 10 files changed, 106 insertions(+), 73 deletions(-) diff --git a/gcloud-java-contrib/gcloud-java-nio/pom.xml b/gcloud-java-contrib/gcloud-java-nio/pom.xml index 0b8af3353eea..099ce0d470e6 100644 --- a/gcloud-java-contrib/gcloud-java-nio/pom.xml +++ b/gcloud-java-contrib/gcloud-java-nio/pom.xml @@ -1,7 +1,6 @@ 4.0.0 - com.google.gcloud gcloud-java-nio jar GCloud Java NIO @@ -37,12 +36,6 @@ javax.inject 1 - - com.google.auto.service - auto-service - 1.0-rc2 - provided - com.google.auto.value auto-value @@ -54,6 +47,28 @@ auto-factory 1.0-beta3 provided + + + com.google.guava + guava + + + + + com.google.auto.service + auto-service + 1.0-rc2 + provided + + + com.google.guava + guava + + + com.google.auto + auto-common + + junit diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java index ad3ed2b98428..80c00b9f0dc1 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageConfiguration.java @@ -36,7 +36,7 @@ public abstract class CloudStorageConfiguration { /** * Returns default GCS NIO configuration. */ - public static CloudStorageConfiguration getDefault() { + public static CloudStorageConfiguration defaultInstance() { return DEFAULT; } @@ -54,9 +54,13 @@ public static Builder builder() { } abstract String workingDirectory(); + abstract boolean permitEmptyPathComponents(); + abstract boolean stripPrefixSlash(); + abstract boolean usePseudoDirectories(); + abstract int blockSize(); /** diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java index 02ec5bc2326c..5ae2ca6aee80 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -38,6 +38,7 @@ import java.nio.file.spi.FileSystemProvider; import java.util.Objects; import java.util.Set; +import java.util.logging.Logger; import javax.annotation.CheckReturnValue; import javax.annotation.concurrent.ThreadSafe; @@ -53,13 +54,15 @@ @ThreadSafe public final class CloudStorageFileSystem extends FileSystem { + private static final Logger logger = Logger.getLogger(CloudStorageFileSystem.class.getName()); + /** * Invokes {@link #forBucket(String, CloudStorageConfiguration)} with - * {@link CloudStorageConfiguration#getDefault()}. + * {@link CloudStorageConfiguration#defaultInstance()}. */ @CheckReturnValue public static CloudStorageFileSystem forBucket(String bucket) { - return forBucket(bucket, CloudStorageConfiguration.getDefault()); + return forBucket(bucket, CloudStorageConfiguration.defaultInstance()); } /** @@ -82,16 +85,26 @@ public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfig checkNotNull(config); checkArgument( !bucket.startsWith(URI_SCHEME + ":"), "Bucket name must not have schema: %s", bucket); - return new CloudStorageFileSystem( - // XXX: This is a kludge to get the provider instance from the SPI. This is necessary since - // the behavior of NIO changes quite a bit if the provider instances aren't the same. - (CloudStorageFileSystemProvider) - Iterables.getOnlyElement( - Iterables.filter( - FileSystemProvider.installedProviders(), - Predicates.instanceOf(CloudStorageFileSystemProvider.class))), - config, - bucket); + return new CloudStorageFileSystem(getProvider(), config, bucket); + } + + private static CloudStorageFileSystemProvider getProvider() { + // XXX: This is a kludge to get the provider instance from the SPI. This is necessary since + // the behavior of NIO changes quite a bit if the provider instances aren't the same. + // If the provider can not be found via the SPI, then we fall back to instantiating it + // ourselves. This should safeguard against situations where the weird provider file + // doesn't find its way into the jar. + FileSystemProvider provider = + Iterables.getOnlyElement( + Iterables.filter( + FileSystemProvider.installedProviders(), + Predicates.instanceOf(CloudStorageFileSystemProvider.class)), + null); + if (provider != null) { + return (CloudStorageFileSystemProvider) provider; + } + logger.warning("Could not find CloudStorageFileSystemProvider via the SPI"); + return new CloudStorageFileSystemProvider(); } public static final String URI_SCHEME = "gs"; @@ -154,10 +167,7 @@ public CloudStoragePath getPath(String first, String... more) { */ @Override public void close() throws IOException { - // TODO(jean-philippe-martin,jart): Synchronously close all active channels associated with this - // FileSystem instance on close, per NIO documentation. But we - // probably shouldn't bother unless a legitimate reason can be - // found to implement this behavior. + // TODO(#809): Synchronously close all channels associated with this FileSystem instance. } /** @@ -207,7 +217,7 @@ public Set supportedFileAttributeViews() { */ @Override public PathMatcher getPathMatcher(String syntaxAndPattern) { - // TODO: Implement me. + // TODO(#813): Implement me. throw new UnsupportedOperationException(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java index 01e5a010568f..60e4d01ae02d 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java @@ -113,11 +113,11 @@ public CloudStorageFileSystem getFileSystem(URI uri) { * Returns Cloud Storage file system, provided a URI with no path. * *

Note: This method should be invoked indirectly via the SPI by calling - * {@link java.nio.file.FileSystems#newFileSystem(URI, Map) FileSystems.newFileSystem()}. However - * we recommend that you don't use the SPI if possible; the recommended approach is to write a - * dependency injection module that calls the statically-linked type-safe version of this method, - * which is: {@link CloudStorageFileSystem#forBucket(String, CloudStorageConfiguration)}. Please - * see that method for further documentation on creating GCS file systems. + * {@link java.nio.file.FileSystems#newFileSystem(URI, Map) FileSystems.newFileSystem()}; however, + * we recommend that you don't use the API if possible. The recommended approach is to write a + * dependency injection module that calls the statically-linked, type-safe version of this method: + * {@link CloudStorageFileSystem#forBucket(String, CloudStorageConfiguration)}. Please see that + * method for further documentation on creating GCS file systems. * * @param uri bucket and current working directory, e.g. {@code gs://bucket} * @param env map of configuration options, whose keys correspond to the method names of @@ -498,9 +498,9 @@ public A readAttributes( @Override public Map readAttributes(Path path, String attributes, LinkOption... options) { - // Java 7 NIO defines at least eleven string attributes we'd want to support - // (eg. BasicFileAttributeView and PosixFileAttributeView), so rather than a partial - // implementation we rely on the other overload for now. + // TODO(#811): Java 7 NIO defines at least eleven string attributes we'd want to support (eg. + // BasicFileAttributeView and PosixFileAttributeView), so rather than a partial + // implementation we rely on the other overload for now. throw new UnsupportedOperationException(); } @@ -532,7 +532,7 @@ public void createDirectory(Path dir, FileAttribute... attrs) { */ @Override public DirectoryStream newDirectoryStream(Path dir, Filter filter) { - // TODO: Implement me. + // TODO(#813): Implement me. throw new UnsupportedOperationException(); } @@ -541,6 +541,7 @@ public DirectoryStream newDirectoryStream(Path dir, Filter f */ @Override public void setAttribute(Path path, String attribute, Object value, LinkOption... options) { + // TODO(#811): Implement me. throw new CloudStorageObjectImmutableException(); } @@ -572,7 +573,7 @@ public String toString() { private IOException asIOException(StorageException oops) { // RPC API can only throw StorageException, but CloudStorageFileSystemProvider // can only throw IOException. Square peg, round hole. - // TODO: research if other codes should be translated similarly. + // TODO(#810): Research if other codes should be translated similarly. if (oops.code() == 404) { return new NoSuchFileException(oops.reason()); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java index deafc6b387f4..a1e82550ec37 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileAttributesTest.java @@ -87,8 +87,9 @@ public void testContentDisposition() throws IOException { Path path = fs.getPath("/water"); Files.write(path, HAPPY, withContentDisposition("crash call")); assertThat( - Files.readAttributes( - path, CloudStorageFileAttributes.class).contentDisposition().get()) + Files.readAttributes(path, CloudStorageFileAttributes.class) + .contentDisposition() + .get()) .isEqualTo("crash call"); } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java index 5c1938e12318..8de6db01e69c 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java @@ -376,8 +376,8 @@ public void testWrite_absoluteObjectName_prefixSlashGetsRemoved() throws IOExcep @Test public void testWrite_absoluteObjectName_disableStrip_slashGetsPreserved() throws IOException { try (CloudStorageFileSystem fs = - helper.forBucket( - "greenbean", CloudStorageConfiguration.builder().stripPrefixSlash(false).build())) { + helper.forBucket( + "greenbean", CloudStorageConfiguration.builder().stripPrefixSlash(false).build())) { Path path = fs.getPath("/adipose/yep"); Files.write(path, FILE_CONTENTS, UTF_8); assertThat(Files.readAllLines(path, UTF_8)).isEqualTo(FILE_CONTENTS); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java index dc943a1d63df..d4774ea429c8 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemTest.java @@ -120,7 +120,8 @@ public void testNullness() throws IOException, NoSuchMethodException, SecurityEx NullPointerTester tester = new NullPointerTester() .ignore(CloudStorageFileSystem.class.getMethod("equals", Object.class)) - .setDefault(CloudStorageConfiguration.class, CloudStorageConfiguration.getDefault()); + .setDefault( + CloudStorageConfiguration.class, CloudStorageConfiguration.defaultInstance()); tester.testAllPublicStaticMethods(CloudStorageFileSystem.class); tester.testAllPublicInstanceMethods(fs); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java index 7afaac0ff503..2f479e68b68c 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageOptionsTest.java @@ -85,7 +85,9 @@ public void testWithContentDisposition() throws IOException { Path path = fs.getPath("/path"); Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentDisposition("bubbly fun")); assertThat( - Files.readAttributes(path, CloudStorageFileAttributes.class).contentDisposition().get()) + Files.readAttributes(path, CloudStorageFileAttributes.class) + .contentDisposition() + .get()) .isEqualTo("bubbly fun"); } } @@ -95,7 +97,8 @@ public void testWithContentEncoding() throws IOException { try (FileSystem fs = helper.forBucket("bucket")) { Path path = fs.getPath("/path"); Files.write(path, "(✿◕ ‿◕ )ノ".getBytes(UTF_8), withContentEncoding("gzip")); - assertThat(Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get()) + assertThat( + Files.readAttributes(path, CloudStorageFileAttributes.class).contentEncoding().get()) .isEqualTo("gzip"); } } @@ -110,7 +113,9 @@ public void testWithUserMetadata() throws IOException { withUserMetadata("nolo", "contendere"), withUserMetadata("eternal", "sadness")); assertThat( - Files.readAttributes(path, CloudStorageFileAttributes.class).userMetadata().get("nolo")) + Files.readAttributes(path, CloudStorageFileAttributes.class) + .userMetadata() + .get("nolo")) .isEqualTo("contendere"); assertThat( Files.readAttributes(path, CloudStorageFileAttributes.class) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/NioTestHelper.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/NioTestHelper.java index 703038f6a9ba..4ddabe557e50 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/NioTestHelper.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/NioTestHelper.java @@ -24,7 +24,7 @@ final class NioTestHelper { } CloudStorageFileSystem forBucket(String bucket) { - return forBucket(bucket, CloudStorageConfiguration.getDefault()); + return forBucket(bucket, CloudStorageConfiguration.defaultInstance()); } CloudStorageFileSystem forBucket(String bucket, CloudStorageConfiguration config) { @@ -35,12 +35,12 @@ private static Storage makeStorage(final StorageRpc storageRpc) { return StorageOptions.builder() .projectId("dummy-project-for-testing") .serviceRpcFactory( - new ServiceRpcFactory() { - @Override - public StorageRpc create(StorageOptions options) { - return storageRpc; - } - }) + new ServiceRpcFactory() { + @Override + public StorageRpc create(StorageOptions options) { + return storageRpc; + } + }) .build() .service(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java index e305b7373b58..2b5206b121fd 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java @@ -39,32 +39,29 @@ import java.util.logging.Level; import java.util.logging.Logger; - /** - * Integration test for gcloud-nio. This test actually talks to GCS (you need an account). - * Tests both reading and writing. - * - * You *must* set the GOOGLE_APPLICATION_CREDENTIALS environment variable - * for this test to work. It must contain the name of a local file that contains - * your Service Account JSON Key. + * Integration test for gcloud-nio. * - * The instructions for how to get the Service Account JSON Key are - * at https://cloud.google.com/storage/docs/authentication?hl=en#service_accounts + *

This test actually talks to GCS (you need an account) and tests both reading and writing. You + * *must* set the {@code GOOGLE_APPLICATION_CREDENTIALS} environment variable for this test to work. + * It must contain the name of a local file that contains your Service Account JSON Key. * - * The short version is this: go to cloud.google.com/console, - * select your project, search for "API manager", click "Credentials", - * click "create credentials/service account key", new service account, - * JSON. The contents of the file that's sent to your browsers is your - * "Service Account JSON Key". + *

The instructions for how to get the Service Account JSON Key are + * here. * + *

The short version is this: go to Cloud Console, + * select your project, search for "API manager", click "Credentials", click "create + * credentials/service account key", new service account, JSON. The contents of the file that's sent + * to your browsers is your "Service Account JSON Key". */ @RunWith(JUnit4.class) public class ITGcsNio { - private static final List FILE_CONTENTS = ImmutableList.of( - "Tous les êtres humains naissent libres et égaux en dignité et en droits.", - "Ils sont doués de raison et de conscience et doivent agir ", - "les uns envers les autres dans un esprit de fraternité."); + private static final List FILE_CONTENTS = + ImmutableList.of( + "Tous les êtres humains naissent libres et égaux en dignité et en droits.", + "Ils sont doués de raison et de conscience et doivent agir ", + "les uns envers les autres dans un esprit de fraternité."); private static final Logger log = Logger.getLogger(ITGcsNio.class.getName()); private static final String BUCKET = RemoteGcsHelper.generateBucketName(); @@ -94,9 +91,10 @@ public static void beforeClass() throws IOException { @AfterClass public static void afterClass() throws ExecutionException, InterruptedException { - if (storage != null && !RemoteGcsHelper.forceDelete(storage, BUCKET, 5, TimeUnit.SECONDS) && - log.isLoggable(Level.WARNING)) { - log.log(Level.WARNING, "Deletion of bucket {0} timed out, bucket is not empty", BUCKET); + if (storage != null + && !RemoteGcsHelper.forceDelete(storage, BUCKET, 5, TimeUnit.SECONDS) + && log.isLoggable(Level.WARNING)) { + log.log(Level.WARNING, "Deletion of bucket {0} timed out, bucket is not empty", BUCKET); } } @@ -335,7 +333,6 @@ private String randomSuffix() { return "-" + rnd.nextInt(99999); } - private CloudStorageFileSystem getTestBucket() throws IOException { // in typical usage we use the single-argument version of forBucket // and rely on the user being logged into their project with the @@ -348,5 +345,4 @@ private CloudStorageFileSystem getTestBucket() throws IOException { return CloudStorageFileSystem.forBucket( BUCKET, CloudStorageConfiguration.DEFAULT, storageOptions); } - } From af45b4996293b4cedb591820180440a09ba726fc Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 29 Mar 2016 17:44:56 -0400 Subject: [PATCH 3/5] refactor integration test --- .../contrib/nio/CloudStorageFileSystem.java | 2 +- .../CloudStorageFileSystemProviderTest.java | 6 +- .../storage/contrib/nio/it/ITGcsNio.java | 75 +++++++++---------- 3 files changed, 39 insertions(+), 44 deletions(-) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java index 5ae2ca6aee80..84dd0eb1cd42 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -103,7 +103,7 @@ private static CloudStorageFileSystemProvider getProvider() { if (provider != null) { return (CloudStorageFileSystemProvider) provider; } - logger.warning("Could not find CloudStorageFileSystemProvider via the SPI"); + logger.warning("Could not find CloudStorageFileSystemProvider via SPI"); return new CloudStorageFileSystemProvider(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java index 8de6db01e69c..775b59ae4701 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java @@ -36,6 +36,7 @@ import com.google.common.testing.NullPointerTester; import com.google.gcloud.storage.testing.FakeStorageRpc; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -723,6 +724,7 @@ public void testCopy_overwriteAttributes() throws IOException { } @Test + @Ignore("TODO(jart): Figure out how to re-enable this.") public void testNullness() throws IOException, NoSuchMethodException, SecurityException { try (FileSystem fs = helper.forBucket("blood")) { NullPointerTester tester = new NullPointerTester(); @@ -731,9 +733,7 @@ public void testNullness() throws IOException, NoSuchMethodException, SecurityEx tester.setDefault(Path.class, fs.getPath("and/one")); tester.setDefault(OpenOption.class, StandardOpenOption.CREATE); tester.setDefault(CopyOption.class, StandardCopyOption.COPY_ATTRIBUTES); - // can't do that, setGCloudOptions accepts a null argument. - // TODO(jart): Figure out how to re-enable this. - // tester.testAllPublicStaticMethods(CloudStorageFileSystemProvider.class); + tester.testAllPublicStaticMethods(CloudStorageFileSystemProvider.class); tester.testAllPublicInstanceMethods(new CloudStorageFileSystemProvider()); } } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java index 2b5206b121fd..34b762c5f2b4 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java @@ -8,7 +8,6 @@ import com.google.gcloud.storage.BucketInfo; import com.google.gcloud.storage.Storage; import com.google.gcloud.storage.StorageOptions; -import com.google.gcloud.storage.contrib.nio.CloudStorageConfiguration; import com.google.gcloud.storage.contrib.nio.CloudStorageFileSystem; import com.google.gcloud.storage.testing.RemoteGcsHelper; @@ -24,12 +23,14 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import java.net.URI; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SeekableByteChannel; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.List; @@ -55,6 +56,7 @@ * to your browsers is your "Service Account JSON Key". */ @RunWith(JUnit4.class) +@SuppressWarnings("resource") public class ITGcsNio { private static final List FILE_CONTENTS = @@ -63,28 +65,28 @@ public class ITGcsNio { "Ils sont doués de raison et de conscience et doivent agir ", "les uns envers les autres dans un esprit de fraternité."); - private static final Logger log = Logger.getLogger(ITGcsNio.class.getName()); - private static final String BUCKET = RemoteGcsHelper.generateBucketName(); private static final String SML_FILE = "tmp-test-small-file.txt"; private static final int SML_SIZE = 100; - // it's big, relatively speaking. - private static final String BIG_FILE = "tmp-test-big-file.txt"; - // arbitrary size that's not too round. - private static final int BIG_SIZE = 2 * 1024 * 1024 - 50; + private static final String BIG_FILE = "tmp-test-big-file.txt"; // it's big, relatively speaking. + private static final int BIG_SIZE = 2 * 1024 * 1024 - 50; // arbitrary size that's not too round. private static final String PREFIX = "tmp-test-file"; + + private static final Logger logger = Logger.getLogger(ITGcsNio.class.getName()); + private static final Random random = new Random(); + + private static String bucket; private static Storage storage; private static StorageOptions storageOptions; - private final Random rnd = new Random(); - @BeforeClass - public static void beforeClass() throws IOException { + public static void beforeClass() { + bucket = RemoteGcsHelper.generateBucketName(); // loads the credentials from local disk as par README RemoteGcsHelper gcsHelper = RemoteGcsHelper.create(); storageOptions = gcsHelper.options(); storage = storageOptions.service(); // create and populate test bucket - storage.create(BucketInfo.of(BUCKET)); + storage.create(BucketInfo.of(bucket)); fillFile(storage, SML_FILE, SML_SIZE); fillFile(storage, BIG_FILE, BIG_SIZE); } @@ -92,9 +94,9 @@ public static void beforeClass() throws IOException { @AfterClass public static void afterClass() throws ExecutionException, InterruptedException { if (storage != null - && !RemoteGcsHelper.forceDelete(storage, BUCKET, 5, TimeUnit.SECONDS) - && log.isLoggable(Level.WARNING)) { - log.log(Level.WARNING, "Deletion of bucket {0} timed out, bucket is not empty", BUCKET); + && !RemoteGcsHelper.forceDelete(storage, bucket, 5, TimeUnit.SECONDS) + && logger.isLoggable(Level.WARNING)) { + logger.log(Level.WARNING, "Deletion of bucket {0} timed out, bucket is not empty", bucket); } } @@ -104,27 +106,33 @@ private static byte[] randomContents(int size) { return bytes; } - private static void fillFile(Storage storage, String fname, int size) throws IOException { - storage.create(BlobInfo.builder(BUCKET, fname).build(), randomContents(size)); + private static void fillFile(Storage storage, String fname, int size) { + storage.create(BlobInfo.builder(bucket, fname).build(), randomContents(size)); } @Test - public void testFileExists() throws IOException { - CloudStorageFileSystem testBucket = getTestBucket(); + public void testFileExists() { + CloudStorageFileSystem testBucket = CloudStorageFileSystem.forBucket(bucket); Path path = testBucket.getPath(SML_FILE); assertThat(Files.exists(path)).isTrue(); } + @Test + public void testFileExistsUsingSpi() { + Path path = Paths.get(URI.create(String.format("gs://%s/%s", bucket, SML_FILE))); + assertThat(Files.exists(path)).isTrue(); + } + @Test public void testFileSize() throws IOException { - CloudStorageFileSystem testBucket = getTestBucket(); + CloudStorageFileSystem testBucket = CloudStorageFileSystem.forBucket(bucket); Path path = testBucket.getPath(SML_FILE); assertThat(Files.size(path)).isEqualTo(SML_SIZE); } @Test(timeout = 60_000) public void testReadByteChannel() throws IOException { - CloudStorageFileSystem testBucket = getTestBucket(); + CloudStorageFileSystem testBucket = CloudStorageFileSystem.forBucket(bucket); Path path = testBucket.getPath(SML_FILE); long size = Files.size(path); SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.READ); @@ -150,7 +158,7 @@ public void testReadByteChannel() throws IOException { @Test public void testSeek() throws IOException { - CloudStorageFileSystem testBucket = getTestBucket(); + CloudStorageFileSystem testBucket = CloudStorageFileSystem.forBucket(bucket); Path path = testBucket.getPath(BIG_FILE); int size = BIG_SIZE; byte[] contents = randomContents(size); @@ -179,7 +187,7 @@ public void testSeek() throws IOException { @Test public void testCreate() throws IOException { - CloudStorageFileSystem testBucket = getTestBucket(); + CloudStorageFileSystem testBucket = CloudStorageFileSystem.forBucket(bucket); Path path = testBucket.getPath(PREFIX + randomSuffix()); // file shouldn't exist initially. If it does it's either because it's a leftover // from a previous run (so we should delete the file) @@ -201,7 +209,7 @@ public void testCreate() throws IOException { @Test public void testWrite() throws IOException { - CloudStorageFileSystem testBucket = getTestBucket(); + CloudStorageFileSystem testBucket = CloudStorageFileSystem.forBucket(bucket); Path path = testBucket.getPath(PREFIX + randomSuffix()); // file shouldn't exist initially. If it does it's either because it's a leftover // from a previous run (so we should delete the file) @@ -233,7 +241,7 @@ public void testWrite() throws IOException { @Test public void testCreateAndWrite() throws IOException { - CloudStorageFileSystem testBucket = getTestBucket(); + CloudStorageFileSystem testBucket = CloudStorageFileSystem.forBucket(bucket); Path path = testBucket.getPath(PREFIX + randomSuffix()); // file shouldn't exist initially (see above). assertThat(Files.exists(path)).isFalse(); @@ -262,7 +270,7 @@ public void testCreateAndWrite() throws IOException { @Test public void testWriteOnClose() throws Exception { - CloudStorageFileSystem testBucket = getTestBucket(); + CloudStorageFileSystem testBucket = CloudStorageFileSystem.forBucket(bucket); Path path = testBucket.getPath(PREFIX + randomSuffix()); // file shouldn't exist initially (see above) assertThat(Files.exists(path)).isFalse(); @@ -296,7 +304,7 @@ public void testWriteOnClose() throws Exception { @Test public void testCopy() throws IOException { - CloudStorageFileSystem testBucket = getTestBucket(); + CloudStorageFileSystem testBucket = CloudStorageFileSystem.forBucket(bucket); Path src = testBucket.getPath(SML_FILE); Path dst = testBucket.getPath(PREFIX + randomSuffix()); // file shouldn't exist initially (see above). @@ -330,19 +338,6 @@ private int readFully(ReadableByteChannel chan, byte[] outputBuf) throws IOExcep } private String randomSuffix() { - return "-" + rnd.nextInt(99999); - } - - private CloudStorageFileSystem getTestBucket() throws IOException { - // in typical usage we use the single-argument version of forBucket - // and rely on the user being logged into their project with the - // gcloud tool, and then everything authenticates automagically - // (or we just use paths that start with "gs://" and rely on NIO's magic). - // - // However for the tests we want to be able to run in automated environments - // where we can set environment variables but not necessarily install gcloud - // or run it. That's why we're setting the credentials programmatically. - return CloudStorageFileSystem.forBucket( - BUCKET, CloudStorageConfiguration.DEFAULT, storageOptions); + return "-" + random.nextInt(99999); } } From cb372fddfc9d26b9c983cf32f839b8da3e2bb6dc Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 31 Mar 2016 14:00:51 -0400 Subject: [PATCH 4/5] More review comments --- .../contrib/nio/CloudStorageFileSystem.java | 20 ++++---- .../nio/CloudStorageFileSystemProvider.java | 6 ++- .../CloudStorageFileSystemProviderTest.java | 2 +- .../storage/contrib/nio/it/ITGcsNio.java | 4 +- .../storage/testing/FakeStorageRpc.java | 47 ++++++++++--------- 5 files changed, 43 insertions(+), 36 deletions(-) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java index 84dd0eb1cd42..f5d6c412b26d 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -73,12 +73,11 @@ public static CloudStorageFileSystem forBucket(String bucket) { * with this object. Therefore calling {@link #close()} on the returned value is optional. * *

Note: It is also possible to instantiate this class via Java's Service Provider - * Interface (SPI), e.g. {@code FileSystems.getFileSystem(URI.create("gs://bucket"))}. We - * discourage you from using the SPI if possible, for the reasons documented in + * Interface, e.g. {@code FileSystems.getFileSystem(URI.create("gs://bucket"))}. We discourage you + * from using that if possible, for the reasons documented in * {@link CloudStorageFileSystemProvider#newFileSystem(URI, java.util.Map)} * - * @see #forBucket(String, CloudStorageConfiguration) - * @see java.nio.file.FileSystems#getFileSystem(java.net.URI) + * @see java.nio.file.FileSystems#getFileSystem(URI) */ @CheckReturnValue public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfiguration config) { @@ -89,11 +88,9 @@ public static CloudStorageFileSystem forBucket(String bucket, CloudStorageConfig } private static CloudStorageFileSystemProvider getProvider() { - // XXX: This is a kludge to get the provider instance from the SPI. This is necessary since - // the behavior of NIO changes quite a bit if the provider instances aren't the same. - // If the provider can not be found via the SPI, then we fall back to instantiating it - // ourselves. This should safeguard against situations where the weird provider file - // doesn't find its way into the jar. + // We want to get the provider instance from the service loader if possible. This is important + // because the behavior of NIO (as implemented in classes like java.nio.files.Files) changes + // quite a bit if the provider instances aren't identical. FileSystemProvider provider = Iterables.getOnlyElement( Iterables.filter( @@ -103,6 +100,11 @@ private static CloudStorageFileSystemProvider getProvider() { if (provider != null) { return (CloudStorageFileSystemProvider) provider; } + // If the provider can not be found via the service loader, then we fall back to instantiating + // it ourselves. This should safeguard against needless user frustration in situations where the + // weird provider file created by @AutoService doesn't find its way into the jar. However this + // could lead to unexpected changes in behavior under the rare circumstance that the user + // instantiates multiple instances and uses them together on operations like copy. logger.warning("Could not find CloudStorageFileSystemProvider via SPI"); return new CloudStorageFileSystemProvider(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java index 60e4d01ae02d..a041ccd81f2e 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java @@ -73,6 +73,10 @@ /** * Google Cloud Storage {@link FileSystemProvider} implementation. + * + *

Note: This class should not be used directly ever. This class is instantiated by the + * service loader and called through a standardized API, e.g. {@link java.nio.file.Files}. However + * the javadocs in this class serve as useful documentation for the behavior of the GCS NIO library. */ @Singleton @ThreadSafe @@ -112,7 +116,7 @@ public CloudStorageFileSystem getFileSystem(URI uri) { /** * Returns Cloud Storage file system, provided a URI with no path. * - *

Note: This method should be invoked indirectly via the SPI by calling + *

Note: This method should be invoked indirectly via the service provider by calling * {@link java.nio.file.FileSystems#newFileSystem(URI, Map) FileSystems.newFileSystem()}; however, * we recommend that you don't use the API if possible. The recommended approach is to write a * dependency injection module that calls the statically-linked, type-safe version of this method: diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java index 775b59ae4701..887ac996a1c7 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProviderTest.java @@ -724,7 +724,7 @@ public void testCopy_overwriteAttributes() throws IOException { } @Test - @Ignore("TODO(jart): Figure out how to re-enable this.") + @Ignore("TODO(#830): Figure out how to re-enable this.") public void testNullness() throws IOException, NoSuchMethodException, SecurityException { try (FileSystem fs = helper.forBucket("blood")) { NullPointerTester tester = new NullPointerTester(); diff --git a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java index 34b762c5f2b4..6368d9b4f19d 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/it/ITGcsNio.java @@ -67,8 +67,8 @@ public class ITGcsNio { private static final String SML_FILE = "tmp-test-small-file.txt"; private static final int SML_SIZE = 100; - private static final String BIG_FILE = "tmp-test-big-file.txt"; // it's big, relatively speaking. - private static final int BIG_SIZE = 2 * 1024 * 1024 - 50; // arbitrary size that's not too round. + private static final String BIG_FILE = "tmp-test-big-file.txt"; // it's big, relatively speaking. + private static final int BIG_SIZE = 2 * 1024 * 1024 - 50; // arbitrary size that's not too round. private static final String PREFIX = "tmp-test-file"; private static final Logger logger = Logger.getLogger(ITGcsNio.class.getName()); diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java index ee163f74f712..82bba6f91f95 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java @@ -18,9 +18,11 @@ import com.google.api.services.storage.model.Bucket; import com.google.api.services.storage.model.StorageObject; -import com.google.gcloud.storage.spi.StorageRpc; +import com.google.common.collect.Maps; +import com.google.common.io.ByteStreams; import com.google.gcloud.storage.Storage; import com.google.gcloud.storage.StorageException; +import com.google.gcloud.storage.spi.StorageRpc; import java.io.IOException; import java.io.InputStream; @@ -38,6 +40,8 @@ @NotThreadSafe public final class FakeStorageRpc implements StorageRpc { + // TODO(#829): Generation support. + // fullname -> metadata private final Map stuff = new HashMap<>(); @@ -50,7 +54,7 @@ public final class FakeStorageRpc implements StorageRpc { private final boolean throwIfOption; /** - * @param throwIfOption if true, we throw when given any option. + * @param throwIfOption if true, we throw when given any option */ public FakeStorageRpc(boolean throwIfOption) { this.throwIfOption = throwIfOption; @@ -68,7 +72,7 @@ public StorageObject create(StorageObject object, InputStream content, Map options) throws StorageException public StorageObject get(StorageObject object, Map options) throws StorageException { // we allow the "ID" option because we need to, but then we give a whole answer anyways // because the caller won't mind the extra fields. - if (throwIfOption && !options.isEmpty() && options.size()>1 - && options.keySet().toArray()[0] != Storage.BlobGetOption.fields(Storage.BlobField.ID)) { + if (throwIfOption && !options.isEmpty() && options.size() > 1 + && options.keySet().toArray()[0] + .equals(Storage.BlobGetOption.fields(Storage.BlobField.ID))) { throw new UnsupportedOperationException(); } @@ -196,14 +201,7 @@ public Tuple read( @Override public String open(StorageObject object, Map options) throws StorageException { String key = fullname(object); - boolean mustNotExist = false; - for (Option option : options.keySet()) { - // this is a bit of a hack, since we don't implement generations. - if (option == Option.IF_GENERATION_MATCH && ((Long) options.get(option)).longValue() == 0L) { - mustNotExist = true; - } - } - if (mustNotExist && stuff.containsKey(key)) { + if (wantsObjectToNotExist(options) && stuff.containsKey(key)) { throw new StorageException(new FileAlreadyExistsException(key)); } stuff.put(key, object); @@ -242,17 +240,8 @@ public RewriteResponse openRewrite(RewriteRequest rewriteRequest) throws Storage throw new StorageException(404, "File not found: " + sourceKey); } - boolean mustNotExist = false; - for (Option option : rewriteRequest.targetOptions.keySet()) { - // this is a bit of a hack, since we don't implement generations. - if (option == Option.IF_GENERATION_MATCH - && ((Long) rewriteRequest.targetOptions.get(option)).longValue() == 0L) { - mustNotExist = true; - } - } - String destKey = fullname(rewriteRequest.target); - if (mustNotExist && contents.containsKey(destKey)) { + if (wantsObjectToNotExist(rewriteRequest.targetOptions) && contents.containsKey(destKey)) { throw new StorageException(new FileAlreadyExistsException(destKey)); } @@ -278,4 +267,16 @@ private void potentiallyThrow(Map options) throws UnsupportedOperatio throw new UnsupportedOperationException(); } } + + /** + * Returns {@code true} if {@code options} request that object being created must not exist. + * + *

Quoth Object Versioning + * Docs: If you set the {@code x-goog-if-generation-match} header to {@code 0} when uploading + * an object, Google Cloud Storage only performs the specified request if the object does not + * currently exist. + */ + private static boolean wantsObjectToNotExist(Map options) { + return options.entrySet().contains(Maps.immutableEntry(Option.IF_GENERATION_MATCH, 0L)); + } } From 4ce43d005b3c40c9bf38256667f7cc3cadd1c194 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 5 Apr 2016 20:53:00 -0400 Subject: [PATCH 5/5] more review comments --- .../gcloud/storage/contrib/nio/CloudStorageFileSystem.java | 2 +- .../storage/contrib/nio/CloudStorageFileSystemProvider.java | 4 ++-- .../com/google/gcloud/storage/testing/FakeStorageRpc.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java index f5d6c412b26d..29f2b7e29ca2 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystem.java @@ -105,7 +105,7 @@ private static CloudStorageFileSystemProvider getProvider() { // weird provider file created by @AutoService doesn't find its way into the jar. However this // could lead to unexpected changes in behavior under the rare circumstance that the user // instantiates multiple instances and uses them together on operations like copy. - logger.warning("Could not find CloudStorageFileSystemProvider via SPI"); + logger.warning("Could not find CloudStorageFileSystemProvider via service loader"); return new CloudStorageFileSystemProvider(); } diff --git a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java index a041ccd81f2e..09fde9ef277f 100644 --- a/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java +++ b/gcloud-java-contrib/gcloud-java-nio/src/main/java/com/google/gcloud/storage/contrib/nio/CloudStorageFileSystemProvider.java @@ -74,7 +74,7 @@ /** * Google Cloud Storage {@link FileSystemProvider} implementation. * - *

Note: This class should not be used directly ever. This class is instantiated by the + *

Note: This class should never be used directly. This class is instantiated by the * service loader and called through a standardized API, e.g. {@link java.nio.file.Files}. However * the javadocs in this class serve as useful documentation for the behavior of the GCS NIO library. */ @@ -93,7 +93,7 @@ public final class CloudStorageFileSystemProvider extends FileSystemProvider { /** * Constructs a new instance with the default options. * - *

Note: This should only be called by the Java SPI. Please use + *

Note: This should only be called by the Java service loader. Please use * {@link CloudStorageFileSystem#forBucket(String, CloudStorageConfiguration)} instead. */ public CloudStorageFileSystemProvider() { diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java index 82bba6f91f95..f2f47cbaed5e 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/FakeStorageRpc.java @@ -109,7 +109,7 @@ public StorageObject get(StorageObject object, Map options) throws St // we allow the "ID" option because we need to, but then we give a whole answer anyways // because the caller won't mind the extra fields. if (throwIfOption && !options.isEmpty() && options.size() > 1 - && options.keySet().toArray()[0] + && !options.keySet().toArray()[0] .equals(Storage.BlobGetOption.fields(Storage.BlobField.ID))) { throw new UnsupportedOperationException(); }