JAL-3576 IntervalStore updated to v1.2
[jalview.git] / test / jalview / io / vcf / VCFLoaderTest.java
1 package jalview.io.vcf;
2
3 import static jalview.io.gff.SequenceOntologyI.SEQUENCE_VARIANT;
4 import static org.testng.Assert.assertEquals;
5 import static org.testng.Assert.assertNull;
6 import static org.testng.Assert.assertTrue;
7
8 import java.io.File;
9 import java.io.IOException;
10 import java.io.PrintWriter;
11 import java.util.List;
12 import java.util.Map;
13
14 import org.testng.annotations.BeforeClass;
15 import org.testng.annotations.BeforeTest;
16 import org.testng.annotations.Test;
17
18 import jalview.bin.Cache;
19 import jalview.datamodel.AlignmentI;
20 import jalview.datamodel.DBRefEntry;
21 import jalview.datamodel.Mapping;
22 import jalview.datamodel.Sequence;
23 import jalview.datamodel.SequenceFeature;
24 import jalview.datamodel.SequenceI;
25 import jalview.datamodel.features.FeatureAttributes;
26 import jalview.datamodel.features.SequenceFeatures;
27 import jalview.gui.AlignFrame;
28 import jalview.io.DataSourceType;
29 import jalview.io.FileLoader;
30 import jalview.io.gff.Gff3Helper;
31 import jalview.util.MapList;
32
33 public class VCFLoaderTest
34 {
35   private static final float DELTA = 0.00001f;
36
37   // columns 9717- of gene P30419 from Ensembl (much modified)
38   private static final String FASTA = ""
39           +
40           /*
41            * forward strand 'gene' and 'transcript' with two exons
42            */
43           ">gene1/1-25 chromosome:GRCh38:17:45051610:45051634:1\n"
44           + "CAAGCTGGCGGACGAGAGTGTGACA\n"
45           + ">transcript1/1-18\n--AGCTGGCG----AGAGTGTGAC-\n"
46
47           /*
48            * reverse strand gene and transcript (reverse complement alleles!)
49            */
50           + ">gene2/1-25 chromosome:GRCh38:17:45051610:45051634:-1\n"
51           + "TGTCACACTCTCGTCCGCCAGCTTG\n"
52           + ">transcript2/1-18\n" + "-GTCACACTCT----CGCCAGCT--\n"
53
54           /*
55            * 'gene' on chromosome 5 with two transcripts
56            */
57           + ">gene3/1-25 chromosome:GRCh38:5:45051610:45051634:1\n"
58           + "CAAGCTGGCGGACGAGAGTGTGACA\n"
59           + ">transcript3/1-18\n--AGCTGGCG----AGAGTGTGAC-\n"
60           + ">transcript4/1-18\n-----TGG-GGACGAGAGTGTGA-A\n";
61
62   private static final String[] VCF = { "##fileformat=VCFv4.2",
63       // fields other than AF are ignored when parsing as they have no INFO definition
64       "##INFO=<ID=AF,Number=A,Type=Float,Description=\"Allele Frequency, for each ALT allele, in the same order as listed\">",
65       "##INFO=<ID=AC_Female,Number=A,Type=Integer,Description=\"Allele count in Female genotypes\"",
66       "##INFO=<ID=AF_AFR,Number=A,Type=Float,Description=\"Allele Frequency among African/African American genotypes\"",
67       "##reference=Homo_sapiens/GRCh38",
68       "#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO",
69       // A/T,C variants in position 2 of gene sequence (precedes transcript)
70       // should create 2 variant features with respective AF values
71       // malformed values for AC_Female and AF_AFR should be ignored
72       "17\t45051611\trs384765\tA\tT,C\t1666.64\tRF;XYZ\tAC=15;AF=5.0e-03,4.0e-03;AC_Female=12,3d;AF_AFR=low,2.3e-4",
73       // SNP G/C in position 4 of gene sequence, position 2 of transcript
74       // insertion G/GA is transferred to nucleotide but not to peptide
75       "17\t45051613\t.\tG\tGA,C\t1666.65\t.\tAC=15;AF=3.0e-03,2.0e-03",
76       // '.' in INFO field should be ignored
77       "17\t45051615\t.\tG\tC\t1666.66\tRF\tAC=16;AF=." };
78
79   @BeforeClass(alwaysRun = true)
80   public void setUp()
81   {
82     /*
83      * configure to capture all available VCF and VEP (CSQ) fields
84      */
85     Cache.loadProperties("test/jalview/io/testProps.jvprops");
86     Cache.setProperty("VCF_FIELDS", ".*");
87     Cache.setProperty("VEP_FIELDS", ".*");
88     Cache.setProperty("VCF_ASSEMBLY", "GRCh38=GRCh38");
89     Cache.initLogger();
90   }
91
92   @BeforeTest(alwaysRun = true)
93   public void setUpBeforeTest()
94   {
95     /*
96      * clear down feature attributes metadata
97      */
98     FeatureAttributes.getInstance().clear();
99   }
100
101   @Test(groups = "Functional")
102   public void testDoLoad() throws IOException
103   {
104     AlignmentI al = buildAlignment();
105
106     File f = makeVcfFile();
107     VCFLoader loader = new VCFLoader(f.getPath());
108
109     loader.doLoad(al.getSequencesArray(), null);
110
111     /*
112      * verify variant feature(s) added to gene
113      * NB alleles at a locus may not be processed, and features added,
114      * in the order in which they appear in the VCF record as method
115      * VariantContext.getAlternateAlleles() does not guarantee order
116      * - order of assertions here matches what we find (is not important) 
117      */
118     List<SequenceFeature> geneFeatures = al.getSequenceAt(0)
119             .getSequenceFeatures();
120     SequenceFeatures.sortFeatures(geneFeatures, true);
121     assertEquals(geneFeatures.size(), 5);
122
123     SequenceFeature sf = geneFeatures.get(0);
124     assertEquals(sf.getFeatureGroup(), "VCF");
125     assertEquals(sf.getBegin(), 2);
126     assertEquals(sf.getEnd(), 2);
127     assertEquals(sf.getType(), SEQUENCE_VARIANT);
128     assertEquals(sf.getScore(), 0f);
129     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 5.0e-03,
130             DELTA);
131     assertEquals(sf.getValue("AC_Female"), "12");
132     // malformed float for AF_AFR is ignored (JAL-3375)
133     assertNull(sf.getValue("AC_AFR"));
134     assertEquals(sf.getValue(Gff3Helper.ALLELES), "A,T");
135
136     sf = geneFeatures.get(1);
137     assertEquals(sf.getFeatureGroup(), "VCF");
138     assertEquals(sf.getBegin(), 2);
139     assertEquals(sf.getEnd(), 2);
140     assertEquals(sf.getScore(), 0f);
141     assertEquals(sf.getValue("AF"), "4.0e-03");
142     assertEquals(sf.getValue("AF_AFR"), "2.3e-4");
143     assertEquals(sf.getValue(Gff3Helper.ALLELES), "A,C");
144     assertEquals(sf.getType(), SEQUENCE_VARIANT);
145     assertEquals(sf.getValue("POS"), "45051611");
146     assertEquals(sf.getValue("ID"), "rs384765");
147     assertEquals(sf.getValue("QUAL"), "1666.64");
148     assertEquals(sf.getValue("FILTER"), "RF;XYZ");
149     // malformed integer for AC_Female is ignored (JAL-3375)
150     assertNull(sf.getValue("AC_Female"));
151
152     sf = geneFeatures.get(2);
153     assertEquals(sf.getFeatureGroup(), "VCF");
154     assertEquals(sf.getBegin(), 4);
155     assertEquals(sf.getEnd(), 4);
156     assertEquals(sf.getType(), SEQUENCE_VARIANT);
157     assertEquals(sf.getScore(), 0f);
158     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 3.0e-03,
159             DELTA);
160     assertEquals(sf.getValue(Gff3Helper.ALLELES), "G,GA");
161     assertNull(sf.getValue("ID")); // '.' is ignored
162     assertNull(sf.getValue("FILTER")); // '.' is ignored
163
164     sf = geneFeatures.get(3);
165     assertEquals(sf.getFeatureGroup(), "VCF");
166     assertEquals(sf.getBegin(), 4);
167     assertEquals(sf.getEnd(), 4);
168     assertEquals(sf.getType(), SEQUENCE_VARIANT);
169     assertEquals(sf.getScore(), 0f);
170     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 2.0e-03,
171             DELTA);
172     assertEquals(sf.getValue(Gff3Helper.ALLELES), "G,C");
173
174     sf = geneFeatures.get(4);
175     assertEquals(sf.getFeatureGroup(), "VCF");
176     assertEquals(sf.getBegin(), 6);
177     assertEquals(sf.getEnd(), 6);
178     assertEquals(sf.getType(), SEQUENCE_VARIANT);
179     assertEquals(sf.getScore(), 0f);
180     // AF=. should not have been captured
181     assertNull(sf.getValue("AF"));
182     assertEquals(sf.getValue(Gff3Helper.ALLELES), "G,C");
183
184     /*
185      * verify variant feature(s) added to transcript
186      */
187     List<SequenceFeature> transcriptFeatures = al.getSequenceAt(1)
188             .getSequenceFeatures();
189     assertEquals(transcriptFeatures.size(), 3);
190     
191     sf = transcriptFeatures.get(0);
192     assertEquals(sf.getFeatureGroup(), "VCF");
193     assertEquals(sf.getBegin(), 2);
194     assertEquals(sf.getEnd(), 2);
195     assertEquals(sf.getType(), SEQUENCE_VARIANT);
196     assertEquals(sf.getScore(), 0f);
197     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 3.0e-03,
198             DELTA);
199     assertEquals(sf.getValue(Gff3Helper.ALLELES), "G,GA");
200     
201     sf = transcriptFeatures.get(1);
202     assertEquals(sf.getFeatureGroup(), "VCF");
203     assertEquals(sf.getBegin(), 2);
204     assertEquals(sf.getEnd(), 2);
205     assertEquals(sf.getType(), SEQUENCE_VARIANT);
206     assertEquals(sf.getScore(), 0f);
207     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 2.0e-03,
208             DELTA);
209     assertEquals(sf.getValue(Gff3Helper.ALLELES), "G,C");
210
211     /*
212      * verify SNP variant feature(s) computed and added to protein
213      * first codon AGC varies to ACC giving S/T
214      */
215     List<DBRefEntry> dbRefs = al.getSequenceAt(1).getDBRefs();
216     SequenceI peptide = null;
217     for (DBRefEntry dbref : dbRefs)
218     {
219       if (dbref.getMap().getMap().getFromRatio() == 3)
220       {
221         peptide = dbref.getMap().getTo();
222       }
223     }
224     List<SequenceFeature> proteinFeatures = peptide.getSequenceFeatures();
225
226     /*
227      * JAL-3187 don't precompute protein features, do dynamically instead
228      */
229     assertTrue(proteinFeatures.isEmpty());
230   }
231
232   private File makeVcfFile() throws IOException
233   {
234     File f = File.createTempFile("Test", ".vcf");
235     f.deleteOnExit();
236     PrintWriter pw = new PrintWriter(f);
237     for (String vcfLine : VCF)
238     {
239       pw.println(vcfLine);
240     }
241     pw.close();
242     return f;
243   }
244
245   /**
246    * Make a simple alignment with one 'gene' and one 'transcript'
247    * 
248    * @return
249    */
250   private AlignmentI buildAlignment()
251   {
252     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(FASTA,
253             DataSourceType.PASTE);
254
255     /*
256      * map gene1 sequence to chromosome (normally done when the sequence is fetched
257      * from Ensembl and transcripts computed)
258      */
259     AlignmentI alignment = af.getViewport().getAlignment();
260     SequenceI gene1 = alignment.findName("gene1");
261     int[] to = new int[] { 45051610, 45051634 };
262     int[] from = new int[] { gene1.getStart(), gene1.getEnd() };
263     gene1.setGeneLoci("homo_sapiens", "GRCh38", "17", new MapList(from, to,
264             1, 1));
265
266     /*
267      * map 'transcript1' to chromosome via 'gene1'
268      * transcript1/1-18 is gene1/3-10,15-24
269      * which is chromosome 45051612-45051619,45051624-45051633
270      */
271     to = new int[] { 45051612, 45051619, 45051624, 45051633 };
272     SequenceI transcript1 = alignment.findName("transcript1");
273     from = new int[] { transcript1.getStart(), transcript1.getEnd() };
274     transcript1.setGeneLoci("homo_sapiens", "GRCh38", "17", new MapList(
275             from, to,
276             1, 1));
277
278     /*
279      * map gene2 to chromosome reverse strand
280      */
281     SequenceI gene2 = alignment.findName("gene2");
282     to = new int[] { 45051634, 45051610 };
283     from = new int[] { gene2.getStart(), gene2.getEnd() };
284     gene2.setGeneLoci("homo_sapiens", "GRCh38", "17", new MapList(from, to,
285             1, 1));
286
287     /*
288      * map 'transcript2' to chromosome via 'gene2'
289      * transcript2/1-18 is gene2/2-11,16-23
290      * which is chromosome 45051633-45051624,45051619-45051612
291      */
292     to = new int[] { 45051633, 45051624, 45051619, 45051612 };
293     SequenceI transcript2 = alignment.findName("transcript2");
294     from = new int[] { transcript2.getStart(), transcript2.getEnd() };
295     transcript2.setGeneLoci("homo_sapiens", "GRCh38", "17", new MapList(
296             from, to,
297             1, 1));
298
299     /*
300      * add a protein product as a DBRef on transcript1
301      */
302     SequenceI peptide1 = new Sequence("ENSP001", "SWRECD");
303     MapList mapList = new MapList(new int[] { 1, 18 }, new int[] { 1, 6 },
304             3, 1);
305     Mapping map = new Mapping(peptide1, mapList);
306     DBRefEntry product = new DBRefEntry("", "", "ENSP001", map);
307     transcript1.addDBRef(product);
308
309     /*
310      * add a protein product as a DBRef on transcript2
311      */
312     SequenceI peptide2 = new Sequence("ENSP002", "VTLSPA");
313     mapList = new MapList(new int[] { 1, 18 }, new int[] { 1, 6 }, 3, 1);
314     map = new Mapping(peptide2, mapList);
315     product = new DBRefEntry("", "", "ENSP002", map);
316     transcript2.addDBRef(product);
317
318     /*
319      * map gene3 to chromosome 
320      */
321     SequenceI gene3 = alignment.findName("gene3");
322     to = new int[] { 45051610, 45051634 };
323     from = new int[] { gene3.getStart(), gene3.getEnd() };
324     gene3.setGeneLoci("homo_sapiens", "GRCh38", "5", new MapList(from, to,
325             1, 1));
326
327     /*
328      * map 'transcript3' to chromosome
329      */
330     SequenceI transcript3 = alignment.findName("transcript3");
331     to = new int[] { 45051612, 45051619, 45051624, 45051633 };
332     from = new int[] { transcript3.getStart(), transcript3.getEnd() };
333     transcript3.setGeneLoci("homo_sapiens", "GRCh38", "5", new MapList(
334             from, to,
335             1, 1));
336
337     /*
338      * map 'transcript4' to chromosome
339      */
340     SequenceI transcript4 = alignment.findName("transcript4");
341     to = new int[] { 45051615, 45051617, 45051619, 45051632, 45051634,
342         45051634 };
343     from = new int[] { transcript4.getStart(), transcript4.getEnd() };
344     transcript4.setGeneLoci("homo_sapiens", "GRCh38", "5", new MapList(
345             from, to,
346             1, 1));
347
348     /*
349      * add a protein product as a DBRef on transcript3
350      */
351     SequenceI peptide3 = new Sequence("ENSP003", "SWRECD");
352     mapList = new MapList(new int[] { 1, 18 }, new int[] { 1, 6 }, 3, 1);
353     map = new Mapping(peptide3, mapList);
354     product = new DBRefEntry("", "", "ENSP003", map);
355     transcript3.addDBRef(product);
356
357     return alignment;
358   }
359
360   /**
361    * Test with 'gene' and 'transcript' mapped to the reverse strand of the
362    * chromosome. The VCF variant positions (in forward coordinates) should get
363    * correctly located on sequence positions.
364    * 
365    * @throws IOException
366    */
367   @Test(groups = "Functional")
368   public void testDoLoad_reverseStrand() throws IOException
369   {
370     AlignmentI al = buildAlignment();
371
372     File f = makeVcfFile();
373
374     VCFLoader loader = new VCFLoader(f.getPath());
375
376     loader.doLoad(al.getSequencesArray(), null);
377
378     /*
379      * verify variant feature(s) added to gene2
380      * gene2/1-25 maps to chromosome 45051634- reverse strand
381      */
382     List<SequenceFeature> geneFeatures = al.getSequenceAt(2)
383             .getSequenceFeatures();
384     SequenceFeatures.sortFeatures(geneFeatures, true);
385     assertEquals(geneFeatures.size(), 5);
386     SequenceFeature sf;
387
388     /*
389      * insertion G/GA at 45051613 maps to an insertion at
390      * the preceding position (21) on reverse strand gene
391      * reference: CAAGC -> GCTTG/21-25
392      * genomic variant: CAAGAC (G/GA)
393      * gene variant: GTCTTG (G/GT at 21)
394      */
395     sf = geneFeatures.get(1);
396     assertEquals(sf.getFeatureGroup(), "VCF");
397     assertEquals(sf.getBegin(), 21);
398     assertEquals(sf.getEnd(), 21);
399     assertEquals(sf.getType(), SEQUENCE_VARIANT);
400     assertEquals(sf.getScore(), 0f);
401     assertEquals(sf.getValue(Gff3Helper.ALLELES), "G,GT");
402     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 3.0e-03,
403             DELTA);
404
405     /*
406      * variant G/C at 45051613 maps to C/G at gene position 22
407      */
408     sf = geneFeatures.get(2);
409     assertEquals(sf.getFeatureGroup(), "VCF");
410     assertEquals(sf.getBegin(), 22);
411     assertEquals(sf.getEnd(), 22);
412     assertEquals(sf.getType(), SEQUENCE_VARIANT);
413     assertEquals(sf.getScore(), 0f);
414     assertEquals(sf.getValue(Gff3Helper.ALLELES), "C,G");
415     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 2.0e-03,
416             DELTA);
417
418     /*
419      * variant A/T at 45051611 maps to T/A at gene position 24
420      */
421     sf = geneFeatures.get(3);
422     assertEquals(sf.getFeatureGroup(), "VCF");
423     assertEquals(sf.getBegin(), 24);
424     assertEquals(sf.getEnd(), 24);
425     assertEquals(sf.getType(), SEQUENCE_VARIANT);
426     assertEquals(sf.getScore(), 0f);
427     assertEquals(sf.getValue(Gff3Helper.ALLELES), "T,A");
428     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 5.0e-03,
429             DELTA);
430
431     /*
432      * variant A/C at 45051611 maps to T/G at gene position 24
433      */
434     sf = geneFeatures.get(4);
435     assertEquals(sf.getFeatureGroup(), "VCF");
436     assertEquals(sf.getBegin(), 24);
437     assertEquals(sf.getEnd(), 24);
438     assertEquals(sf.getType(), SEQUENCE_VARIANT);
439     assertEquals(sf.getScore(), 0f);
440     assertEquals(sf.getValue(Gff3Helper.ALLELES), "T,G");
441     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 4.0e-03,
442             DELTA);
443
444     /*
445      * verify 3 variant features added to transcript2
446      */
447     List<SequenceFeature> transcriptFeatures = al.getSequenceAt(3)
448             .getSequenceFeatures();
449     assertEquals(transcriptFeatures.size(), 3);
450
451     /*
452      * insertion G/GT at position 21 of gene maps to position 16 of transcript
453      */
454     sf = transcriptFeatures.get(1);
455     assertEquals(sf.getFeatureGroup(), "VCF");
456     assertEquals(sf.getBegin(), 16);
457     assertEquals(sf.getEnd(), 16);
458     assertEquals(sf.getType(), SEQUENCE_VARIANT);
459     assertEquals(sf.getScore(), 0f);
460     assertEquals(sf.getValue(Gff3Helper.ALLELES), "G,GT");
461     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 3.0e-03,
462             DELTA);
463
464     /*
465      * SNP C/G at position 22 of gene maps to position 17 of transcript
466      */
467     sf = transcriptFeatures.get(2);
468     assertEquals(sf.getFeatureGroup(), "VCF");
469     assertEquals(sf.getBegin(), 17);
470     assertEquals(sf.getEnd(), 17);
471     assertEquals(sf.getType(), SEQUENCE_VARIANT);
472     assertEquals(sf.getScore(), 0f);
473     assertEquals(sf.getValue(Gff3Helper.ALLELES), "C,G");
474     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 2.0e-03,
475             DELTA);
476
477     /*
478      * verify variant feature(s) computed and added to protein
479      * last codon GCT varies to GGT giving A/G in the last peptide position
480      */
481     List<DBRefEntry> dbRefs = al.getSequenceAt(3).getDBRefs();
482     SequenceI peptide = null;
483     for (DBRefEntry dbref : dbRefs)
484     {
485       if (dbref.getMap().getMap().getFromRatio() == 3)
486       {
487         peptide = dbref.getMap().getTo();
488       }
489     }
490     List<SequenceFeature> proteinFeatures = peptide.getSequenceFeatures();
491
492     /*
493      * JAL-3187 don't precompute protein features, do dynamically instead
494      */
495     assertTrue(proteinFeatures.isEmpty());
496   }
497
498   /**
499    * Tests that if VEP consequence (CSQ) data is present in the VCF data, then
500    * it is added to the variant feature, but restricted where possible to the
501    * consequences for a specific transcript
502    * 
503    * @throws IOException
504    */
505   @Test(groups = "Functional")
506   public void testDoLoad_vepCsq() throws IOException
507   {
508     AlignmentI al = buildAlignment();
509
510     VCFLoader loader = new VCFLoader("test/jalview/io/vcf/testVcf.vcf");
511
512     /*
513      * VCF data file with variants at gene3 positions
514      * 1 C/A
515      * 5 C/T
516      * 9 CGT/C (deletion)
517      * 13 C/G, C/T
518      * 17 A/AC (insertion), A/G
519      */
520     loader.doLoad(al.getSequencesArray(), null);
521
522     /*
523      * verify variant feature(s) added to gene3
524      */
525     List<SequenceFeature> geneFeatures = al.findName("gene3")
526             .getSequenceFeatures();
527     SequenceFeatures.sortFeatures(geneFeatures, true);
528     assertEquals(geneFeatures.size(), 7);
529     SequenceFeature sf = geneFeatures.get(0);
530     assertEquals(sf.getBegin(), 1);
531     assertEquals(sf.getEnd(), 1);
532     assertEquals(sf.getScore(), 0f);
533     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.1f, DELTA);
534     assertEquals(sf.getValue("alleles"), "C,A");
535     // gene features include Consequence for all transcripts
536     Map map = (Map) sf.getValue("CSQ");
537     assertEquals(map.size(), 9);
538     assertEquals(map.get("PolyPhen"), "Bad");
539
540     sf = geneFeatures.get(1);
541     assertEquals(sf.getBegin(), 5);
542     assertEquals(sf.getEnd(), 5);
543     assertEquals(sf.getScore(), 0f);
544     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.2f, DELTA);
545     assertEquals(sf.getValue("alleles"), "C,T");
546     map = (Map) sf.getValue("CSQ");
547     assertEquals(map.size(), 9);
548     assertEquals(map.get("PolyPhen"), "Bad;;"); // %3B%3B decoded
549
550     sf = geneFeatures.get(2);
551     assertEquals(sf.getBegin(), 9);
552     assertEquals(sf.getEnd(), 11); // deletion over 3 positions
553     assertEquals(sf.getScore(), 0f);
554     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.3f, DELTA);
555     assertEquals(sf.getValue("alleles"), "CGG,C");
556     map = (Map) sf.getValue("CSQ");
557     assertEquals(map.size(), 9);
558
559     sf = geneFeatures.get(3);
560     assertEquals(sf.getBegin(), 13);
561     assertEquals(sf.getEnd(), 13);
562     assertEquals(sf.getScore(), 0f);
563     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.4f, DELTA);
564     assertEquals(sf.getValue("alleles"), "C,G");
565     map = (Map) sf.getValue("CSQ");
566     assertEquals(map.size(), 9);
567
568     sf = geneFeatures.get(4);
569     assertEquals(sf.getBegin(), 13);
570     assertEquals(sf.getEnd(), 13);
571     assertEquals(sf.getScore(), 0f);
572     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.5f, DELTA);
573     assertEquals(sf.getValue("alleles"), "C,T");
574     map = (Map) sf.getValue("CSQ");
575     assertEquals(map.size(), 9);
576
577     sf = geneFeatures.get(5);
578     assertEquals(sf.getBegin(), 17);
579     assertEquals(sf.getEnd(), 17);
580     assertEquals(sf.getScore(), 0f);
581     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.6f, DELTA);
582     assertEquals(sf.getValue("alleles"), "A,AC");
583     map = (Map) sf.getValue("CSQ");
584     assertEquals(map.size(), 9);
585
586     sf = geneFeatures.get(6);
587     assertEquals(sf.getBegin(), 17);
588     assertEquals(sf.getEnd(), 17); // insertion
589     assertEquals(sf.getScore(), 0f);
590     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.7f, DELTA);
591     assertEquals(sf.getValue("alleles"), "A,G");
592     map = (Map) sf.getValue("CSQ");
593     assertEquals(map.size(), 9);
594
595     /*
596      * verify variant feature(s) added to transcript3
597      * at columns 5 (1), 17 (2), positions 3, 11
598      * note the deletion at columns 9-11 is not transferred since col 11
599      * has no mapping to transcript 3 
600      */
601     List<SequenceFeature> transcriptFeatures = al.findName("transcript3")
602             .getSequenceFeatures();
603     SequenceFeatures.sortFeatures(transcriptFeatures, true);
604     assertEquals(transcriptFeatures.size(), 3);
605     sf = transcriptFeatures.get(0);
606     assertEquals(sf.getBegin(), 3);
607     assertEquals(sf.getEnd(), 3);
608     assertEquals(sf.getScore(), 0f);
609     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.2f, DELTA);
610     assertEquals(sf.getValue("alleles"), "C,T");
611     // transcript features only have Consequence for that transcripts
612     map = (Map) sf.getValue("CSQ");
613     assertEquals(map.size(), 9);
614     assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript3");
615
616     sf = transcriptFeatures.get(1);
617     assertEquals(sf.getBegin(), 11);
618     assertEquals(sf.getEnd(), 11);
619     assertEquals(sf.getScore(), 0f);
620     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.6f, DELTA);
621     assertEquals(sf.getValue("alleles"), "A,AC");
622     map = (Map) sf.getValue("CSQ");
623     assertEquals(map.size(), 9);
624     assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript3");
625
626     sf = transcriptFeatures.get(2);
627     assertEquals(sf.getBegin(), 11);
628     assertEquals(sf.getEnd(), 11);
629     assertEquals(sf.getScore(), 0f);
630     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.7f, DELTA);
631     assertEquals(sf.getValue("alleles"), "A,G");
632     map = (Map) sf.getValue("CSQ");
633     assertEquals(map.size(), 9);
634     assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript3");
635
636     /*
637      * verify variants computed on protein product for transcript3
638      * peptide is SWRECD
639      * codon variants are AGC/AGT position 1 which is synonymous
640      * and GAG/GGG which is E/G in position 4
641      * the insertion variant is not transferred to the peptide
642      */
643     List<DBRefEntry> dbRefs = al.findName("transcript3").getDBRefs();
644     SequenceI peptide = null;
645     for (DBRefEntry dbref : dbRefs)
646     {
647       if (dbref.getMap().getMap().getFromRatio() == 3)
648       {
649         peptide = dbref.getMap().getTo();
650       }
651     }
652     List<SequenceFeature> proteinFeatures = peptide.getSequenceFeatures();
653     /*
654      * JAL-3187 don't precompute protein features, do dynamically instead
655      */
656     assertTrue(proteinFeatures.isEmpty());
657     // SequenceFeatures.sortFeatures(proteinFeatures, true);
658     // assertEquals(proteinFeatures.size(), 2);
659     // sf = proteinFeatures.get(0);
660     // assertEquals(sf.getFeatureGroup(), "VCF");
661     // assertEquals(sf.getBegin(), 1);
662     // assertEquals(sf.getEnd(), 1);
663     // assertEquals(sf.getType(), SequenceOntologyI.SYNONYMOUS_VARIANT);
664     // assertEquals(sf.getDescription(), "agC/agT");
665     // sf = proteinFeatures.get(1);
666     // assertEquals(sf.getFeatureGroup(), "VCF");
667     // assertEquals(sf.getBegin(), 4);
668     // assertEquals(sf.getEnd(), 4);
669     // assertEquals(sf.getType(), SequenceOntologyI.NONSYNONYMOUS_VARIANT);
670     // assertEquals(sf.getDescription(), "p.Glu4Gly");
671
672     /*
673      * verify variant feature(s) added to transcript4
674      * at columns 13 (2) and 17 (2), positions 7 and 11
675      */
676     transcriptFeatures = al.findName("transcript4").getSequenceFeatures();
677     SequenceFeatures.sortFeatures(transcriptFeatures, true);
678     assertEquals(transcriptFeatures.size(), 4);
679     sf = transcriptFeatures.get(0);
680     assertEquals(sf.getBegin(), 7);
681     assertEquals(sf.getEnd(), 7);
682     assertEquals(sf.getScore(), 0f);
683     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.4f, DELTA);
684     assertEquals(sf.getValue("alleles"), "C,G");
685     map = (Map) sf.getValue("CSQ");
686     assertEquals(map.size(), 9);
687     assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript4");
688
689     sf = transcriptFeatures.get(1);
690     assertEquals(sf.getBegin(), 7);
691     assertEquals(sf.getEnd(), 7);
692     assertEquals(sf.getScore(), 0f);
693     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.5f, DELTA);
694     assertEquals(sf.getValue("alleles"), "C,T");
695     map = (Map) sf.getValue("CSQ");
696     assertEquals(map.size(), 9);
697     assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript4");
698
699     sf = transcriptFeatures.get(2);
700     assertEquals(sf.getBegin(), 11);
701     assertEquals(sf.getEnd(), 11);
702     assertEquals(sf.getScore(), 0f);
703     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.6f, DELTA);
704     assertEquals(sf.getValue("alleles"), "A,AC");
705     map = (Map) sf.getValue("CSQ");
706     assertEquals(map.size(), 9);
707     assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript4");
708
709     sf = transcriptFeatures.get(3);
710     assertEquals(sf.getBegin(), 11);
711     assertEquals(sf.getEnd(), 11);
712     assertEquals(sf.getScore(), 0f);
713     assertEquals(Float.parseFloat((String) sf.getValue("AF")), 0.7f, DELTA);
714     assertEquals(sf.getValue("alleles"), "A,G");
715     map = (Map) sf.getValue("CSQ");
716     assertEquals(map.size(), 9);
717     assertEquals(sf.getValueAsString("CSQ", "Feature"), "transcript4");
718   }
719
720   /**
721    * A test that demonstrates loading a contig sequence from an indexed sequence
722    * database which is the reference for a VCF file
723    * 
724    * @throws IOException
725    */
726   @Test(groups = "Functional")
727   public void testLoadVCFContig() throws IOException
728   {
729     VCFLoader loader = new VCFLoader(
730             "test/jalview/io/vcf/testVcf2.vcf");
731
732     SequenceI seq = loader.loadVCFContig("contig123");
733     assertEquals(seq.getLength(), 15);
734     assertEquals(seq.getSequenceAsString(), "AAAAACCCCCGGGGG");
735     List<SequenceFeature> features = seq.getSequenceFeatures();
736     SequenceFeatures.sortFeatures(features, true);
737     assertEquals(features.size(), 2);
738     SequenceFeature sf = features.get(0);
739     assertEquals(sf.getBegin(), 8);
740     assertEquals(sf.getEnd(), 8);
741     assertEquals(sf.getDescription(), "C,A");
742     sf = features.get(1);
743     assertEquals(sf.getBegin(), 12);
744     assertEquals(sf.getEnd(), 12);
745     assertEquals(sf.getDescription(), "G,T");
746
747     seq = loader.loadVCFContig("contig789");
748     assertEquals(seq.getLength(), 25);
749     assertEquals(seq.getSequenceAsString(), "GGGGGTTTTTAAAAACCCCCGGGGG");
750     features = seq.getSequenceFeatures();
751     SequenceFeatures.sortFeatures(features, true);
752     assertEquals(features.size(), 2);
753     sf = features.get(0);
754     assertEquals(sf.getBegin(), 2);
755     assertEquals(sf.getEnd(), 2);
756     assertEquals(sf.getDescription(), "G,T");
757     sf = features.get(1);
758     assertEquals(sf.getBegin(), 21);
759     assertEquals(sf.getEnd(), 21);
760     assertEquals(sf.getDescription(), "G,A");
761
762     seq = loader.loadVCFContig("contig456");
763     assertEquals(seq.getLength(), 20);
764     assertEquals(seq.getSequenceAsString(), "CCCCCGGGGGTTTTTAAAAA");
765     features = seq.getSequenceFeatures();
766     SequenceFeatures.sortFeatures(features, true);
767     assertEquals(features.size(), 1);
768     sf = features.get(0);
769     assertEquals(sf.getBegin(), 15);
770     assertEquals(sf.getEnd(), 15);
771     assertEquals(sf.getDescription(), "T,C");
772   }
773 }