{"action":"created","comment":{"url":"https://api.github.com/repos/broadinstitute/gatk/pulls/comments/156431314","pull_request_review_id":82906698,"id":156431314,"diff_hunk":"@@ -0,0 +1,392 @@\n+package org.broadinstitute.hellbender.tools.walkers.annotator;\n+\n+import htsjdk.samtools.seekablestream.SeekablePathStream;\n+import htsjdk.tribble.readers.LineIterator;\n+import htsjdk.tribble.readers.PositionalBufferedStream;\n+import htsjdk.variant.variantcontext.VariantContext;\n+import htsjdk.variant.vcf.*;\n+import org.broadinstitute.hellbender.CommandLineProgramTest;\n+import org.broadinstitute.hellbender.utils.Utils;\n+import org.broadinstitute.hellbender.utils.runtime.ProcessController;\n+import org.broadinstitute.hellbender.utils.runtime.ProcessOutput;\n+import org.broadinstitute.hellbender.utils.runtime.ProcessSettings;\n+import org.broadinstitute.hellbender.utils.test.ArgumentsBuilder;\n+import org.broadinstitute.hellbender.utils.test.IntegrationTestSpec;\n+import org.broadinstitute.hellbender.utils.test.VariantContextTestUtils;\n+import org.seqdoop.hadoop_bam.VCFFormat;\n+import org.seqdoop.hadoop_bam.util.VCFHeaderReader;\n+import org.testng.Assert;\n+import org.testng.annotations.Test;\n+\n+import javax.validation.constraints.AssertFalse;\n+import java.io.File;\n+import java.io.FileInputStream;\n+import java.io.IOException;\n+import java.security.NoSuchAlgorithmException;\n+import java.util.*;\n+import java.util.function.BiConsumer;\n+import java.util.stream.Collectors;\n+\n+public class VariantAnnotatorIntegrationTest extends CommandLineProgramTest {\n+    final static String STANDARD_ANNOTATIONS = \" -G StandardAnnotation \"; //-G StandardUG ?\n+    private static final List<String> ATTRIBUTES_TO_IGNORE = Arrays.asList(\n+            \"QD\",//TODO QD has a cap value and anything that reaches that is randomized.  It's difficult to reproduce the same random numbers across gatk3 -> 4\n+            \"FS\");//TODO There's some bug in either gatk3 or gatk4 fisherstrand that's making them not agree still, I'm not sure which is correct\n+    private static final List<String> HEADER_LINES_TO_IGNROE = Arrays.asList(\n+            \"reference\",\n+            \"HaplotypeScore\",\n+            \"GATKCommandLine.VariantAnnotator\",\n+            \"RAW_MQ\"\n+    );\n+\n+\n+    //TODO because of differences between how GATK3 and GATK4 handle capturing reads for spanning deletions (Namely 3 only looks for reads overlapping the first site, 4 gets all reads over the span)\n+    //TODO then we want ot ignore affected attributes for concordance tests\n+    private static final List<String> DEPTH_ATTRIBUTES_TO_IGNORE = Arrays.asList(\n+            \"QD\",\n+            \"DP\",\n+            \"MQ\");\n+\n+    final String testFile = getToolTestDataDir() + \"vcfexample2.vcf\";\n+\n+    public static String baseTestString() {\n+        return \"-R \" + b37_reference_20_21 + \" -O %s \";\n+    }\n+\n+    //==================================================================================================================\n+    // Testing\n+    //==================================================================================================================\n+    private void assertVariantContextsMatch(File input, File expected, List<String> extraArgs, String reference) throws IOException {\n+        assertVariantContextsMatch(input, expected, extraArgs,reference, ATTRIBUTES_TO_IGNORE);\n+    }\n+\n+    private void assertVariantContextsMatch(File input, File expected, List<String> extraArgs, String reference, List<String> ignoreAtrributes) throws IOException {\n+        final VCFHeader header = getHeaderFromFile(expected);\n+\n+        runVariantAnnotatorAndAssertSomething(input, expected, extraArgs, (a, e) -> {\n+            VariantContextTestUtils.assertVariantContextsAreEqualAlleleOrderIndependent(a, e, ATTRIBUTES_TO_IGNORE, header);\n+        }, reference);\n+    }\n+\n+    //TODO if this gets generalized to another class this should be made more robust to differing number of header lines\n+    private void assertHeadersMatch(File input, File expected, List<String> linesToIgnore) throws IOException {\n+        final VCFHeader expectedHeader = getHeaderFromFile(expected);\n+        final VCFHeader inputHeader = getHeaderFromFile(input);\n+\n+        Iterator<VCFHeaderLine> inputItr = inputHeader.getMetaDataInSortedOrder().iterator();\n+        Iterator<VCFHeaderLine> expectedItr = expectedHeader.getMetaDataInSortedOrder().iterator();\n+\n+        while (inputItr.hasNext() && expectedItr.hasNext()) {\n+            VCFHeaderLine expectedLine = expectedItr.next();\n+            VCFHeaderLine inputLine = inputItr.next();\n+            while (shouldFilterLine(inputLine, linesToIgnore)) {\n+                inputLine = inputItr.hasNext()?inputItr.next():null;\n+            }\n+            while (shouldFilterLine(expectedLine, linesToIgnore)) {\n+                expectedLine = expectedItr.hasNext()?expectedItr.next():null;\n+            }\n+            if ((inputLine == null || expectedLine==null) && expectedLine!=inputLine) {\n+                Assert.fail(\"Files had a differing number of un-skipped lines\");\n+            } else {\n+                Assert.assertEquals(inputLine, expectedLine);\n+            }\n+\n+        }\n+        Assert.assertFalse(inputItr.hasNext());\n+        Assert.assertFalse(expectedItr.hasNext());\n+    }\n+\n+    private boolean shouldFilterLine(VCFHeaderLine line, List<String> linesToIgnore) {\n+        return (line != null)\n+                && ((line instanceof VCFIDHeaderLine && linesToIgnore.contains(((VCFIDHeaderLine)line).getID()))\n+                    || linesToIgnore.contains(line.getKey()));\n+    }\n+\n+    private void runVariantAnnotatorAndAssertSomething(File input, File expected, List<String> additionalArguments, BiConsumer<VariantContext, VariantContext> assertion, String reference) throws IOException {\n+        final File output = createTempFile(\"variantAnnotator\", \".vcf\");\n+\n+        final ArgumentsBuilder args = new ArgumentsBuilder();\n+        if (reference!=null) {\n+            args.addReference(new File(reference));\n+        }\n+        args.addOutput(output).addArgument(\"V\", input.getAbsolutePath());\n+\n+\n+        // Handling a difference in syntax between GATK3 and GATK4 wrt. annotation groups\n+        additionalArguments = additionalArguments.stream().map(a -> a.contains(\"Standard\") ? a + \"Annotation\" : a).collect(Collectors.toList());\n+        additionalArguments.forEach(args::add);\n+\n+        Utils.resetRandomGenerator();\n+        runCommandLine(args);\n+\n+        assertHeadersMatch(output, expected, HEADER_LINES_TO_IGNROE);\n+        final List<VariantContext> expectedVC = getVariantContexts(expected);\n+        final List<VariantContext> actualVC = getVariantContexts(output);\n+        assertForEachElementInLists(actualVC, expectedVC, assertion);\n+    }\n+\n+    /**\n+     * Returns a list of VariantContext records from a VCF file\n+     *\n+     * @param vcfFile VCF file\n+     * @return list of VariantContext records\n+     * @throws IOException if the file does not exist or can not be opened\n+     */\n+    private static List<VariantContext> getVariantContexts(final File vcfFile) throws IOException {\n+        final VCFCodec codec = new VCFCodec();\n+        final FileInputStream s = new FileInputStream(vcfFile);\n+        final LineIterator lineIteratorVCF = codec.makeSourceFromStream(new PositionalBufferedStream(s));\n+        codec.readHeader(lineIteratorVCF);\n+\n+        final List<VariantContext> VCs = new ArrayList<>();\n+        while (lineIteratorVCF.hasNext()) {\n+            final String line = lineIteratorVCF.next();\n+            Assert.assertFalse(line == null);\n+            VCs.add(codec.decode(line));\n+        }\n+\n+        return VCs;\n+    }\n+\n+    private static VCFHeader getHeaderFromFile(final File vcfFile) throws IOException {\n+        SeekablePathStream stream = new SeekablePathStream(vcfFile.toPath());\n+        VCFHeader header = VCFHeaderReader.readHeaderFrom(new SeekablePathStream(vcfFile.toPath()));\n+        stream.close();\n+        return header;\n+    }\n+\n+    private static <T> void assertForEachElementInLists(final List<T> actual, final List<T> expected, final BiConsumer<T, T> assertion) {\n+        Assert.assertEquals(actual.size(), expected.size(), \"different number of elements in lists:\\n\"\n+                + actual.stream().map(Object::toString).collect(Collectors.joining(\"\\n\",\"actual:\\n\",\"\\n\"))\n+                +  expected.stream().map(Object::toString).collect(Collectors.joining(\"\\n\",\"expected:\\n\",\"\\n\")));\n+        for (int i = 0; i < actual.size(); i++) {\n+\n+            assertion.accept(actual.get(i), expected.get(i));\n+        }\n+    }\n+\n+    //==================================================================================================================\n+    // Tests\n+    //==================================================================================================================\n+\n+    @Test\n+    public void GATK3LargeConcoranceTest() throws IOException {\n+        assertVariantContextsMatch(getTestFile(\"HCOutput.NoAnnotations.vcf\"), new File(getToolTestDataDir() + \"expected/integrationTest.vcf\"), Arrays.asList(\"-G\", \"Standard\", \"-G\", \"AS_Standard\", \"-L\", \"20:10000000-10100000\", \"-I\", NA12878_20_21_WGS_bam), b37_reference_20_21);\n+    }\n+\n+    //TODO these tests must be modernized :-(\n+    @Test\n+    public void testHasAnnotsNotAsking1() throws IOException {\n+        final File expected = new File(getToolTestDataDir() + \"expected/testHsAnnotsNotAsking1.vcf\");\n+        final VCFHeader header = getHeaderFromFile(expected);\n+        runVariantAnnotatorAndAssertSomething(getTestFile(\"vcfexamplemultisample.vcf\"), new File(getToolTestDataDir() + \"expected/testHsAnnotsNotAsking1.vcf\"), Arrays.asList( \"-I\", largeFileTestDir + \"CEUTrio.multisample.b37.1M-1M50k.bam\"),\n+                (a, e) -> {\n+                    // We need to filter out sites where we saw a DP of 250 because we are comparing the results to GATK3, which downsamples to 250 reads per sample, which GATK4 does not currently support.\n+                    if (!e.getGenotypes().stream().anyMatch(g -> g.hasDP() && g.getDP() >= 250)) {\n+                        VariantContextTestUtils.assertVariantContextsAreEqualAlleleOrderIndependent(a, e, ATTRIBUTES_TO_IGNORE, header);\n+                    }\n+                },\n+                b37_reference_20_21);\n+    }\n+\n+    @Test\n+    public void testHasAnnotsAsking1() throws IOException {\n+        final File expected = new File(getToolTestDataDir() + \"expected/testHasAnnotsAsking1.vcf\");\n+        final VCFHeader header = getHeaderFromFile(expected);\n+        runVariantAnnotatorAndAssertSomething(getTestFile(\"vcfexamplemultisample.vcf\"), new File(getToolTestDataDir() + \"expected/testHasAnnotsAsking1.vcf\"), Arrays.asList(\"-G\", \"Standard\", \"-I\", largeFileTestDir + \"CEUTrio.multisample.b37.1M-1M50k.bam\"),\n+                (a, e) -> {\n+                    // We need to filter out sites where we saw a DP of 250 because we are comparing the results to GATK3, which downsamples to 250 reads per sample, which GATK4 does not currently support.\n+                    if (!e.getGenotypes().stream().anyMatch(g -> g.hasDP() && g.getDP() >= 250)) {\n+                        VariantContextTestUtils.assertVariantContextsAreEqualAlleleOrderIndependent(a, e, ATTRIBUTES_TO_IGNORE, header);\n+                    }\n+                },\n+                b37_reference_20_21);\n+    }\n+\n+    @Test\n+    public void testNoAnnotsNotAsking1() throws IOException {\n+        final File expected = new File(getToolTestDataDir() + \"expected/testHsAnnotsNotAsking1.vcf\");\n+        final VCFHeader header = getHeaderFromFile(expected);\n+        runVariantAnnotatorAndAssertSomething(getTestFile(\"vcfexamplemultisampleempty.vcf\"), new File(getToolTestDataDir() + \"expected/testHasNoAnnotsNotAsking1.vcf\"), Arrays.asList( \"-I\", largeFileTestDir + \"CEUTrio.multisample.b37.1M-1M50k.bam\"),\n+                (a, e) -> {\n+                    // We need to filter out sites where we saw a DP of 250 because we are comparing the results to GATK3, which downsamples to 250 reads per sample, which GATK4 does not currently support.\n+                    if (!e.getGenotypes().stream().anyMatch(g -> g.hasDP() && g.getDP() >= 250)) {\n+                        VariantContextTestUtils.assertVariantContextsAreEqualAlleleOrderIndependent(a, e, ATTRIBUTES_TO_IGNORE, header);\n+                    }\n+                },\n+                b37_reference_20_21);\n+    }\n+\n+\n+    @Test\n+    public void testNoAnnotsAsking1() throws IOException {\n+        final File expected = new File(getToolTestDataDir() + \"expected/testHasNoAnnotsAsking1.vcf\");\n+        final VCFHeader header = getHeaderFromFile(expected);\n+        runVariantAnnotatorAndAssertSomething(getTestFile(\"vcfexamplemultisampleempty.vcf\"), new File(getToolTestDataDir() + \"expected/testHasNoAnnotsAsking1.vcf\"), Arrays.asList(\"-G\", \"Standard\", \"-I\", largeFileTestDir + \"CEUTrio.multisample.b37.1M-1M50k.bam\"),\n+                (a, e) -> {\n+                    // We need to filter out sites where we saw a DP of 250 because we are comparing the results to GATK3, which downsamples to 250 reads per sample, which GATK4 does not currently support.\n+                    if (!e.getGenotypes().stream().anyMatch(g -> g.hasDP() && g.getDP() >= 250)) {\n+                        VariantContextTestUtils.assertVariantContextsAreEqualAlleleOrderIndependent(a, e, ATTRIBUTES_TO_IGNORE, header);\n+                    }\n+                },\n+                b37_reference_20_21);\n+    }\n+    @Test\n+    public void testOverwritingHeader() throws IOException {\n+        assertVariantContextsMatch(getTestFile(\"vcfexample4.vcf\"), getTestFile(\"expected/testReplaceHeader.vcf\"), Arrays.asList(\"-G\", \"Standard\", \"-L\", \"20:10,001,292\"), b37_reference_20_21);\n+    }\n+    @Test\n+    public void testNoReads() throws IOException {\n+        assertVariantContextsMatch(getTestFile(\"vcfexample3empty.vcf\"), getTestFile(\"expected/testNoReads.vcf\"), Arrays.asList(\"-G\", \"Standard\", \"-L\", getToolTestDataDir() + \"vcfexample3empty.vcf\"), b37_reference_20_21);\n+    }\n+    @Test\n+    public void testMultipleIdsWithDbsnp() throws IOException {\n+        assertVariantContextsMatch(getTestFile(\"vcfdbsnpwithIDs.vcf\"), getTestFile(\"expected/testMultipleIdsWithDbsnp.vcf\"), Arrays.asList(\"-G\", \"Standard\", \"-L\",getToolTestDataDir() + \"vcfdbsnpwithIDs.vcf\", \"--alwaysAppendDbsnpId\", \"--dbsnp\", dbsnp_138_b37_20_21_vcf), null);\n+    }\n+    @Test\n+    public void testDBTagWithHapMap() throws IOException {\n+        assertVariantContextsMatch(getTestFile(\"vcfexample3empty.vcf\"),\n+                getTestFile(\"expected/testDBTagWithHapMap.vcf\"),\n+                Arrays.asList(\"-G\", \"Standard\", \"-L\", getToolTestDataDir() + \"vcfexample3empty.vcf\", \"--comp\", \"H3:\" + getToolTestDataDir() + \"fakeHM3.vcf\"),\n+                b37_reference_20_21, Collections.emptyList());\n+    }\n+\n+    @Test\n+    public void testDBTagWithTwoComps() throws IOException {\n+        assertVariantContextsMatch(getTestFile(\"vcfexample3empty.vcf\"),\n+                getTestFile(\"expected/testDBTagWithTwoComps.vcf\"),\n+                Arrays.asList(\"-G\", \"Standard\", \"-L\", getToolTestDataDir() + \"vcfexample3empty.vcf\", \"--comp\", \"H3:\" + getToolTestDataDir() + \"fakeHM3.vcf\", \"--comp\", \"foo:\" + getToolTestDataDir() + \"fakeHM3.vcf\"),\n+                b37_reference_20_21, Collections.emptyList());\n+    }\n+\n+    @Test\n+    public void testNoQuals() throws IOException {\n+        // NOTE, this test is asserting that the QD calculation is dependant on existing QUAL field, the values themselves are subject to random jitter\n+        assertVariantContextsMatch(getTestFile(\"noQual.vcf\"),\n+                getTestFile(\"expected/noQual.vcf\"),\n+                Arrays.asList( \"-L\", getToolTestDataDir() + \"noQual.vcf\", \"-I\", NA12878_20_21_WGS_bam, \"-A\", \"QualByDepth\"),\n+                b37_reference_20_21, Collections.emptyList());\n+    }\n+\n+    @Test\n+    public void testUsingExpression() throws IOException {\n+        assertVariantContextsMatch(getTestFile(\"vcfexample3empty.vcf\"),\n+                new File(getToolTestDataDir() + \"expected/testUsingExpression.vcf\"),\n+                Arrays.asList(\"--resourceAlleleConcordance\",  \"--resource\",  \"foo:\" + getToolTestDataDir() + \"targetAnnotations.vcf\",\n+                        \"-G\", \"Standard\", \"-E\", \"foo.AF\", \"-L\", getToolTestDataDir()+\"vcfexample3empty.vcf\"), b37_reference_20_21);\n+    }\n+\n+    @Test\n+    public void testUsingExpressionAlleleMisMatch() throws IOException {\n+        assertVariantContextsMatch(getTestFile(\"vcfexample3empty-mod.vcf\"),\n+                new File(getToolTestDataDir() + \"expected/testUsingExpressionAlleleMisMatch.vcf\"),\n+                Arrays.asList(\"--resourceAlleleConcordance\",  \"--resource\",  \"foo:\" + getToolTestDataDir() + \"targetAnnotations.vcf\",\n+                        \"-G\", \"Standard\", \"-E\", \"foo.AF\", \"-L\", getToolTestDataDir()+\"vcfexample3empty-mod.vcf\"), b37_reference_20_21);\n+    }\n+\n+    @Test\n+    public void testUsingExpressionMultiAllele() throws IOException {\n+        assertVariantContextsMatch(getTestFile(\"vcfexample3empty-multiAllele.vcf\"),\n+                getTestFile(\"expected/testUsingExpressionMultiAllele.vcf\"),\n+                Arrays.asList(\"-G\", \"Standard\", \"-L\", getToolTestDataDir() + \"vcfexample3empty-multiAllele.vcf\", \"--resource\", \"foo:\" + getToolTestDataDir() + \"targetAnnotations-multiAllele.vcf\", \"-E\", \"foo.AF\", \"-E\", \"foo.AC\"),\n+                b37_reference_20_21, Collections.emptyList());\n+    }\n+\n+    @Test\n+    public void testFilterInExpression() throws IOException {\n+        /* The order of filters in the output seems platform-dependent. May need to change htsjdk to make the order consistent across platforms. [Sato] */\n+        assertVariantContextsMatch(getTestFile(\"vcfexample3empty-multiAllele.vcf\"),\n+                getTestFile(\"expected/testFilterInExpression.vcf\"),\n+                Arrays.asList(\"-G\", \"Standard\", \"-L\", getToolTestDataDir() + \"vcfexample3empty-multiAllele.vcf\", \"--resource\", \"foo:\" + getToolTestDataDir() + \"annotationResourceWithFilter.vcf\", \"-E\", \"foo.FILTER\"),\n+                b37_reference_20_21, Collections.emptyList());\n+    }\n+\n+    @Test\n+    public void testUsingExpressionWithID() throws IOException {\n+        assertVariantContextsMatch(getTestFile(\"vcfexample3empty.vcf\"),\n+                getTestFile(\"expected/testUsingExpressionWithID.vcf\"),\n+                Arrays.asList(\"-G\", \"Standard\", \"-L\", getToolTestDataDir() + \"vcfexample3empty.vcf\", \"--resource\", \"foo:\" + getToolTestDataDir() + \"targetAnnotations.vcf\", \"-E\", \"foo.ID\"),\n+                b37_reference_20_21, Collections.emptyList());\n+    }\n+\n+//\n+//\n+//    @Test(enabled = true)\n+//    public void testChromosomeCountsPed() {\n+//        final String       nD5 = \"0a18fe81dde8d0f94f9ac5e5f65d00d5\";\n+//        WalkerTestSpec spec = new WalkerTestSpec(\n+//                \"-T VariantAnnotator -R \" + b37KGReference + \" -A ChromosomeCounts --variant:vcf \" + privateTestDir + \"ug.random50000.subset300bp.chr1.family.vcf\" +\n+//                        \" -L \" + privateTestDir + \"ug.random50000.subset300bp.chr1.family.vcf --no_cmdline_in_header -ped \" + privateTestDir + \"ug.random50000.family.ped -o %s\", 1,\n+//                Arrays.asList(MD5));\n+//        executeTest(\"Testing ChromosomeCounts annotation with PED file\", spec);\n+//    }\n+//\n+    //TODO this has no arguments just yet\n+//    @Test(enabled = true)\n+//    public void testInbreedingCoeffPed() {\n+//        final String MD5 = \"95408408863cc81c63ec3c53716bf9d2\";\n+//        WalkerTestSpec spec = new WalkerTestSpec(\n+//                \"-T VariantAnnotator -R \" + b37KGReference + \" -A InbreedingCoeff --variant:vcf \" + privateTestDir + \"ug.random50000.subset300bp.chr1.family.vcf\" +\n+//                        \" -L \" + privateTestDir + \"ug.random50000.subset300bp.chr1.family.vcf --no_cmdline_in_header -ped \" + privateTestDir + \"ug.random50000.family.ped -o %s\", 1,\n+//                Arrays.asList(MD5));\n+//        executeTest(\"Testing InbreedingCoeff annotation with PED file\", spec);\n+//    }\n+\n+    @Test\n+    public void testAlleleTrimming() throws IOException {\n+        // This test makes sure that the expression code works in a complex case with many overlapping variant contexts\n+        assertVariantContextsMatch(getTestFile(\"AlleleTrim.vcf\"),\n+                getTestFile(\"expected/testAlleleTrimming.vcf\"),\n+                Arrays.asList( \"--resource\", \"exac:\" + getToolTestDataDir() + \"exacAlleleTrim.vcf\", \"-E\", \"exac.AC_Adj\", \"-A\", \"InbreedingCoeff\"),\n+                null, Collections.emptyList());\n+    }\n+\n+    @Test\n+    public void testStrandBiasBySample() throws IOException {\n+        // Created variants via HalotypeCaller GATK3 with no default annotations\n+        final File outputVCF = getTestFile(\"HCOutputNoAnnotations.vcf\");\n+\n+        // Created variant via HalotypeCaller GATK3; include StrandBiasBySample, exclude FisherStrand annotation\n+        //             re-Annotate the variant with VariantAnnotator using FisherStrand annotation\n+        final File outputVCFNoFS = getTestFile(\"HCOutputNoFSAnnotation.vcf\");\n+\n+        final File outputWithAddedFS = createTempFile(\"variantannotator\", \".vcf\");\n+\n+        final ArgumentsBuilder args = new ArgumentsBuilder();\n+        args.addReference(new File(b37_reference_20_21));\n+        args.addVCF(outputVCFNoFS);\n+        args.addOutput(outputWithAddedFS);\n+        args.add(\"-L 20:10130000-10134800\");\n+        args.add(\"-A FisherStrand\");\n+        runCommandLine(args);\n+\n+        // confirm that the FisherStrand values are identical for the two pipelines\n+        final VCFCodec codec = new VCFCodec();\n+        final FileInputStream s = new FileInputStream(outputVCF);\n+        final LineIterator lineIterator = codec.makeSourceFromStream(new PositionalBufferedStream(s));\n+        codec.readHeader(lineIterator);\n+\n+        final VCFCodec codecAnn = new VCFCodec();\n+        final FileInputStream sAnn = new FileInputStream(outputWithAddedFS);\n+        final LineIterator lineIteratorAnn = codecAnn.makeSourceFromStream(new PositionalBufferedStream(sAnn));\n+        codecAnn.readHeader(lineIteratorAnn);\n+\n+        while( lineIterator.hasNext() && lineIteratorAnn.hasNext() ) {\n+            final String line = lineIterator.next();\n+            Assert.assertFalse(line == null);\n+            final VariantContext vc = codec.decode(line);\n+\n+            final String lineAnn = lineIteratorAnn.next();\n+            Assert.assertFalse(lineAnn == null);\n+            final VariantContext vcAnn = codecAnn.decode(lineAnn);\n+\n+            Assert.assertTrue(vc.hasAttribute(\"FS\"));\n+            Assert.assertTrue(vcAnn.hasAttribute(\"FS\"));\n+            Assert.assertEquals(vc.getAttributeAsDouble(\"FS\", 0.0), vcAnn.getAttributeAsDouble(\"FS\", -1.0));\n+        }\n+\n+        Assert.assertFalse(lineIterator.hasNext());\n+        Assert.assertFalse(lineIteratorAnn.hasNext());\n+    }\n+\n+}","path":"src/test/java/org/broadinstitute/hellbender/tools/walkers/annotator/VariantAnnotatorIntegrationTest.java","position":392,"original_position":392,"commit_id":"c629e1cda070801ebda8dc5b78e18a558d1e4cd0","original_commit_id":"cce8d59c2fe549cbaf7bdb41c175ece497cb6e31","user":{"login":"davidbenjamin","id":8438890,"avatar_url":"https://avatars3.githubusercontent.com/u/8438890?v=4","gravatar_id":"","url":"https://api.github.com/users/davidbenjamin","html_url":"https://github.com/davidbenjamin","followers_url":"https://api.github.com/users/davidbenjamin/followers","following_url":"https://api.github.com/users/davidbenjamin/following{/other_user}","gists_url":"https://api.github.com/users/davidbenjamin/gists{/gist_id}","starred_url":"https://api.github.com/users/davidbenjamin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davidbenjamin/subscriptions","organizations_url":"https://api.github.com/users/davidbenjamin/orgs","repos_url":"https://api.github.com/users/davidbenjamin/repos","events_url":"https://api.github.com/users/davidbenjamin/events{/privacy}","received_events_url":"https://api.github.com/users/davidbenjamin/received_events","type":"User","site_admin":false},"body":"MappingQuality is a default annotation of Mutect2 and remains useful to us.","created_at":"2017-12-12T17:03:36Z","updated_at":"2017-12-12T17:03:36Z","html_url":"https://github.com/broadinstitute/gatk/pull/3803#discussion_r156431314","pull_request_url":"https://api.github.com/repos/broadinstitute/gatk/pulls/3803","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/broadinstitute/gatk/pulls/comments/156431314"},"html":{"href":"https://github.com/broadinstitute/gatk/pull/3803#discussion_r156431314"},"pull_request":{"href":"https://api.github.com/repos/broadinstitute/gatk/pulls/3803"}},"in_reply_to_id":155877304},"pull_request":{"url":"https://api.github.com/repos/broadinstitute/gatk/pulls/3803","id":151142440,"html_url":"https://github.com/broadinstitute/gatk/pull/3803","diff_url":"https://github.com/broadinstitute/gatk/pull/3803.diff","patch_url":"https://github.com/broadinstitute/gatk/pull/3803.patch","issue_url":"https://api.github.com/repos/broadinstitute/gatk/issues/3803","number":3803,"state":"open","locked":false,"title":"Port of GATK3 Variant Annotator Tool","user":{"login":"jamesemery","id":16102845,"avatar_url":"https://avatars2.githubusercontent.com/u/16102845?v=4","gravatar_id":"","url":"https://api.github.com/users/jamesemery","html_url":"https://github.com/jamesemery","followers_url":"https://api.github.com/users/jamesemery/followers","following_url":"https://api.github.com/users/jamesemery/following{/other_user}","gists_url":"https://api.github.com/users/jamesemery/gists{/gist_id}","starred_url":"https://api.github.com/users/jamesemery/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jamesemery/subscriptions","organizations_url":"https://api.github.com/users/jamesemery/orgs","repos_url":"https://api.github.com/users/jamesemery/repos","events_url":"https://api.github.com/users/jamesemery/events{/privacy}","received_events_url":"https://api.github.com/users/jamesemery/received_events","type":"User","site_admin":false},"body":"Resolves #51","created_at":"2017-11-07T15:22:54Z","updated_at":"2017-12-12T17:03:36Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c0efcca1a0290a355bb1929e3f8e74dc1eefb2a9","assignee":{"login":"droazen","id":798637,"avatar_url":"https://avatars0.githubusercontent.com/u/798637?v=4","gravatar_id":"","url":"https://api.github.com/users/droazen","html_url":"https://github.com/droazen","followers_url":"https://api.github.com/users/droazen/followers","following_url":"https://api.github.com/users/droazen/following{/other_user}","gists_url":"https://api.github.com/users/droazen/gists{/gist_id}","starred_url":"https://api.github.com/users/droazen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/droazen/subscriptions","organizations_url":"https://api.github.com/users/droazen/orgs","repos_url":"https://api.github.com/users/droazen/repos","events_url":"https://api.github.com/users/droazen/events{/privacy}","received_events_url":"https://api.github.com/users/droazen/received_events","type":"User","site_admin":false},"assignees":[{"login":"droazen","id":798637,"avatar_url":"https://avatars0.githubusercontent.com/u/798637?v=4","gravatar_id":"","url":"https://api.github.com/users/droazen","html_url":"https://github.com/droazen","followers_url":"https://api.github.com/users/droazen/followers","following_url":"https://api.github.com/users/droazen/following{/other_user}","gists_url":"https://api.github.com/users/droazen/gists{/gist_id}","starred_url":"https://api.github.com/users/droazen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/droazen/subscriptions","organizations_url":"https://api.github.com/users/droazen/orgs","repos_url":"https://api.github.com/users/droazen/repos","events_url":"https://api.github.com/users/droazen/events{/privacy}","received_events_url":"https://api.github.com/users/droazen/received_events","type":"User","site_admin":false},{"login":"ldgauthier","id":6578548,"avatar_url":"https://avatars0.githubusercontent.com/u/6578548?v=4","gravatar_id":"","url":"https://api.github.com/users/ldgauthier","html_url":"https://github.com/ldgauthier","followers_url":"https://api.github.com/users/ldgauthier/followers","following_url":"https://api.github.com/users/ldgauthier/following{/other_user}","gists_url":"https://api.github.com/users/ldgauthier/gists{/gist_id}","starred_url":"https://api.github.com/users/ldgauthier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ldgauthier/subscriptions","organizations_url":"https://api.github.com/users/ldgauthier/orgs","repos_url":"https://api.github.com/users/ldgauthier/repos","events_url":"https://api.github.com/users/ldgauthier/events{/privacy}","received_events_url":"https://api.github.com/users/ldgauthier/received_events","type":"User","site_admin":false}],"requested_reviewers":[{"login":"droazen","id":798637,"avatar_url":"https://avatars0.githubusercontent.com/u/798637?v=4","gravatar_id":"","url":"https://api.github.com/users/droazen","html_url":"https://github.com/droazen","followers_url":"https://api.github.com/users/droazen/followers","following_url":"https://api.github.com/users/droazen/following{/other_user}","gists_url":"https://api.github.com/users/droazen/gists{/gist_id}","starred_url":"https://api.github.com/users/droazen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/droazen/subscriptions","organizations_url":"https://api.github.com/users/droazen/orgs","repos_url":"https://api.github.com/users/droazen/repos","events_url":"https://api.github.com/users/droazen/events{/privacy}","received_events_url":"https://api.github.com/users/droazen/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/broadinstitute/gatk/pulls/3803/commits","review_comments_url":"https://api.github.com/repos/broadinstitute/gatk/pulls/3803/comments","review_comment_url":"https://api.github.com/repos/broadinstitute/gatk/pulls/comments{/number}","comments_url":"https://api.github.com/repos/broadinstitute/gatk/issues/3803/comments","statuses_url":"https://api.github.com/repos/broadinstitute/gatk/statuses/c629e1cda070801ebda8dc5b78e18a558d1e4cd0","head":{"label":"broadinstitute:je_portVariantAnnotator","ref":"je_portVariantAnnotator","sha":"c629e1cda070801ebda8dc5b78e18a558d1e4cd0","user":{"login":"broadinstitute","id":393552,"avatar_url":"https://avatars0.githubusercontent.com/u/393552?v=4","gravatar_id":"","url":"https://api.github.com/users/broadinstitute","html_url":"https://github.com/broadinstitute","followers_url":"https://api.github.com/users/broadinstitute/followers","following_url":"https://api.github.com/users/broadinstitute/following{/other_user}","gists_url":"https://api.github.com/users/broadinstitute/gists{/gist_id}","starred_url":"https://api.github.com/users/broadinstitute/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/broadinstitute/subscriptions","organizations_url":"https://api.github.com/users/broadinstitute/orgs","repos_url":"https://api.github.com/users/broadinstitute/repos","events_url":"https://api.github.com/users/broadinstitute/events{/privacy}","received_events_url":"https://api.github.com/users/broadinstitute/received_events","type":"Organization","site_admin":false},"repo":{"id":27452807,"name":"gatk","full_name":"broadinstitute/gatk","owner":{"login":"broadinstitute","id":393552,"avatar_url":"https://avatars0.githubusercontent.com/u/393552?v=4","gravatar_id":"","url":"https://api.github.com/users/broadinstitute","html_url":"https://github.com/broadinstitute","followers_url":"https://api.github.com/users/broadinstitute/followers","following_url":"https://api.github.com/users/broadinstitute/following{/other_user}","gists_url":"https://api.github.com/users/broadinstitute/gists{/gist_id}","starred_url":"https://api.github.com/users/broadinstitute/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/broadinstitute/subscriptions","organizations_url":"https://api.github.com/users/broadinstitute/orgs","repos_url":"https://api.github.com/users/broadinstitute/repos","events_url":"https://api.github.com/users/broadinstitute/events{/privacy}","received_events_url":"https://api.github.com/users/broadinstitute/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/broadinstitute/gatk","description":"Official code repository for GATK versions 4 and up","fork":false,"url":"https://api.github.com/repos/broadinstitute/gatk","forks_url":"https://api.github.com/repos/broadinstitute/gatk/forks","keys_url":"https://api.github.com/repos/broadinstitute/gatk/keys{/key_id}","collaborators_url":"https://api.github.com/repos/broadinstitute/gatk/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/broadinstitute/gatk/teams","hooks_url":"https://api.github.com/repos/broadinstitute/gatk/hooks","issue_events_url":"https://api.github.com/repos/broadinstitute/gatk/issues/events{/number}","events_url":"https://api.github.com/repos/broadinstitute/gatk/events","assignees_url":"https://api.github.com/repos/broadinstitute/gatk/assignees{/user}","branches_url":"https://api.github.com/repos/broadinstitute/gatk/branches{/branch}","tags_url":"https://api.github.com/repos/broadinstitute/gatk/tags","blobs_url":"https://api.github.com/repos/broadinstitute/gatk/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/broadinstitute/gatk/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/broadinstitute/gatk/git/refs{/sha}","trees_url":"https://api.github.com/repos/broadinstitute/gatk/git/trees{/sha}","statuses_url":"https://api.github.com/repos/broadinstitute/gatk/statuses/{sha}","languages_url":"https://api.github.com/repos/broadinstitute/gatk/languages","stargazers_url":"https://api.github.com/repos/broadinstitute/gatk/stargazers","contributors_url":"https://api.github.com/repos/broadinstitute/gatk/contributors","subscribers_url":"https://api.github.com/repos/broadinstitute/gatk/subscribers","subscription_url":"https://api.github.com/repos/broadinstitute/gatk/subscription","commits_url":"https://api.github.com/repos/broadinstitute/gatk/commits{/sha}","git_commits_url":"https://api.github.com/repos/broadinstitute/gatk/git/commits{/sha}","comments_url":"https://api.github.com/repos/broadinstitute/gatk/comments{/number}","issue_comment_url":"https://api.github.com/repos/broadinstitute/gatk/issues/comments{/number}","contents_url":"https://api.github.com/repos/broadinstitute/gatk/contents/{+path}","compare_url":"https://api.github.com/repos/broadinstitute/gatk/compare/{base}...{head}","merges_url":"https://api.github.com/repos/broadinstitute/gatk/merges","archive_url":"https://api.github.com/repos/broadinstitute/gatk/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/broadinstitute/gatk/downloads","issues_url":"https://api.github.com/repos/broadinstitute/gatk/issues{/number}","pulls_url":"https://api.github.com/repos/broadinstitute/gatk/pulls{/number}","milestones_url":"https://api.github.com/repos/broadinstitute/gatk/milestones{/number}","notifications_url":"https://api.github.com/repos/broadinstitute/gatk/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/broadinstitute/gatk/labels{/name}","releases_url":"https://api.github.com/repos/broadinstitute/gatk/releases{/id}","deployments_url":"https://api.github.com/repos/broadinstitute/gatk/deployments","created_at":"2014-12-02T20:50:36Z","updated_at":"2017-12-10T22:10:50Z","pushed_at":"2017-12-12T16:52:15Z","git_url":"git://github.com/broadinstitute/gatk.git","ssh_url":"git@github.com:broadinstitute/gatk.git","clone_url":"https://github.com/broadinstitute/gatk.git","svn_url":"https://github.com/broadinstitute/gatk","homepage":"https://software.broadinstitute.org/gatk","size":168285,"stargazers_count":273,"watchers_count":273,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":108,"mirror_url":null,"archived":false,"open_issues_count":822,"license":{"key":"bsd-3-clause","name":"BSD 3-clause \"New\" or \"Revised\" License","spdx_id":"BSD-3-Clause","url":"https://api.github.com/licenses/bsd-3-clause"},"forks":108,"open_issues":822,"watchers":273,"default_branch":"master"}},"base":{"label":"broadinstitute:master","ref":"master","sha":"fe47cf66a64b6aeb3136a43277f500bfe5dd9d6d","user":{"login":"broadinstitute","id":393552,"avatar_url":"https://avatars0.githubusercontent.com/u/393552?v=4","gravatar_id":"","url":"https://api.github.com/users/broadinstitute","html_url":"https://github.com/broadinstitute","followers_url":"https://api.github.com/users/broadinstitute/followers","following_url":"https://api.github.com/users/broadinstitute/following{/other_user}","gists_url":"https://api.github.com/users/broadinstitute/gists{/gist_id}","starred_url":"https://api.github.com/users/broadinstitute/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/broadinstitute/subscriptions","organizations_url":"https://api.github.com/users/broadinstitute/orgs","repos_url":"https://api.github.com/users/broadinstitute/repos","events_url":"https://api.github.com/users/broadinstitute/events{/privacy}","received_events_url":"https://api.github.com/users/broadinstitute/received_events","type":"Organization","site_admin":false},"repo":{"id":27452807,"name":"gatk","full_name":"broadinstitute/gatk","owner":{"login":"broadinstitute","id":393552,"avatar_url":"https://avatars0.githubusercontent.com/u/393552?v=4","gravatar_id":"","url":"https://api.github.com/users/broadinstitute","html_url":"https://github.com/broadinstitute","followers_url":"https://api.github.com/users/broadinstitute/followers","following_url":"https://api.github.com/users/broadinstitute/following{/other_user}","gists_url":"https://api.github.com/users/broadinstitute/gists{/gist_id}","starred_url":"https://api.github.com/users/broadinstitute/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/broadinstitute/subscriptions","organizations_url":"https://api.github.com/users/broadinstitute/orgs","repos_url":"https://api.github.com/users/broadinstitute/repos","events_url":"https://api.github.com/users/broadinstitute/events{/privacy}","received_events_url":"https://api.github.com/users/broadinstitute/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/broadinstitute/gatk","description":"Official code repository for GATK versions 4 and up","fork":false,"url":"https://api.github.com/repos/broadinstitute/gatk","forks_url":"https://api.github.com/repos/broadinstitute/gatk/forks","keys_url":"https://api.github.com/repos/broadinstitute/gatk/keys{/key_id}","collaborators_url":"https://api.github.com/repos/broadinstitute/gatk/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/broadinstitute/gatk/teams","hooks_url":"https://api.github.com/repos/broadinstitute/gatk/hooks","issue_events_url":"https://api.github.com/repos/broadinstitute/gatk/issues/events{/number}","events_url":"https://api.github.com/repos/broadinstitute/gatk/events","assignees_url":"https://api.github.com/repos/broadinstitute/gatk/assignees{/user}","branches_url":"https://api.github.com/repos/broadinstitute/gatk/branches{/branch}","tags_url":"https://api.github.com/repos/broadinstitute/gatk/tags","blobs_url":"https://api.github.com/repos/broadinstitute/gatk/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/broadinstitute/gatk/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/broadinstitute/gatk/git/refs{/sha}","trees_url":"https://api.github.com/repos/broadinstitute/gatk/git/trees{/sha}","statuses_url":"https://api.github.com/repos/broadinstitute/gatk/statuses/{sha}","languages_url":"https://api.github.com/repos/broadinstitute/gatk/languages","stargazers_url":"https://api.github.com/repos/broadinstitute/gatk/stargazers","contributors_url":"https://api.github.com/repos/broadinstitute/gatk/contributors","subscribers_url":"https://api.github.com/repos/broadinstitute/gatk/subscribers","subscription_url":"https://api.github.com/repos/broadinstitute/gatk/subscription","commits_url":"https://api.github.com/repos/broadinstitute/gatk/commits{/sha}","git_commits_url":"https://api.github.com/repos/broadinstitute/gatk/git/commits{/sha}","comments_url":"https://api.github.com/repos/broadinstitute/gatk/comments{/number}","issue_comment_url":"https://api.github.com/repos/broadinstitute/gatk/issues/comments{/number}","contents_url":"https://api.github.com/repos/broadinstitute/gatk/contents/{+path}","compare_url":"https://api.github.com/repos/broadinstitute/gatk/compare/{base}...{head}","merges_url":"https://api.github.com/repos/broadinstitute/gatk/merges","archive_url":"https://api.github.com/repos/broadinstitute/gatk/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/broadinstitute/gatk/downloads","issues_url":"https://api.github.com/repos/broadinstitute/gatk/issues{/number}","pulls_url":"https://api.github.com/repos/broadinstitute/gatk/pulls{/number}","milestones_url":"https://api.github.com/repos/broadinstitute/gatk/milestones{/number}","notifications_url":"https://api.github.com/repos/broadinstitute/gatk/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/broadinstitute/gatk/labels{/name}","releases_url":"https://api.github.com/repos/broadinstitute/gatk/releases{/id}","deployments_url":"https://api.github.com/repos/broadinstitute/gatk/deployments","created_at":"2014-12-02T20:50:36Z","updated_at":"2017-12-10T22:10:50Z","pushed_at":"2017-12-12T16:52:15Z","git_url":"git://github.com/broadinstitute/gatk.git","ssh_url":"git@github.com:broadinstitute/gatk.git","clone_url":"https://github.com/broadinstitute/gatk.git","svn_url":"https://github.com/broadinstitute/gatk","homepage":"https://software.broadinstitute.org/gatk","size":168285,"stargazers_count":273,"watchers_count":273,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":108,"mirror_url":null,"archived":false,"open_issues_count":822,"license":{"key":"bsd-3-clause","name":"BSD 3-clause \"New\" or \"Revised\" License","spdx_id":"BSD-3-Clause","url":"https://api.github.com/licenses/bsd-3-clause"},"forks":108,"open_issues":822,"watchers":273,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/broadinstitute/gatk/pulls/3803"},"html":{"href":"https://github.com/broadinstitute/gatk/pull/3803"},"issue":{"href":"https://api.github.com/repos/broadinstitute/gatk/issues/3803"},"comments":{"href":"https://api.github.com/repos/broadinstitute/gatk/issues/3803/comments"},"review_comments":{"href":"https://api.github.com/repos/broadinstitute/gatk/pulls/3803/comments"},"review_comment":{"href":"https://api.github.com/repos/broadinstitute/gatk/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/broadinstitute/gatk/pulls/3803/commits"},"statuses":{"href":"https://api.github.com/repos/broadinstitute/gatk/statuses/c629e1cda070801ebda8dc5b78e18a558d1e4cd0"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"davidbenjamin"}}