From 8ae622161fa3f910b1bd6bd6c8a9fb2b5ab19bc1 Mon Sep 17 00:00:00 2001 From: tfenne Date: Thu, 13 Dec 2018 09:51:48 -0500 Subject: [PATCH 1/3] Converted to using scalatest. --- build.gradle | 13 +- .../core/utils/PathSpecifierUnitTest.java | 358 ------------------ .../test/scala/org/htsjdk/core/UnitTest.scala | 33 ++ .../htsjdk/core/utils/PathSpecifierTest.scala | 198 ++++++++++ .../utils/testDirWith#InName/testTextFile.txt | 2 - data/utils/testTextFile.txt | 2 - 6 files changed, 239 insertions(+), 367 deletions(-) delete mode 100644 core/src/test/java/org/htsjdk/core/utils/PathSpecifierUnitTest.java create mode 100644 core/src/test/scala/org/htsjdk/core/UnitTest.scala create mode 100644 core/src/test/scala/org/htsjdk/core/utils/PathSpecifierTest.scala delete mode 100644 data/utils/testDirWith#InName/testTextFile.txt delete mode 100644 data/utils/testTextFile.txt diff --git a/build.gradle b/build.gradle index 3423174..ab434bc 100644 --- a/build.gradle +++ b/build.gradle @@ -8,6 +8,8 @@ buildscript { plugins { id 'com.github.johnrengelman.shadow' version '2.0.4' id 'java-library' + id 'scala' + id 'com.github.maiflai.scalatest' version '0.21' } //This allows you to build a single shadowJar with the contents of all the @@ -18,6 +20,8 @@ dependencies { subprojects { apply plugin: 'java-library' + apply plugin: 'scala' + apply plugin: 'com.github.maiflai.scalatest' group 'org.htsjdk' version '0.0.1' @@ -33,13 +37,12 @@ subprojects { testCompile "com.google.jimfs:jimfs:1.1" testCompile "org.apache.commons:commons-lang3:3.7" - compile 'commons-io:commons-io:2.5' - } + testCompile "org.scala-lang:scala-library:2.12.4" + testCompile 'org.scalatest:scalatest_2.12:3.0.5' + testRuntime 'org.pegdown:pegdown:1.4.2' - test { - useTestNG{} + compile 'commons-io:commons-io:2.5' } - } project(':cram') { diff --git a/core/src/test/java/org/htsjdk/core/utils/PathSpecifierUnitTest.java b/core/src/test/java/org/htsjdk/core/utils/PathSpecifierUnitTest.java deleted file mode 100644 index 4905d97..0000000 --- a/core/src/test/java/org/htsjdk/core/utils/PathSpecifierUnitTest.java +++ /dev/null @@ -1,358 +0,0 @@ -package org.htsjdk.core.utils; - -//TODO: NAMING: is there any useful distinction between Unit/Integration tests in this repo ? - -import com.google.common.jimfs.Configuration; -import com.google.common.jimfs.Jimfs; -import org.apache.commons.lang3.SystemUtils; -import org.htsjdk.core.api.io.IOResource; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; - -import java.io.*; -import java.nio.file.*; - -public class PathSpecifierUnitTest { - - final static String FS_SEPARATOR = FileSystems.getDefault().getSeparator(); - - @DataProvider - public Object[][] validPathSpecifiers() { - return new Object[][] { - // Paths specifiers that are syntactically valid as either a relative or absolute local file - // name, or as a URI, but which may fail isNIO or isPath - - // input String, expected resulting URI String, expected isNIO, expected isPath - - //******************************** - // Local (non-URI) file references - //******************************** - - {"localFile.bam", "file://" + getCWDAsURIPathString() + "localFile.bam", true, true}, - // absolute reference to a file in the root of the current file system (Windows accepts the "/" as root) - {"/localFile.bam", "file://" + getRootDirectoryAsURIPathString() + "localFile.bam", true, true}, - // absolute reference to a file in the root of the current file system, where root is specified using the - // default FS separator - {FS_SEPARATOR + "localFile.bam", "file://" + getRootDirectoryAsURIPathString() + "localFile.bam", true, true}, - // absolute reference to a file - {FS_SEPARATOR + joinWithFSSeparator("path", "to", "localFile.bam"), - "file://" + getRootDirectoryAsURIPathString() + "path/to/localFile.bam", true, true}, - // absolute reference to a file that contains a URI "excluded" character in the path ("#"), which without - // encoding will be treated as a fragment delimiter - {FS_SEPARATOR + joinWithFSSeparator("project", "gvcf-pcr", "23232_1#1", "1.g.vcf.gz"), - "file://" + getRootDirectoryAsURIPathString() + "project/gvcf-pcr/23232_1%231/1.g.vcf.gz", true, true}, - // relative reference to a file on the local file system - {joinWithFSSeparator("path", "to", "localFile.bam"), - "file://" + getCWDAsURIPathString() + "path/to/localFile.bam", true, true}, - // Windows also accepts "/" as a valid root specifier - {"/", "file://" + getRootDirectoryAsURIPathString(), true, true}, - {".", "file://" + getCWDAsURIPathString() + "./", true, true}, - {"../.", "file://" + getCWDAsURIPathString() + ".././", true, true}, - // an empty path is equivalent to accessing the current directory of the default file system - {"", "file://" + getCWDAsURIPathString(), true, true}, - - //*********************************************************** - // Local file references using a URI with a "file://" scheme. - //*********************************************************** - - {"file:localFile.bam", "file:localFile.bam", true, false}, // absolute, opaque (not hierarchical) - {"file:/localFile.bam", "file:/localFile.bam", true, true}, // absolute, hierarchical - {"file://localFile.bam", "file://localFile.bam", true, false}, // file URLs can't have an authority ("localFile.bam") - {"file:///localFile.bam", "file:///localFile.bam", true, true}, // empty authority - {"file:path/to/localFile.bam", "file:path/to/localFile.bam", true, false}, - {"file:/path/to/localFile.bam", "file:/path/to/localFile.bam", true, true}, - // "path" appears to be an authority, and will be accepted on Windows since this URI will be - // interpreted as a UNC path containing an authority - {"file://path/to/localFile.bam", "file://path/to/localFile.bam", true, SystemUtils.IS_OS_WINDOWS}, - // "localhost" is accepted as a special case authority for "file://" Paths on Windows; but not Linux/Mac - {"file://localhost/to/localFile.bam","file://localhost/to/localFile.bam", true, SystemUtils.IS_OS_WINDOWS}, - {"file:///path/to/localFile.bam", "file:///path/to/localFile.bam", true, true}, // empty authority - - //***************************************************************************** - // Valid URIs which are NOT valid NIO paths (no installed file system provider) - //***************************************************************************** - - {"gs://file.bam", "gs://file.bam", false, false}, - {"gs://bucket/file.bam", "gs://bucket/file.bam", false, false}, - {"gs:///bucket/file.bam", "gs:///bucket/file.bam", false, false}, - {"gs://auth/bucket/file.bam", "gs://auth/bucket/file.bam", false, false}, - {"gs://hellbender/test/resources/", "gs://hellbender/test/resources/", false, false}, - {"gcs://abucket/bucket", "gcs://abucket/bucket", false, false}, - {"gendb://somegdb", "gendb://somegdb", false, false}, - {"chr1:1-100", "chr1:1-100", false, false}, - - //***************************************************************************************** - // Valid URIs which are backed by an installed NIO file system provider), but are which not - // actually resolvable as paths because the scheme-specific part is not valid for one reason - // or another. - //********************************************************************************************** - - // uri must have a path: jimfs:file.bam - {"jimfs:file.bam", "jimfs:file.bam", true, false}, - // java.lang.AssertionError: java.net.URISyntaxException: Expected scheme-specific part at index 6: jimfs: - {"jimfs:/file.bam", "jimfs:/file.bam", true, false}, - // java.lang.AssertionError: uri must have a path: jimfs://file.bam - {"jimfs://file.bam", "jimfs://file.bam", true, false}, - // java.lang.AssertionError: java.net.URISyntaxException: Expected scheme-specific part at index 6: jimfs: - {"jimfs:///file.bam", "jimfs:///file.bam", true, false}, - // java.nio.file.FileSystemNotFoundException: jimfs://root - {"jimfs://root/file.bam","jimfs://root/file.bam", true, false}, - - //*********************************************************************************************** - // References that contain characters that require URI-encoding. If the input string is presented - // without no scheme, it will be be automatically encoded by PathSpecifier, otherwise it - // must already be URI-encoded. - //*********************************************************************************************** - - // relative (non-URI) reference to a file on the local file system that contains a URI fragment delimiter - // is automatically URI-encoded - {joinWithFSSeparator("project", "gvcf-pcr", "23232_1#1", "1.g.vcf.gz"), - "file://" + getCWDAsURIPathString() + "project/gvcf-pcr/23232_1%231/1.g.vcf.gz", true, true}, - // URI reference with fragment delimiter is not automatically URI-encoded - {"file:project/gvcf-pcr/23232_1#1/1.g.vcf.gz", "file:project/gvcf-pcr/23232_1#1/1.g.vcf.gz", true, false}, - {"file:/project/gvcf-pcr/23232_1#1/1.g.vcf.gz", "file:/project/gvcf-pcr/23232_1#1/1.g.vcf.gz", true, false}, - {"file:///project/gvcf-pcr/23232_1%231/1.g.vcf.g", "file:///project/gvcf-pcr/23232_1%231/1.g.vcf.g", true, true}, - }; - } - - @Test(dataProvider = "validPathSpecifiers") - public void testPathSpecifier(final String referenceString, final String expectedURIString, final boolean isNIO, final boolean isPath) { - final IOResource ioResource = new PathSpecifier(referenceString); - Assert.assertNotNull(ioResource); - Assert.assertEquals(ioResource.getURI().toString(), expectedURIString); - } - - @Test(dataProvider = "validPathSpecifiers") - public void testIsNIO(final String referenceString, final String expectedURIString, final boolean isNIO, final boolean isPath) { - final IOResource pathURI = new PathSpecifier(referenceString); - Assert.assertEquals(pathURI.isNIO(), isNIO); - } - - @Test(dataProvider = "validPathSpecifiers") - public void testIsPath(final String referenceString, final String expectedURIString, final boolean isNIO, final boolean isPath) { - final IOResource pathURI = new PathSpecifier(referenceString); - if (isPath) { - Assert.assertEquals(pathURI.isPath(), isPath, pathURI.getToPathFailureReason().orElse("no failure")); - } else { - Assert.assertEquals(pathURI.isPath(), isPath); - } - } - - @Test(dataProvider = "validPathSpecifiers") - public void testToPath(final String referenceString, final String expectedURIString, final boolean isNIO, final boolean isPath) { - final IOResource pathURI = new PathSpecifier(referenceString); - if (isPath) { - final Path path = pathURI.toPath(); - Assert.assertEquals(path != null, isPath, pathURI.getToPathFailureReason().orElse("no failure")); - } else { - Assert.assertEquals(pathURI.isPath(), isPath); - } - } - - @DataProvider - public Object[][] invalidPathSpecifiers() { - return new Object[][] { - // the nul character is rejected on all of the supported platforms in both local - // filenames and URIs, so use it to test PathSpecifier constructor failure on all platforms - {"\0"}, - }; - } - - @Test(dataProvider = "invalidPathSpecifiers", expectedExceptions = {IllegalArgumentException.class}) - public void testPathSpecifierInvalid(final String referenceString) { - new PathSpecifier(referenceString); - } - - @DataProvider - public Object[][] invalidPath() { - return new Object[][] { - // valid references that are not valid as a path - - {"file:/project/gvcf-pcr/23232_1#1/1.g.vcf.gz"}, // not encoded - {"file:project/gvcf-pcr/23232_1#1/1.g.vcf.gz"}, // scheme-specific part is not hierarchical - - // The hadoop file system provider explicitly throws an NPE if no host is specified and HDFS is not - // the default file system - //{"hdfs://nonexistent_authority/path/to/file.bam"}, // unknown authority "nonexistent_authority" - {"hdfs://userinfo@host:80/path/to/file.bam"}, // UnknownHostException "host" - - {"unknownscheme://foobar"}, - {"gendb://adb"}, - {"gcs://abucket/bucket"}, - - // URIs with schemes that are backed by an valid NIO provider, but for which the - // scheme-specific part is not valid. - {"file://nonexistent_authority/path/to/file.bam"}, // unknown authority "nonexistent_authority" - }; - } - - @Test(dataProvider = "invalidPath") - public void testIsPathInvalid(final String invalidPathString) { - final IOResource htsURI = new PathSpecifier(invalidPathString); - Assert.assertFalse(htsURI.isPath()); - } - - @Test(dataProvider = "invalidPath", expectedExceptions = {IllegalArgumentException.class, FileSystemNotFoundException.class}) - public void testToPathInvalid(final String invalidPathString) { - final IOResource htsURI = new PathSpecifier(invalidPathString); - htsURI.toPath(); - } - - @Test - public void testInstalledNonDefaultFileSystem() throws IOException { - // create a jimfs file system and round trip through PathSpecifier/stream - try (FileSystem jimfs = Jimfs.newFileSystem(Configuration.unix())) { - final Path outputPath = jimfs.getPath("alternateFileSystemTest.txt"); - doStreamRoundTrip(outputPath.toUri().toString()); - } - } - - @DataProvider - public Object[][] inputStreamSpecifiers() throws IOException { - return new Object[][]{ - // references that can be resolved to an actual test file that can be read - - // relative (file) reference to a local file - {joinWithFSSeparator("..", "data", "utils", "testTextFile.txt"), "Test file."}, - - // absolute reference to a local file - {getCWDAsFileReference() + FS_SEPARATOR + joinWithFSSeparator("..", "data", "utils", "testTextFile.txt"), "Test file."}, - - // URI reference to a local file, where the path is absolute - {"file://" + getCWDAsURIPathString() + "../data/utils/testTextFile.txt", "Test file."}, - - // reference to a local file with an embedded fragment delimiter ("#") in the name; if the file - // scheme is included, the rest of the path must already be encoded; if no file scheme is - // included, the path is encoded by the PathSpecifier class - {joinWithFSSeparator("..", "data", "utils", "testDirWith#InName", "testTextFile.txt"), "Test file."}, - {"file://" + getCWDAsURIPathString() + "../data/utils/testDirWith%23InName/testTextFile.txt", "Test file."}, - }; - } - - @Test(dataProvider = "inputStreamSpecifiers") - public void testGetInputStream(final String referenceString, final String expectedFileContents) throws IOException { - final IOResource htsURI = new PathSpecifier(referenceString); - - try (final InputStream is = htsURI.getInputStream(); - final DataInputStream dis = new DataInputStream(is)) { - final byte[] actualFileContents = new byte[expectedFileContents.length()]; - dis.readFully(actualFileContents); - - Assert.assertEquals(new String(actualFileContents), expectedFileContents); - } - } - - @DataProvider - public Object[][] outputStreamSpecifiers() throws IOException { - return new Object[][]{ - // output URIs that can be resolved to an actual test file - {IOUtils.createTempPath("testOutputStream", ".txt").toString()}, - {"file://" + getLocalFileAsURIPathString(IOUtils.createTempPath("testOutputStream", ".txt"))}, - }; - } - - @Test(dataProvider = "outputStreamSpecifiers") - public void testGetOutputStream(final String referenceString) throws IOException { - doStreamRoundTrip(referenceString); - } - - @Test - public void testStdIn() throws IOException { - final IOResource htsURI = new PathSpecifier( - SystemUtils.IS_OS_WINDOWS ? - "-" : - "/dev/stdin"); - try (final InputStream is = htsURI.getInputStream(); - final DataInputStream dis = new DataInputStream(is)) { - final byte[] actualFileContents = new byte[0]; - dis.readFully(actualFileContents); - - Assert.assertEquals(new String(actualFileContents), ""); - } - } - - @Test - public void testStdOut() throws IOException { - final IOResource pathURI = new PathSpecifier( - SystemUtils.IS_OS_WINDOWS ? - "-" : - "/dev/stdout"); - try (final OutputStream os = pathURI.getOutputStream(); - final DataOutputStream dos = new DataOutputStream(os)) { - dos.write("some stuff".getBytes()); - } - } - - /** - * Return the string resulting from joining the individual components using the local default - * file system separator. - * - * This is used to create test inputs that are local file references, as would be presented by a - * user on the platform on which these tests are running. - */ - private String joinWithFSSeparator(String... parts) { - return String.join(FileSystems.getDefault().getSeparator(), parts); - } - - private void doStreamRoundTrip(final String referenceString) throws IOException { - final String expectedFileContents = "Test contents"; - - final IOResource pathURI = new PathSpecifier(referenceString); - try (final OutputStream os = pathURI.getOutputStream(); - final DataOutputStream dos = new DataOutputStream(os)) { - dos.write(expectedFileContents.getBytes()); - } - - // read it back in and make sure it matches expected contents - try (final InputStream is = pathURI.getInputStream(); - final DataInputStream dis = new DataInputStream(is)) { - final byte[] actualFileContents = new byte[expectedFileContents.length()]; - dis.readFully(actualFileContents); - - Assert.assertEquals(new String(actualFileContents), expectedFileContents); - } - } - - /** - * Get an absolute reference to the current working directory using local file system syntax and - * the local file system separator. Used to construct valid local, absolute file references as test inputs. - * - * Returns a string of the form '/some/path/.` or `d:\some\path\.` on Windows - */ - private static String getCWDAsFileReference() { - return new File(".").getAbsolutePath(); - } - - /** - * Get the current working directory as a locally valid, hierarchical URI string. Used to - * construct expected URI string values for test inputs that are local file references. - * - * Returns '/some/path/` or `/d:/some/path/` on Windows - */ - private String getCWDAsURIPathString() { - return getLocalFileAsURIPathString(Paths.get(".")); - } - - /** - * Get just the path part of the URI representing the current working directory. Used - * to construct expected URI string values for test inputs that specify a file in the - * root of the local file system. - * - * Returns a string of the form "/" or "/d:/" on Windows. - */ - private String getRootDirectoryAsURIPathString() { - return getLocalFileAsURIPathString(Paths.get(FS_SEPARATOR)); - } - - /** - * Get just the path part of the URI representing a file on the local file system as a normalized - * String. - * - * Returns a string of the form `/some/path' or '/d:/some/path/` on Windows. - */ - private String getLocalFileAsURIPathString(final Path localPath) { - return localPath.toUri().normalize().getPath(); - } - -} diff --git a/core/src/test/scala/org/htsjdk/core/UnitTest.scala b/core/src/test/scala/org/htsjdk/core/UnitTest.scala new file mode 100644 index 0000000..13045de --- /dev/null +++ b/core/src/test/scala/org/htsjdk/core/UnitTest.scala @@ -0,0 +1,33 @@ +package org.htsjdk.core + +import java.nio.file.{Files, Path, Paths} + +import org.scalatest.{FlatSpec, Matchers} + +/** Base class for all Scala tests. */ +class UnitTest extends FlatSpec with Matchers { + /** Make a temporary file that will get cleaned up at the end of testing. */ + protected def makeTempFile(prefix: String, suffix: String): Path = { + val path = Files.createTempFile(prefix, suffix) + path.toFile.deleteOnExit() + path + } + + /** Implicit conversion from Java to Scala iterator. */ + implicit def javaIteratorAsScalaIterator[A](iter: java.util.Iterator[A]): Iterator[A] = { + scala.collection.JavaConverters.asScalaIterator(iter) + } + + /** Implicit conversion from Java to Scala iterable. */ + implicit def javaIterableAsScalaIterable[A](iterable: java.lang.Iterable[A]): Iterable[A] = { + scala.collection.JavaConverters.iterableAsScalaIterable(iterable) + } + + /** Small implicit class to give operator like syntax to paths. */ + implicit class PathLike(path: Path) { + def /(sub: String): Path = path.resolve(sub) + } + + /** Generates a PathLike from a String instead of a Path. */ + implicit def stringToPathLike(path: String): PathLike = PathLike(Paths.get(path)) +} diff --git a/core/src/test/scala/org/htsjdk/core/utils/PathSpecifierTest.scala b/core/src/test/scala/org/htsjdk/core/utils/PathSpecifierTest.scala new file mode 100644 index 0000000..fbc0a4f --- /dev/null +++ b/core/src/test/scala/org/htsjdk/core/utils/PathSpecifierTest.scala @@ -0,0 +1,198 @@ +package org.htsjdk.core.utils + +import java.io.{DataInputStream, DataOutputStream} +import java.nio.file.{FileSystems, Path, Paths} + +import com.google.common.jimfs.{Configuration, Jimfs} +import org.apache.commons.lang3.SystemUtils +import org.htsjdk.core.UnitTest + +class PathSpecifierTest extends UnitTest { + private val Sep = FileSystems.getDefault.getSeparator + private val CwdPath: String = Paths.get(".").normalize().toAbsolutePath.toString + private val CwdUriPath: String = Paths.get(".").toUri.normalize().getPath + private val RootUriPath: String = Paths.get("/").toUri.normalize().getPath + private val StdIn: String = if (SystemUtils.IS_OS_WINDOWS) "-" else "/dev/stdin" + private val StdOut: String = if (SystemUtils.IS_OS_WINDOWS) "-" else "/dev/stdout" + + case class Test(input: String, uri: String, nio: Boolean, path: Boolean) + object Test { + def apply(input: Path, uri: String, nio: Boolean, path: Boolean): Test = Test(input.toString, uri, nio, path) + } + + val ValidInputs = Seq( + Test("localFile.bam", "file://" + CwdUriPath + "localFile.bam", nio = true, path = true), + // absolute reference to a file in the root of the current file system (Windows accepts the "/" as root) + Test("/localFile.bam", "file://" + RootUriPath + "localFile.bam", nio = true, path = true), + // absolute reference to a file in the root of the current file system, where root is specified using the default FS separator + Test(Sep + "localFile.bam", "file://" + RootUriPath + "localFile.bam", nio = true, path = true), + // absolute reference to a file + Test("/path" / "to" / "localFile.bam", "file://" + RootUriPath + "path/to/localFile.bam", nio = true, path = true), + // absolute reference to a file that contains a URI "excluded" character in the path ("#"), which without + // encoding will be treated as a fragment delimiter + Test("/project" / "gvcf-pcr" / "23232_1#1" / "1.g.vcf.gz", "file://" + RootUriPath+ "project/gvcf-pcr/23232_1%231/1.g.vcf.gz", nio = true, path = true), + // relative reference to a file on the local file system + Test("path" / "to" / "localFile.bam", "file://" + CwdUriPath + "path/to/localFile.bam", nio = true, path = true), + // Windows also accepts "/" as a valid root specifier + Test("/", "file://" + RootUriPath, nio = true, path = true), + Test(".", "file://" + CwdUriPath + "./", nio = true, path = true), + Test("../.", "file://" + CwdUriPath + ".././", nio = true, path = true), + // an empty path is equivalent to accessing the current directory of the default file system + Test("", "file://" + CwdUriPath, nio = true, path = true), + + //*********************************************************** + // Local file references using a URI with a "file://" scheme. + //*********************************************************** + + Test("file:localFile.bam", "file:localFile.bam", nio = true, path = false), // absolute, opaque (not hierarchical) + Test("file:/localFile.bam", "file:/localFile.bam", nio = true, path = true), // absolute, hierarchical + Test("file://localFile.bam", "file://localFile.bam", nio = true, path = false), // file URLs can't have an authority ("localFile.bam") + Test("file:///localFile.bam", "file:///localFile.bam", nio = true, path = true), // empty authority + Test("file:path/to/localFile.bam", "file:path/to/localFile.bam", nio = true, path = false), + Test("file:/path/to/localFile.bam", "file:/path/to/localFile.bam", nio = true, path = true), + // "path" appears to be an authority, and will be accepted on Windows since this URI will be + // interpreted as a UNC path containing an authority + Test("file://path/to/localFile.bam", "file://path/to/localFile.bam", nio = true, SystemUtils.IS_OS_WINDOWS), + // "localhost" is accepted as a special case authority for "file://" Paths on Windows; but not Linux/Mac + Test("file://localhost/to/localFile.bam","file://localhost/to/localFile.bam", nio = true, SystemUtils.IS_OS_WINDOWS), + Test("file:///path/to/localFile.bam", "file:///path/to/localFile.bam", nio = true, path = true), // empty authority + + //***************************************************************************** + // Valid URIs which are NOT valid NIO paths (no installed file system provider) + //***************************************************************************** + + Test("gs://file.bam", "gs://file.bam", nio = false, path = false), + Test("gs://bucket/file.bam", "gs://bucket/file.bam", nio = false, path = false), + Test("gs:///bucket/file.bam", "gs:///bucket/file.bam", nio = false, path = false), + Test("gs://auth/bucket/file.bam", "gs://auth/bucket/file.bam", nio = false, path = false), + Test("gs://hellbender/test/resources/", "gs://hellbender/test/resources/", nio = false, path = false), + Test("gcs://abucket/bucket", "gcs://abucket/bucket", nio = false, path = false), + Test("gendb://somegdb", "gendb://somegdb", nio = false, path = false), + Test("chr1:1-100", "chr1:1-100", nio = false, path = false), + + //***************************************************************************************** + // Valid URIs which are backed by an installed NIO file system provider), but are which not + // actually resolvable as paths because the scheme-specific part is not valid for one reason + // or another. + //********************************************************************************************** + + // uri must have a path: jimfs:file.bam + Test("jimfs:file.bam", "jimfs:file.bam", nio = true, path = false), + // java.lang.AssertionError: java.net.URISyntaxException: Expected scheme-specific part at index 6: jimfs: + Test("jimfs:/file.bam", "jimfs:/file.bam", nio = true, path = false), + // java.lang.AssertionError: uri must have a path: jimfs://file.bam + Test("jimfs://file.bam", "jimfs://file.bam", nio = true, path = false), + // java.lang.AssertionError: java.net.URISyntaxException: Expected scheme-specific part at index 6: jimfs: + Test("jimfs:///file.bam", "jimfs:///file.bam", nio = true, path = false), + // java.nio.file.FileSystemNotFoundException: jimfs://root + Test("jimfs://root/file.bam","jimfs://root/file.bam", nio = true, path = false), + + //*********************************************************************************************** + // References that contain characters that require URI-encoding. If the input string is presented + // without no scheme, it will be be automatically encoded by PathSpecifier, otherwise it + // must already be URI-encoded. + //*********************************************************************************************** + + // relative (non-URI) reference to a file on the local file system that contains a URI fragment delimiter + // is automatically URI-encoded + Test("project" / "gvcf-pcr" / "23232_1#1" / "1.g.vcf.gz", "file://" + CwdUriPath + "project/gvcf-pcr/23232_1%231/1.g.vcf.gz", nio = true, path = true), + // URI reference with fragment delimiter is not automatically URI-encoded + Test("file:project/gvcf-pcr/23232_1#1/1.g.vcf.gz", "file:project/gvcf-pcr/23232_1#1/1.g.vcf.gz", nio = true, path = false), + Test("file:/project/gvcf-pcr/23232_1#1/1.g.vcf.gz", "file:/project/gvcf-pcr/23232_1#1/1.g.vcf.gz", nio = true, path = false), + Test("file:///project/gvcf-pcr/23232_1%231/1.g.vcf.g", "file:///project/gvcf-pcr/23232_1%231/1.g.vcf.g", nio = true, path = true), + ) + + "PathSpecifier" should "be able to open an input stream from stdin" in { + val spec = new PathSpecifier(StdIn) + val in = new DataInputStream(spec.getInputStream) + val bytes = new Array[Byte](0) + in.readFully(bytes) + new String(bytes) shouldBe "" + } + + it should "be able to open up an output stream from stdout" in { + val spec = new PathSpecifier(StdOut) + val out = new DataOutputStream(spec.getOutputStream) + out.write("some stuff".getBytes) + } + + ValidInputs.zipWithIndex.foreach { case(in, index) => + it should s"correctly handle input #$index '${in.input}'" in { + val spec = new PathSpecifier(in.input) + spec.getURIString shouldBe in.uri + spec.isNIO shouldBe in.nio + spec.isPath shouldBe in.path + if (in.path) spec.toPath should not be null + } + } + + it should "throw an exception on an invalid input string" in { + an[IllegalArgumentException] shouldBe thrownBy { new PathSpecifier("\u0000")} + } + + Seq( + ("file:/project/gvcf-pcr/23232_1#1/1.g.vcf.gz", "not URL encoded"), + ("file:project/gvcf-pcr/23232_1#1/1.g.vcf.gz", "scheme-specific part is not hierarchical"), + ("hdfs://userinfo@host:80/path/to/file.bam", "hdfs doesn't allow invalid hosts"), + ("unknownscheme://foobar", "URI with an unknown scheme"), + ("gendb://adb", "???"), + ("gcs://abucket/bucket", "valid URI that isn't a path"), + ("file://nonexistent_authority/path/to/file.bam", "file URI with unknown authority nonexistent_authority") + ).foreach { case (input, desc) => + it should s"not see $input as a path because: $desc" in { + val spec = new PathSpecifier(input) + spec.isPath shouldBe false + an[Exception] shouldBe thrownBy { spec.toPath } + } + } + + it should "work with a non-default NIO filesystem" in { + val jimfs = Jimfs.newFileSystem(Configuration.unix) + val outputPath = jimfs.getPath("alternateFileSystemTest.txt") + val contents = "Test contents" + val spec = new PathSpecifier(outputPath.toUri.toString) + val out = new DataOutputStream(spec.getOutputStream) + out.write(contents.getBytes) + out.close() + + // read it back in and make sure it matches expected contents + val in = new DataInputStream(spec.getInputStream) + val bytes = new Array[Byte](contents.length * 2) + val count = in.read(bytes) + in.close() + jimfs.close() + count shouldBe contents.length + new String(bytes, 0, count) shouldBe contents + } + + Seq( + (".." / "data" / "utils" / "testTextFile.txt", "Test file."), // relative (file) reference to a local file + (CwdPath / ".." / "data" / "utils" / "testTextFile.txt", "Test file."), // absolute reference to a local file + (s"file://$CwdUriPath//../data/utils/testTextFile.txt", "Test file."), // URI reference to a local file, where the path is absolute + + // reference to a local file with an embedded fragment delimiter ("#") in the name; if the file + // scheme is included, the rest of the path must already be encoded; if no file scheme is + // included, the path is encoded by the PathSpecifier class + (".." / "data" / "utils" / "testDirWith#InName" / "testTextFile.txt", "Test file."), + (s"file://$CwdUriPath/../data/utils/testDirWith%23InName/testTextFile.txt", "Test file."), + ).foreach { case (input, contents) => + it should s"be able to generate an input stream from $input" in { + val spec = new PathSpecifier(input.toString) + val stream = new DataInputStream(spec.getInputStream) + val bytes = new Array[Byte](contents.length * 2) + val count = stream.read(bytes) + stream.close() + + count shouldBe contents.length + new String(bytes, 0, count) shouldBe contents + } + } + + it should "be able to generate an output stream for valid paths" in { + val path = makeTempFile("some_text_file.", ".txt") + val uri = s"file://${path.toAbsolutePath}" + new PathSpecifier(path.toString).getOutputStream.close() + new PathSpecifier(uri).getOutputStream.close() + } + + } diff --git a/data/utils/testDirWith#InName/testTextFile.txt b/data/utils/testDirWith#InName/testTextFile.txt deleted file mode 100644 index f5d299f..0000000 --- a/data/utils/testDirWith#InName/testTextFile.txt +++ /dev/null @@ -1,2 +0,0 @@ -Test file. - diff --git a/data/utils/testTextFile.txt b/data/utils/testTextFile.txt deleted file mode 100644 index f5d299f..0000000 --- a/data/utils/testTextFile.txt +++ /dev/null @@ -1,2 +0,0 @@ -Test file. - From 9d41665e0d98c23d8a9d99e8a93d06cfafd1b7b9 Mon Sep 17 00:00:00 2001 From: tfenne Date: Wed, 13 Feb 2019 09:46:36 -0700 Subject: [PATCH 2/3] Restoring test data files that were lost. --- data/utils/testDirWith#InName/testTextFile.txt | 2 ++ data/utils/testTextFile.txt | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 data/utils/testDirWith#InName/testTextFile.txt create mode 100644 data/utils/testTextFile.txt diff --git a/data/utils/testDirWith#InName/testTextFile.txt b/data/utils/testDirWith#InName/testTextFile.txt new file mode 100644 index 0000000..f5d299f --- /dev/null +++ b/data/utils/testDirWith#InName/testTextFile.txt @@ -0,0 +1,2 @@ +Test file. + diff --git a/data/utils/testTextFile.txt b/data/utils/testTextFile.txt new file mode 100644 index 0000000..f5d299f --- /dev/null +++ b/data/utils/testTextFile.txt @@ -0,0 +1,2 @@ +Test file. + From 12369628f9668ebd8f2b55a1c03e58802d38837e Mon Sep 17 00:00:00 2001 From: tfenne Date: Wed, 13 Feb 2019 10:10:10 -0700 Subject: [PATCH 3/3] Removed trailing newlines from test data files. --- .../test/scala/org/htsjdk/core/utils/PathSpecifierTest.scala | 3 +-- data/utils/testDirWith#InName/testTextFile.txt | 3 +-- data/utils/testTextFile.txt | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/core/src/test/scala/org/htsjdk/core/utils/PathSpecifierTest.scala b/core/src/test/scala/org/htsjdk/core/utils/PathSpecifierTest.scala index fbc0a4f..cc48d1a 100644 --- a/core/src/test/scala/org/htsjdk/core/utils/PathSpecifierTest.scala +++ b/core/src/test/scala/org/htsjdk/core/utils/PathSpecifierTest.scala @@ -194,5 +194,4 @@ class PathSpecifierTest extends UnitTest { new PathSpecifier(path.toString).getOutputStream.close() new PathSpecifier(uri).getOutputStream.close() } - - } +} diff --git a/data/utils/testDirWith#InName/testTextFile.txt b/data/utils/testDirWith#InName/testTextFile.txt index f5d299f..7b03026 100644 --- a/data/utils/testDirWith#InName/testTextFile.txt +++ b/data/utils/testDirWith#InName/testTextFile.txt @@ -1,2 +1 @@ -Test file. - +Test file. \ No newline at end of file diff --git a/data/utils/testTextFile.txt b/data/utils/testTextFile.txt index f5d299f..7b03026 100644 --- a/data/utils/testTextFile.txt +++ b/data/utils/testTextFile.txt @@ -1,2 +1 @@ -Test file. - +Test file. \ No newline at end of file