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