forked from googleapis/google-cloud-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add integration test and example jar.
The integration tests show that we can read/write from GCS, and should be able to run from Travis. The example jar demonstrates the other use case for NIO: adding GCS support to a legacy Java 7 program just by adding a jar file to its classpath.
- Loading branch information
1 parent
3137b3d
commit 73b9ef6
Showing
9 changed files
with
503 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
...gcloud-java-nio/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
com.google.gcloud.storage.contrib.nio.CloudStorageFileSystemProvider |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
325 changes: 325 additions & 0 deletions
325
...contrib/gcloud-java-nio/src/test/java/com/google/gcloud/storage/contrib/nio/ITGcsNio.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,325 @@ | ||
package com.google.gcloud.storage.contrib.nio; | ||
|
||
import static com.google.common.truth.Truth.assertThat; | ||
import static java.nio.charset.StandardCharsets.UTF_8; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
import com.google.gcloud.AuthCredentials; | ||
import com.google.gcloud.storage.StorageOptions; | ||
|
||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.ExpectedException; | ||
import org.junit.runner.RunWith; | ||
import org.junit.runners.JUnit4; | ||
|
||
import java.io.BufferedWriter; | ||
import java.io.ByteArrayInputStream; | ||
import java.io.EOFException; | ||
import java.io.IOException; | ||
import java.io.StringBufferInputStream; | ||
import java.io.StringReader; | ||
import java.net.URI; | ||
import java.nio.ByteBuffer; | ||
import java.nio.channels.ReadableByteChannel; | ||
import java.nio.channels.SeekableByteChannel; | ||
import java.nio.file.CopyOption; | ||
import java.nio.file.Files; | ||
import java.nio.file.NoSuchFileException; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.nio.file.StandardCopyOption; | ||
import java.nio.file.StandardOpenOption; | ||
import java.nio.file.attribute.BasicFileAttributes; | ||
import java.util.List; | ||
import java.util.Random; | ||
|
||
|
||
/** | ||
* Integration test for gcloud-nio. This test actually talks to GCS (you need an account). | ||
* Tests both reading and writing. | ||
*/ | ||
@RunWith(JUnit4.class) | ||
public class ITGcsNio { | ||
|
||
// | ||
// The variables below are filled in via environment variables | ||
// (of the same name). | ||
// You *must* set these environment variables before running this test or it will fail. | ||
// The test also relies on you using the gcloud command-line utility to log into | ||
// an account that has read access to the bucket you'll point the tests to. | ||
// | ||
// The instructions for how to get the Service Account JSON Key are | ||
// at https://cloud.google.com/storage/docs/authentication?hl=en#service_accounts | ||
// | ||
// 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 bucket we'll read the files from (e.g. "abucket") | ||
private final String TEST_NIO_BUCKET; | ||
// A first file we can read (can be small) (e.g. "folder/file.txt") | ||
private final String TEST_NIO_SML; | ||
// The size of the small file, in bytes | ||
private final int TEST_NIO_SML_SIZE; | ||
// A second file we can read (can be large) | ||
private final String TEST_NIO_LGE; | ||
// A non-existant file we can write to. We're going to add a suffix to it | ||
// just in case multiple tests are running concurrently. | ||
private final String TEST_NIO_WRITE_PREFIX; | ||
// The project that the bucket belongs to. | ||
// This ought to match the project_id entry in the service account json key below. | ||
private final String TEST_NIO_PROJECT; | ||
// the full text of the service account key. | ||
// e.g. {"type":"service_account","project_id":... | ||
// Naturally you don't want to share this since it gives people access to this project. | ||
private final String TEST_NIO_SERVICE_ACCOUNT_JSON_KEY; | ||
|
||
private final Random rnd; | ||
|
||
private static final List<String> 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é."); | ||
|
||
|
||
|
||
/** | ||
* Sets up the test according to the provided env. vars. | ||
*/ | ||
public ITGcsNio() { | ||
TEST_NIO_BUCKET = getEnv("TEST_NIO_BUCKET"); | ||
TEST_NIO_SML = getEnv("TEST_NIO_SML"); | ||
TEST_NIO_SML_SIZE = Integer.parseInt(getEnv("TEST_NIO_SML_SIZE")); | ||
TEST_NIO_LGE = getEnv("TEST_NIO_LGE"); | ||
TEST_NIO_WRITE_PREFIX = getEnv("TEST_NIO_WRITE_PREFIX"); | ||
TEST_NIO_PROJECT = getEnv("TEST_NIO_PROJECT"); | ||
TEST_NIO_SERVICE_ACCOUNT_JSON_KEY = getEnv("TEST_NIO_SERVICE_ACCOUNT_JSON_KEY"); | ||
rnd = new Random(); | ||
} | ||
|
||
@Test | ||
public void testFileExists() throws IOException { | ||
CloudStorageFileSystem testBucket = getTestBucket(); | ||
Path path = testBucket.getPath(TEST_NIO_SML); | ||
assertThat(Files.exists(path)).isTrue(); | ||
} | ||
|
||
@Test | ||
public void testFileSize() throws IOException { | ||
CloudStorageFileSystem testBucket = getTestBucket(); | ||
Path path = testBucket.getPath(TEST_NIO_SML); | ||
assertThat(Files.size(path)).isEqualTo(TEST_NIO_SML_SIZE); | ||
} | ||
|
||
@Test | ||
public void testReadByteChannel() throws IOException { | ||
CloudStorageFileSystem testBucket = getTestBucket(); | ||
Path path = testBucket.getPath(TEST_NIO_SML); | ||
long size = Files.size(path); | ||
SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.READ); | ||
assertThat(chan.size()).isEqualTo(size); | ||
ByteBuffer buf = ByteBuffer.allocate(1024 * 1024); | ||
int timeout = 1024 * 1024; | ||
int read = 0; | ||
while (chan.isOpen()) { | ||
if (timeout-- <= 0) { | ||
break; | ||
} | ||
int rc = chan.read(buf); | ||
if (rc < 0) { | ||
// EOF | ||
break; | ||
} | ||
buf.clear(); | ||
assertThat(rc).isGreaterThan(0); | ||
read += rc; | ||
assertThat(chan.position() == read); | ||
} | ||
assertThat(read).isEqualTo(size); | ||
} | ||
|
||
@Test | ||
public void testSeek() throws IOException { | ||
CloudStorageFileSystem testBucket = getTestBucket(); | ||
Path path = testBucket.getPath(TEST_NIO_LGE); | ||
long size = Files.size(path); | ||
final ByteBuffer buf1a = ByteBuffer.allocate(100); | ||
final ByteBuffer buf1b = ByteBuffer.allocate(100); | ||
final ByteBuffer buf2a = ByteBuffer.allocate(100); | ||
final ByteBuffer buf2b = ByteBuffer.allocate(100); | ||
SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.READ); | ||
assertThat(chan.size()).isEqualTo(size); | ||
reallyRead(chan, buf1a); | ||
long dest = size / 2; | ||
chan.position(dest); | ||
reallyRead(chan, buf2a); | ||
// now go back and read it again | ||
// (we do 2 locations because 0 is sometimes a special case). | ||
chan.position(0); | ||
reallyRead(chan, buf1b); | ||
chan.position(dest); | ||
reallyRead(chan, buf2b); | ||
// if the two spots in the file have the same contents, then this isn't a good file for this | ||
// test. | ||
assertThat(buf1a.array()).isNotEqualTo(buf2a.array()); | ||
assertThat(buf1a.array()).isEqualTo(buf1b.array()); | ||
assertThat(buf2a.array()).isEqualTo(buf2b.array()); | ||
} | ||
|
||
@Test | ||
public void testCreate() throws IOException { | ||
CloudStorageFileSystem testBucket = getTestBucket(); | ||
Path path = testBucket.getPath(TEST_NIO_WRITE_PREFIX + randomSuffix()); | ||
try { | ||
// 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) | ||
// or because we're misconfigured and pointing to an actually important file | ||
// (so we should absolutely not delete it). | ||
// So if the file's here, don't try to fix it automatically, let the user deal with it. | ||
assertThat(Files.exists(path)).isFalse(); | ||
|
||
Files.createFile(path); | ||
// now it does, and it has size 0. | ||
assertThat(Files.exists(path)).isTrue(); | ||
long size = Files.size(path); | ||
assertThat(size).isEqualTo(0); | ||
} finally { | ||
// let's not leave files around | ||
Files.deleteIfExists(path); | ||
} | ||
} | ||
|
||
@Test | ||
public void testWrite() throws IOException { | ||
CloudStorageFileSystem testBucket = getTestBucket(); | ||
Path path = testBucket.getPath(TEST_NIO_WRITE_PREFIX + randomSuffix()); | ||
try { | ||
// 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) | ||
// or because we're misconfigured and pointing to an actually important file | ||
// (so we should absolutely not delete it). | ||
// So if the file's here, don't try to fix it automatically, let the user deal with it. | ||
assertThat(Files.exists(path)).isFalse(); | ||
|
||
Files.write(path, FILE_CONTENTS, UTF_8); | ||
// now it does. | ||
assertThat(Files.exists(path)).isTrue(); | ||
|
||
// let's check that the contents look OK. | ||
ByteBuffer buf = ByteBuffer.allocate(100); | ||
SeekableByteChannel chan = Files.newByteChannel(path, StandardOpenOption.READ); | ||
reallyRead(chan, buf); | ||
byte[] gotBytes = buf.array(); | ||
byte[] wantBytes = FILE_CONTENTS.get(0).getBytes(UTF_8); | ||
for (int i = 0; i < wantBytes.length; i++) { | ||
assertThat(gotBytes[i] == wantBytes[i]).isTrue(); | ||
} | ||
|
||
} finally { | ||
// let's not leave files around | ||
Files.deleteIfExists(path); | ||
} | ||
} | ||
|
||
@Test | ||
public void testWriteOnClose() throws Exception { | ||
CloudStorageFileSystem testBucket = getTestBucket(); | ||
Path path = testBucket.getPath(TEST_NIO_WRITE_PREFIX + randomSuffix()); | ||
// file shouldn't exist initially (see above) | ||
assertThat(Files.exists(path)).isFalse(); | ||
try { | ||
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. | ||
} | ||
} | ||
// channel now closed, the file should be there and with the new contents. | ||
assertThat(Files.exists(path)).isTrue(); | ||
assertThat(Files.size(path)).isGreaterThan(0L); | ||
} finally { | ||
Files.deleteIfExists(path); | ||
} | ||
} | ||
|
||
@Test | ||
public void testCopy() throws IOException { | ||
CloudStorageFileSystem testBucket = getTestBucket(); | ||
Path src = testBucket.getPath(TEST_NIO_SML); | ||
Path dst = testBucket.getPath(TEST_NIO_WRITE_PREFIX + randomSuffix()); | ||
try { | ||
// file shouldn't exist initially (see above). | ||
assertThat(Files.exists(dst)).isFalse(); | ||
|
||
Files.copy(src, dst); | ||
|
||
assertThat(Files.exists(dst)).isTrue(); | ||
assertThat(Files.size(dst)).isEqualTo(TEST_NIO_SML_SIZE); | ||
} finally { | ||
// let's not leave files around | ||
Files.deleteIfExists(dst); | ||
} | ||
} | ||
|
||
private String getEnv(String name) { | ||
String ret = System.getenv(name); | ||
// we don't want empty strings either because then tests could access places not expected by | ||
// the caller. | ||
if (null == ret || ret.length() == 0) { | ||
throw new RuntimeException(name + " environment variable must be set. Please see the " | ||
+ "instructions for setting up nio's integration tests."); | ||
} | ||
return ret; | ||
} | ||
|
||
private int reallyRead(ReadableByteChannel chan, ByteBuffer buf) throws IOException { | ||
int sofar = 0; | ||
int bytes = buf.remaining(); | ||
while (sofar < bytes) { | ||
int read = chan.read(buf); | ||
if (read < 0) { | ||
throw new EOFException("channel EOF"); | ||
} | ||
sofar += read; | ||
} | ||
return sofar; | ||
} | ||
|
||
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. | ||
StorageOptions storageOptions = StorageOptions.builder() | ||
.authCredentials(AuthCredentials.createForJson(new ByteArrayInputStream | ||
(TEST_NIO_SERVICE_ACCOUNT_JSON_KEY.getBytes(UTF_8)))) | ||
.projectId(TEST_NIO_PROJECT) | ||
.build(); | ||
return CloudStorageFileSystem.forBucket( | ||
TEST_NIO_BUCKET, CloudStorageConfiguration.DEFAULT, storageOptions); | ||
} | ||
|
||
} |
Oops, something went wrong.