diff --git a/biopet-framework/pom.xml b/biopet-framework/pom.xml index 69c3c3ff4f7ac147c87ade7ec0c9a0c790090a33..ac2879e75dea217e25cc42adf3e9b0aae80cd277 100644 --- a/biopet-framework/pom.xml +++ b/biopet-framework/pom.xml @@ -1,36 +1,35 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> - <groupId>nl.lumc.sasc</groupId> - <artifactId>BiopetFramework</artifactId> - <version>0.2.0-DEV</version> - <packaging>jar</packaging> + <groupId>nl.lumc.sasc</groupId> + <artifactId>BiopetFramework</artifactId> + <version>0.2.0-DEV</version> + <packaging>jar</packaging> - <name>BiopetFramework</name> - <url>http://maven.apache.org</url> + <name>BiopetFramework</name> + <url>http://maven.apache.org</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <sting.unpack.phase>prepare-package</sting.unpack.phase> + <sting.shade.phase>package</sting.shade.phase> + <app.main.class>nl.lumc.sasc.biopet.core.BiopetExecutable</app.main.class> + </properties> - <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <sting.unpack.phase>prepare-package</sting.unpack.phase> - <sting.shade.phase>package</sting.shade.phase> - <sting.binary-dist.name>SASC-Pipelines</sting.binary-dist.name> - <app.main.class>nl.lumc.sasc.biopet.core.BiopetExecutable</app.main.class> - </properties> - <repositories> <repository> <id>biojava-maven-repo</id> <name>BioJava repository</name> - <url>http://www.biojava.org/download/maven/</url> + <url>http://www.biojava.org/download/maven/</url> </repository> </repositories> <dependencies> <dependency> - <groupId>org.testng</groupId> - <artifactId>testng</artifactId> - <version>6.8</version> - <scope>test</scope> + <groupId>org.testng</groupId> + <artifactId>testng</artifactId> + <version>6.8</version> + <scope>test</scope> </dependency> <dependency> <groupId>org.scalatest</groupId> @@ -68,9 +67,9 @@ <version>18.0</version> </dependency> <dependency> - <groupId>com.github.scopt</groupId> - <artifactId>scopt_2.10</artifactId> - <version>3.2.0</version> + <groupId>com.github.scopt</groupId> + <artifactId>scopt_2.10</artifactId> + <version>3.2.0</version> </dependency> <dependency> <groupId>org.mockito</groupId> @@ -81,16 +80,16 @@ <build> <resources> <resource> - <directory>src/main/scripts</directory> - <includes> - <include>**/*</include> - </includes> + <directory>src/main/scripts</directory> + <includes> + <include>**/*</include> + </includes> </resource> <resource> - <directory>src/main/resources</directory> - <includes> - <include>**/*</include> - </includes> + <directory>src/main/resources</directory> + <includes> + <include>**/*</include> + </includes> </resource> </resources> <plugins> @@ -102,7 +101,7 @@ <execution> <goals> <goal>revision</goal> - </goals> + </goals> </execution> </executions> @@ -141,9 +140,10 @@ </goals> <configuration> <args> -<!-- <arg>-make:transitive</arg>--> <arg>-dependencyfile</arg> <arg>${project.build.directory}/.scala_dependencies</arg> + <arg>-deprecation</arg> + <arg>-feature</arg> </args> </configuration> </execution> @@ -154,7 +154,7 @@ <artifactId>maven-jar-plugin</artifactId> <version>2.5</version> <configuration> - <archive> + <archive> <manifest> <addDefaultImplementationEntries>true</addDefaultImplementationEntries> <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries> @@ -163,50 +163,50 @@ </configuration> </plugin> <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-shade-plugin</artifactId> - <version>2.3</version> - <configuration> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>2.3</version> + <configuration> <finalName>Biopet-${project.version}-${git.commit.id.abbrev}</finalName> <transformers> - <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> - <manifestEntries> - <Main-Class>${app.main.class}</Main-Class> - <X-Compile-Source-JDK>${maven.compile.source}</X-Compile-Source-JDK> - <X-Compile-Target-JDK>${maven.compile.target}</X-Compile-Target-JDK> - </manifestEntries> - </transformer> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <manifestEntries> + <Main-Class>${app.main.class}</Main-Class> + <X-Compile-Source-JDK>${maven.compile.source}</X-Compile-Source-JDK> + <X-Compile-Target-JDK>${maven.compile.target}</X-Compile-Target-JDK> + </manifestEntries> + </transformer> </transformers> <filters> <filter> - <artifact>org.broadinstitute.gatk:gatk-queue-package-distribution</artifact> - <includes> - <include>*/**</include> - <include>org/simpleframework/**</include> - <include>org/jets3t/**</include> - <include>com/sun/**</include> - <include>org/ggf/**</include> - <include>picard/**</include> - <include>htsjdk/**</include> - <include>javassist/**</include> - <include>com/google/**</include> - <include>org/reflections/</include> - <include>org/jgrapht/**</include> - <include>org/broadinstitute/**</include> - <include>org/apache/**</include> - <include>scala/tools/nsc/reporters/**</include> - </includes> + <artifact>org.broadinstitute.gatk:gatk-queue-package-distribution</artifact> + <includes> + <include>*/**</include> + <include>org/simpleframework/**</include> + <include>org/jets3t/**</include> + <include>com/sun/**</include> + <include>org/ggf/**</include> + <include>picard/**</include> + <include>htsjdk/**</include> + <include>javassist/**</include> + <include>com/google/**</include> + <include>org/reflections/</include> + <include>org/jgrapht/**</include> + <include>org/broadinstitute/**</include> + <include>org/apache/**</include> + <include>scala/tools/nsc/reporters/**</include> + </includes> </filter> </filters> - </configuration> - <executions> - <execution> - <phase>package</phase> - <goals> - <goal>shade</goal> - </goals> - </execution> - </executions> + </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + </execution> + </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> @@ -221,33 +221,35 @@ <artifactId>scalariform-maven-plugin</artifactId> <version>0.1.4</version> <executions> - <execution> - <phase>process-sources</phase> - <goals> - <goal>format</goal> - </goals> - <configuration> - <rewriteArrowSymbols>false</rewriteArrowSymbols> - <alignParameters>true</alignParameters> - <alignSingleLineCaseStatements_maxArrowIndent>40</alignSingleLineCaseStatements_maxArrowIndent> - <alignSingleLineCaseStatements>true</alignSingleLineCaseStatements> - <compactStringConcatenation>false</compactStringConcatenation> - <compactControlReadability>false</compactControlReadability> - <doubleIndentClassDeclaration>false</doubleIndentClassDeclaration> - <formatXml>true</formatXml> - <indentLocalDefs>false</indentLocalDefs> - <indentPackageBlocks>true</indentPackageBlocks> - <indentSpaces>2</indentSpaces> - <placeScaladocAsterisksBeneathSecondAsterisk>false</placeScaladocAsterisksBeneathSecondAsterisk> - <preserveDanglingCloseParenthesis>true</preserveDanglingCloseParenthesis> - <preserveSpaceBeforeArguments>false</preserveSpaceBeforeArguments> - <rewriteArrowSymbols>false</rewriteArrowSymbols> - <spaceBeforeColon>false</spaceBeforeColon> - <spaceInsideBrackets>false</spaceInsideBrackets> - <spaceInsideParentheses>false</spaceInsideParentheses> - <spacesWithinPatternBinders>true</spacesWithinPatternBinders> - </configuration> - </execution> + <execution> + <phase>process-sources</phase> + <goals> + <goal>format</goal> + </goals> + <configuration> + <rewriteArrowSymbols>false</rewriteArrowSymbols> + <alignParameters>true</alignParameters> + <alignSingleLineCaseStatements_maxArrowIndent>40 + </alignSingleLineCaseStatements_maxArrowIndent> + <alignSingleLineCaseStatements>true</alignSingleLineCaseStatements> + <compactStringConcatenation>false</compactStringConcatenation> + <compactControlReadability>false</compactControlReadability> + <doubleIndentClassDeclaration>false</doubleIndentClassDeclaration> + <formatXml>true</formatXml> + <indentLocalDefs>false</indentLocalDefs> + <indentPackageBlocks>true</indentPackageBlocks> + <indentSpaces>2</indentSpaces> + <placeScaladocAsterisksBeneathSecondAsterisk>false + </placeScaladocAsterisksBeneathSecondAsterisk> + <preserveDanglingCloseParenthesis>true</preserveDanglingCloseParenthesis> + <preserveSpaceBeforeArguments>false</preserveSpaceBeforeArguments> + <rewriteArrowSymbols>false</rewriteArrowSymbols> + <spaceBeforeColon>false</spaceBeforeColon> + <spaceInsideBrackets>false</spaceInsideBrackets> + <spaceInsideParentheses>false</spaceInsideParentheses> + <spacesWithinPatternBinders>true</spacesWithinPatternBinders> + </configuration> + </execution> </executions> </plugin> </plugins> diff --git a/biopet-framework/src/main/scala/nl/lumc/sasc/biopet/extensions/Ln.scala b/biopet-framework/src/main/scala/nl/lumc/sasc/biopet/extensions/Ln.scala index 253cab16b78908eee3e236bc323e0ed437449690..cc8bf6afe2ea4f8f4fa4856ccf2047273ec4957a 100644 --- a/biopet-framework/src/main/scala/nl/lumc/sasc/biopet/extensions/Ln.scala +++ b/biopet-framework/src/main/scala/nl/lumc/sasc/biopet/extensions/Ln.scala @@ -1,7 +1,7 @@ package nl.lumc.sasc.biopet.extensions import java.io.File -import scala.sys.process.Process +import scala.sys.process.{ Process, ProcessLogger } import org.broadinstitute.gatk.queue.function.InProcessFunction import org.broadinstitute.gatk.utils.commandline.{ Input, Output } import nl.lumc.sasc.biopet.core.config.Configurable @@ -71,15 +71,21 @@ class Ln(val root: Configurable) extends InProcessFunction with Configurable { if (relative) { // workaround until we have `ln` that works with relative path (i.e. `ln -r`) - "ln -s '" + inRelative + "' '" + outCanonical + "'" + "ln -s " + inRelative + " " + outCanonical } else { - "ln -s '" + inCanonical + "' '" + outCanonical + "'" + "ln -s " + inCanonical + " " + outCanonical } } override def run { - val process = Process(cmd).run - logger.info("cmd: '" + cmd + "', exitcode: " + process.exitValue) + val stdout = new StringBuffer() + val stderr = new StringBuffer() + val process = Process(cmd).run(ProcessLogger(stdout append _ + "\n", stderr append _ + "\n")) + val exitcode = process.exitValue + if (exitcode != 0) { + throw new Exception("Error creating symbolic link, this was the original message: \n" + stderr) + } + logger.info("cmd: '" + cmd + "', exitcode: " + exitcode) } } diff --git a/biopet-framework/src/main/scala/nl/lumc/sasc/biopet/tools/ExtractAlignedFastq.scala b/biopet-framework/src/main/scala/nl/lumc/sasc/biopet/tools/ExtractAlignedFastq.scala index d0f09baaa820b1a57ed181f66c25996faf9330a6..7f7fe2239ba176f7b1ed9104c5f9e7a507f44760 100644 --- a/biopet-framework/src/main/scala/nl/lumc/sasc/biopet/tools/ExtractAlignedFastq.scala +++ b/biopet-framework/src/main/scala/nl/lumc/sasc/biopet/tools/ExtractAlignedFastq.scala @@ -19,7 +19,7 @@ import nl.lumc.sasc.biopet.core.ToolCommand object ExtractAlignedFastq extends ToolCommand { - type FastqPair = (FastqRecord, FastqRecord) + type FastqInput = (FastqRecord, Option[FastqRecord]) /** * Function to create iterator over Interval given input interval string @@ -74,7 +74,7 @@ object ExtractAlignedFastq extends ToolCommand { def makeMembershipFunction(iv: Iterator[Interval], inAln: File, minMapQ: Int = 0, - commonSuffixLength: Int = 0): (FastqPair => Boolean) = { + commonSuffixLength: Int = 0): (FastqInput => Boolean) = { val inAlnReader = SamReaderFactory .make() @@ -112,54 +112,39 @@ object ExtractAlignedFastq extends ToolCommand { } ) - (pair: FastqPair) => pair._2 match { - case null => selected.contains(pair._1.getReadHeader) - case otherwise => + (pair: FastqInput) => pair._2 match { + case None => selected.contains(pair._1.getReadHeader) + case Some(x) => require(commonSuffixLength < pair._1.getReadHeader.length) - require(commonSuffixLength < pair._2.getReadHeader.length) + require(commonSuffixLength < x.getReadHeader.length) selected.contains(pair._1.getReadHeader.dropRight(commonSuffixLength)) } } - def selectFastqReads(memFunc: FastqPair => Boolean, - inputFastq1: FastqReader, - outputFastq1: BasicFastqWriter, - inputFastq2: FastqReader = null, - outputFastq2: BasicFastqWriter = null): Unit = { - - val i1 = inputFastq1.iterator.asScala - val i2 = inputFastq2 match { - case null => Iterator.continually(null) - case otherwise => otherwise.iterator.asScala - } - val o1 = outputFastq1 - val o2 = (inputFastq2, outputFastq2) match { - case (null, null) => null - case (_, null) => throw new IllegalArgumentException("Missing output FASTQ 2") - case (null, _) => throw new IllegalArgumentException("Output FASTQ 2 supplied but there is no input FASTQ 2") - case (x, y) => outputFastq2 - } - - logger.info("Writing output file(s) ...") - // zip, filter based on function, and write to output file(s) - i1.zip(i2) + def extractReads(memFunc: FastqInput => Boolean, + inputFastq1: FastqReader, outputFastq1: BasicFastqWriter): Unit = + inputFastq1.iterator.asScala + .zip(Iterator.continually(None)) .filter(rec => memFunc(rec._1, rec._2)) - .foreach { - case (rec1, null) => - o1.write(rec1) - case (rec1, rec2) => - o1.write(rec1) - o2.write(rec2) - } - - } - - case class Args(inputBam: File = null, + .foreach(rec => outputFastq1.write(rec._1)) + + def extractReads(memFunc: FastqInput => Boolean, + inputFastq1: FastqReader, outputFastq1: BasicFastqWriter, + inputFastq2: FastqReader, outputFastq2: BasicFastqWriter): Unit = + inputFastq1.iterator.asScala + .zip(inputFastq2.iterator.asScala) + .filter(rec => memFunc(rec._1, Some(rec._2))) + .foreach(rec => { + outputFastq1.write(rec._1) + outputFastq2.write(rec._2) + }) + + case class Args(inputBam: File = new File(""), intervals: List[String] = List.empty[String], - inputFastq1: File = null, - inputFastq2: File = null, - outputFastq1: File = null, - outputFastq2: File = null, + inputFastq1: File = new File(""), + inputFastq2: Option[File] = None, + outputFastq1: File = new File(""), + outputFastq2: Option[File] = None, minMapQ: Int = 0, commonSuffixLength: Int = 0) extends AbstractArgs @@ -188,7 +173,7 @@ object ExtractAlignedFastq extends ToolCommand { } text "Input FASTQ file 1" opt[File]('j', "in2") optional () valueName "<fastq>" action { (x, c) => - c.copy(inputFastq1 = x) + c.copy(inputFastq2 = Option(x)) } validate { x => if (x.exists) success else failure("Input FASTQ file 2 not found") } text "Input FASTQ file 2 (default: none)" @@ -198,7 +183,7 @@ object ExtractAlignedFastq extends ToolCommand { } text "Output FASTQ file 1" opt[File]('p', "out2") optional () valueName "<fastq>" action { (x, c) => - c.copy(outputFastq1 = x) + c.copy(outputFastq2 = Option(x)) } text "Output FASTQ file 2 (default: none)" opt[Int]('Q', "min_mapq") optional () action { (x, c) => @@ -215,35 +200,43 @@ object ExtractAlignedFastq extends ToolCommand { """.stripMargin) checkConfig { c => - if (!c.inputBam.exists) - failure("Input BAM file not found") - else if (!c.inputFastq1.exists) - failure("Input FASTQ file 1 not found") - else if (c.inputFastq2 != null && c.outputFastq2 == null) + if (c.inputFastq2 != None && c.outputFastq2 == None) failure("Missing output FASTQ file 2") - else if (c.inputFastq2 == null && c.outputFastq2 != null) + else if (c.inputFastq2 == None && c.outputFastq2 != None) failure("Missing input FASTQ file 2") else success } } - def main(args: Array[String]): Unit = { - - val commandArgs: Args = new OptParser() + def parseArgs(args: Array[String]): Args = + new OptParser() .parse(args, Args()) .getOrElse(sys.exit(1)) + def main(args: Array[String]): Unit = { + + val commandArgs: Args = parseArgs(args) + val memFunc = makeMembershipFunction( iv = makeIntervalFromString(commandArgs.intervals), inAln = commandArgs.inputBam, minMapQ = commandArgs.minMapQ, commonSuffixLength = commandArgs.commonSuffixLength) - selectFastqReads(memFunc, - inputFastq1 = new FastqReader(commandArgs.inputFastq1), - inputFastq2 = new FastqReader(commandArgs.inputFastq2), - outputFastq1 = new BasicFastqWriter(commandArgs.outputFastq1), - outputFastq2 = new BasicFastqWriter(commandArgs.outputFastq2)) + (commandArgs.inputFastq2, commandArgs.outputFastq2) match { + + case (None, None) => extractReads(memFunc, + new FastqReader(commandArgs.inputFastq1), + new BasicFastqWriter(commandArgs.inputFastq1)) + + case (Some(i2), Some(o2)) => extractReads(memFunc, + new FastqReader(commandArgs.inputFastq1), + new BasicFastqWriter(commandArgs.outputFastq1), + new FastqReader(i2), + new BasicFastqWriter(o2)) + + case _ => // handled by the command line config check above + } } } diff --git a/biopet-framework/src/test/scala/nl/lumc/sasc/biopet/extensions/LnUnitTest.scala b/biopet-framework/src/test/scala/nl/lumc/sasc/biopet/extensions/LnUnitTest.scala index 76c609094230185cead8dd01789b4604beb87342..04f6758ca3141c4c9910c10daeef2cadaaccc11b 100644 --- a/biopet-framework/src/test/scala/nl/lumc/sasc/biopet/extensions/LnUnitTest.scala +++ b/biopet-framework/src/test/scala/nl/lumc/sasc/biopet/extensions/LnUnitTest.scala @@ -19,7 +19,7 @@ class LnUnitTest extends TestNGSuite with Matchers { ln.relative = true ln.in = new File("/dir/nested/target.txt") ln.out = new File("/dir/nested/link.txt") - ln.cmd should ===("ln -s 'target.txt' '/dir/nested/link.txt'") + ln.cmd should ===("ln -s target.txt /dir/nested/link.txt") } @Test(description = "Target is one level above link, relative set to true") @@ -28,7 +28,7 @@ class LnUnitTest extends TestNGSuite with Matchers { ln.relative = true ln.in = new File("/dir/target.txt") ln.out = new File("/dir/nested/link.txt") - ln.cmd should ===("ln -s '../target.txt' '/dir/nested/link.txt'") + ln.cmd should ===("ln -s ../target.txt /dir/nested/link.txt") } @Test(description = "Target is two levels above link, relative set to true") @@ -37,7 +37,7 @@ class LnUnitTest extends TestNGSuite with Matchers { ln.relative = true ln.in = new File("/target.txt") ln.out = new File("/dir/nested/link.txt") - ln.cmd should ===("ln -s '../../target.txt' '/dir/nested/link.txt'") + ln.cmd should ===("ln -s ../../target.txt /dir/nested/link.txt") } @Test(description = "Target is a child of a directory one level above link, relative set to true") @@ -46,7 +46,7 @@ class LnUnitTest extends TestNGSuite with Matchers { ln.relative = true ln.in = new File("/dir/another_nested/target.txt") ln.out = new File("/dir/nested/link.txt") - ln.cmd should ===("ln -s '../another_nested/target.txt' '/dir/nested/link.txt'") + ln.cmd should ===("ln -s ../another_nested/target.txt /dir/nested/link.txt") } @Test(description = "Target is one level below link, relative set to true") @@ -55,7 +55,7 @@ class LnUnitTest extends TestNGSuite with Matchers { ln.relative = true ln.in = new File("/dir/nested/deeper/target.txt") ln.out = new File("/dir/nested/link.txt") - ln.cmd should ===("ln -s 'deeper/target.txt' '/dir/nested/link.txt'") + ln.cmd should ===("ln -s deeper/target.txt /dir/nested/link.txt") } @Test(description = "Target is two levels below link, relative set to true") @@ -64,7 +64,7 @@ class LnUnitTest extends TestNGSuite with Matchers { ln.relative = true ln.in = new File("/dir/nested/even/deeper/target.txt") ln.out = new File("/dir/nested/link.txt") - ln.cmd should ===("ln -s 'even/deeper/target.txt' '/dir/nested/link.txt'") + ln.cmd should ===("ln -s even/deeper/target.txt /dir/nested/link.txt") } @Test(description = "Relative set to false") @@ -73,7 +73,7 @@ class LnUnitTest extends TestNGSuite with Matchers { ln.relative = false ln.in = new File("/dir/nested/target.txt") ln.out = new File("/dir/nested/link.txt") - ln.cmd should ===("ln -s '/dir/nested/target.txt' '/dir/nested/link.txt'") + ln.cmd should ===("ln -s /dir/nested/target.txt /dir/nested/link.txt") } // TODO: test for case where abosolute is true and input paths are relative? diff --git a/biopet-framework/src/test/scala/nl/lumc/sasc/biopet/tools/ExtractAlignedFastqUnitTest.scala b/biopet-framework/src/test/scala/nl/lumc/sasc/biopet/tools/ExtractAlignedFastqUnitTest.scala index 19b05904b6135df1576d334668a4338494e6bd7f..4cd7205c0ac6e22c4f93c384600c9f570bc88c06 100644 --- a/biopet-framework/src/test/scala/nl/lumc/sasc/biopet/tools/ExtractAlignedFastqUnitTest.scala +++ b/biopet-framework/src/test/scala/nl/lumc/sasc/biopet/tools/ExtractAlignedFastqUnitTest.scala @@ -8,7 +8,7 @@ import java.io.File import java.nio.file.Paths import org.mockito.Matchers._ -import org.mockito.Mockito._ +import org.mockito.Mockito.{ inOrder => inOrd, times, verify } import org.scalatest.Matchers import org.scalatest.mock.MockitoSugar import org.scalatest.testng.TestNGSuite @@ -22,7 +22,10 @@ class ExtractAlignedFastqUnitTest extends TestNGSuite with MockitoSugar with Mat import ExtractAlignedFastq._ private def resourceFile(p: String): File = - new File(Paths.get(getClass.getResource(p).toURI).toString) + new File(resourcePath(p)) + + private def resourcePath(p: String): String = + Paths.get(getClass.getResource(p).toURI).toString private def makeInterval(chr: String, start: Int, end: Int): Interval = new Interval(chr, start, end) @@ -30,11 +33,11 @@ class ExtractAlignedFastqUnitTest extends TestNGSuite with MockitoSugar with Mat private def makeRecord(header: String): FastqRecord = new FastqRecord(header, "ATGC", "", "HIHI") - private def makeSingleRecords(headers: String*): Map[String, FastqPair] = - headers.map(x => (x, (makeRecord(x), null))).toMap + private def makeSingleRecords(headers: String*): Map[String, FastqInput] = + headers.map(x => (x, (makeRecord(x), None))).toMap - private def makePairRecords(headers: (String, (String, String))*): Map[String, FastqPair] = - headers.map(x => (x._1, (makeRecord(x._2._1), makeRecord(x._2._2)))).toMap + private def makePairRecords(headers: (String, (String, String))*): Map[String, FastqInput] = + headers.map(x => (x._1, (makeRecord(x._2._1), Some(makeRecord(x._2._2))))).toMap private def makeClue(tName: String, f: File, rName: String): String = tName + " on " + f.getName + ", read " + rName + ": " @@ -109,7 +112,7 @@ class ExtractAlignedFastqUnitTest extends TestNGSuite with MockitoSugar with Mat @Test(dataProvider = "singleAlnProvider1") def testSingleBamDefault(name: String, feat: Interval, inAln: File, - fastqMap: Map[String, FastqPair], resultMap: Map[String, Boolean]) = { + fastqMap: Map[String, FastqInput], resultMap: Map[String, Boolean]) = { require(resultMap.keySet == fastqMap.keySet) val memFunc = makeMembershipFunction(Iterator(feat), inAln) for ((key, (rec1, rec2)) <- fastqMap) { @@ -137,7 +140,7 @@ class ExtractAlignedFastqUnitTest extends TestNGSuite with MockitoSugar with Mat @Test(dataProvider = "singleAlnProvider2") def testSingleBamMinMapQ(name: String, feat: Interval, inAln: File, minMapQ: Int, - fastqMap: Map[String, FastqPair], resultMap: Map[String, Boolean]) = { + fastqMap: Map[String, FastqInput], resultMap: Map[String, Boolean]) = { require(resultMap.keySet == fastqMap.keySet) val memFunc = makeMembershipFunction(Iterator(feat), inAln, minMapQ) for ((key, (rec1, rec2)) <- fastqMap) { @@ -175,7 +178,7 @@ class ExtractAlignedFastqUnitTest extends TestNGSuite with MockitoSugar with Mat @Test(dataProvider = "pairAlnProvider1") def testPairBamDefault(name: String, feat: Interval, inAln: File, - fastqMap: Map[String, FastqPair], resultMap: Map[String, Boolean]) = { + fastqMap: Map[String, FastqInput], resultMap: Map[String, Boolean]) = { require(resultMap.keySet == fastqMap.keySet) val memFunc = makeMembershipFunction(Iterator(feat), inAln, commonSuffixLength = 2) for ((key, (rec1, rec2)) <- fastqMap) { @@ -185,53 +188,68 @@ class ExtractAlignedFastqUnitTest extends TestNGSuite with MockitoSugar with Mat } } - @Test def testWriteSingleBamDefault() = { - val memFunc = (recs: FastqPair) => Set("r01", "r03").contains(recs._1.getReadHeader) + @Test def testWriteSingleFastqDefault() = { + val memFunc = (recs: FastqInput) => Set("r01", "r03").contains(recs._1.getReadHeader) val in1 = new FastqReader(resourceFile("/single01.fq")) val mo1 = mock[BasicFastqWriter] - selectFastqReads(memFunc, in1, mo1) + val obs = inOrd(mo1) + extractReads(memFunc, in1, mo1) verify(mo1, times(2)).write(anyObject.asInstanceOf[FastqRecord]) - verify(mo1).write(new FastqRecord("r01", "A", "", "H")) - verify(mo1).write(new FastqRecord("r03", "G", "", "H")) + obs.verify(mo1).write(new FastqRecord("r01", "A", "", "H")) + obs.verify(mo1).write(new FastqRecord("r03", "G", "", "H")) } - @Test def testWritePairBamDefault() = { - val memFunc = (recs: FastqPair) => Set("r01/1", "r01/2", "r03/1", "r03/2").contains(recs._1.getReadHeader) + @Test def testWritePairFastqDefault() = { + val mockSet = Set("r01/1", "r01/2", "r03/1", "r03/2") + val memFunc = (recs: FastqInput) => mockSet.contains(recs._1.getReadHeader) || mockSet.contains(recs._2.get.getReadHeader) val in1 = new FastqReader(resourceFile("/paired01a.fq")) val in2 = new FastqReader(resourceFile("/paired01b.fq")) val mo1 = mock[BasicFastqWriter] val mo2 = mock[BasicFastqWriter] - selectFastqReads(memFunc, in1, mo1, in2, mo2) + val obs = inOrd(mo1, mo2) + extractReads(memFunc, in1, mo1, in2, mo2) + obs.verify(mo1).write(new FastqRecord("r01/1", "A", "", "H")) + obs.verify(mo2).write(new FastqRecord("r01/2", "T", "", "I")) + obs.verify(mo1).write(new FastqRecord("r03/1", "G", "", "H")) + obs.verify(mo2).write(new FastqRecord("r03/2", "C", "", "I")) verify(mo1, times(2)).write(anyObject.asInstanceOf[FastqRecord]) - verify(mo1).write(new FastqRecord("r01/1", "A", "", "H")) - verify(mo1).write(new FastqRecord("r03/1", "G", "", "H")) verify(mo2, times(2)).write(anyObject.asInstanceOf[FastqRecord]) - verify(mo2).write(new FastqRecord("r01/2", "T", "", "I")) - verify(mo2).write(new FastqRecord("r03/2", "C", "", "I")) - } - - @Test def testWriteNoOutputFastq2() = { - val memFunc: (FastqPair => Boolean) = (recs) => true - val in1 = mock[FastqReader] - val in2 = mock[FastqReader] - val out1 = mock[BasicFastqWriter] - val thrown = intercept[IllegalArgumentException] { - selectFastqReads(memFunc, in1, out1, in2) - } - thrown.getMessage should ===("Missing output FASTQ 2") - verify(out1, never).write(anyObject.asInstanceOf[FastqRecord]) } - @Test def testWriteNoInputFastq2() = { - val memFunc: (FastqPair => Boolean) = (recs) => true - val in1 = mock[FastqReader] - val out1 = mock[BasicFastqWriter] - val out2 = mock[BasicFastqWriter] - val thrown = intercept[IllegalArgumentException] { - selectFastqReads(memFunc, in1, out1, outputFastq2 = out2) - } - thrown.getMessage should ===("Output FASTQ 2 supplied but there is no input FASTQ 2") - verify(out1, never).write(anyObject.asInstanceOf[FastqRecord]) - verify(out2, never).write(anyObject.asInstanceOf[FastqRecord]) + @Test def testArgsMinimum() = { + val args = Array( + "-I", resourcePath("/single01.bam"), + "--interval", "chrQ:1-400", + "-i", resourcePath("/single01.fq"), + "-o", "/tmp/tm1.fq" + ) + val parsed = parseArgs(args) + parsed.inputBam shouldBe resourceFile("/single01.bam") + parsed.intervals shouldBe List("chrQ:1-400") + parsed.inputFastq1 shouldBe resourceFile("/single01.fq") + parsed.outputFastq1 shouldBe new File("/tmp/tm1.fq") + } + + @Test def testArgsMaximum() = { + val args = Array( + "-I", resourcePath("/paired01.bam"), + "--interval", "chrQ:1-400", + "--interval", "chrP:1000-4000", + "-i", resourcePath("/paired01a.fq"), + "-j", resourcePath("/paired01b.fq"), + "-o", "/tmp/tm1.fq", + "-p", "/tmp/tm2.fq", + "-s", "2", + "-Q", "30" + ) + val parsed = parseArgs(args) + parsed.inputBam shouldBe resourceFile("/paired01.bam") + parsed.intervals shouldBe List("chrQ:1-400", "chrP:1000-4000") + parsed.inputFastq1 shouldBe resourceFile("/paired01a.fq") + parsed.inputFastq2.get shouldBe resourceFile("/paired01b.fq") + parsed.outputFastq1 shouldBe new File("/tmp/tm1.fq") + parsed.outputFastq2.get shouldBe new File("/tmp/tm2.fq") + parsed.commonSuffixLength shouldBe 2 + parsed.minMapQ shouldBe 30 } } diff --git a/pom.xml b/pom.xml index 7e98eb6956a1390dff1a32cce1bf1ee28d581f46..732ff3d241c9af014fdd61b74aea83b3b091a430 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>nl.lumc.sasc</groupId> - <artifactId>Biopet</artifactId> - <version>0.1.3</version> - <packaging>pom</packaging> - <name>Biopet</name> - <modules> - <module>biopet-framework</module> - </modules> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>nl.lumc.sasc</groupId> + <artifactId>Biopet</artifactId> + <version>0.2.0-DEV</version> + <packaging>pom</packaging> + <name>Biopet</name> + <modules> + <module>biopet-framework</module> + </modules> </project>