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