Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.io

File FeaturesFileTest.java

 

Code metrics

2
402
18
1
928
687
19
0.05
22.33
18
1.06

Classes

Class Line # Actions
FeaturesFileTest 64 402 19
0.9976303699.8%
 

Contributing tests

This file is covered by 14 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.io;
22   
23    import static org.testng.AssertJUnit.assertEquals;
24    import static org.testng.AssertJUnit.assertFalse;
25    import static org.testng.AssertJUnit.assertNotNull;
26    import static org.testng.AssertJUnit.assertSame;
27    import static org.testng.AssertJUnit.assertTrue;
28    import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
29   
30    import java.awt.Color;
31    import java.io.File;
32    import java.io.IOException;
33    import java.util.HashMap;
34    import java.util.Iterator;
35    import java.util.List;
36    import java.util.Map;
37   
38    import org.testng.annotations.AfterClass;
39    import org.testng.annotations.BeforeClass;
40    import org.testng.annotations.Test;
41   
42    import jalview.api.FeatureColourI;
43    import jalview.api.FeatureRenderer;
44    import jalview.datamodel.Alignment;
45    import jalview.datamodel.AlignmentI;
46    import jalview.datamodel.SequenceDummy;
47    import jalview.datamodel.SequenceFeature;
48    import jalview.datamodel.SequenceI;
49    import jalview.datamodel.features.FeatureMatcher;
50    import jalview.datamodel.features.FeatureMatcherI;
51    import jalview.datamodel.features.FeatureMatcherSet;
52    import jalview.datamodel.features.FeatureMatcherSetI;
53    import jalview.datamodel.features.SequenceFeatures;
54    import jalview.gui.AlignFrame;
55    import jalview.gui.Desktop;
56    import jalview.gui.JvOptionPane;
57    import jalview.schemes.FeatureColour;
58    import jalview.structure.StructureSelectionManager;
59    import jalview.util.matcher.Condition;
60    import jalview.viewmodel.seqfeatures.FeatureRendererModel;
61    import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
62    import junit.extensions.PA;
63   
 
64    public class FeaturesFileTest
65    {
66    private static String simpleGffFile = "examples/testdata/simpleGff3.gff";
67   
 
68  1 toggle @AfterClass(alwaysRun = true)
69    public void tearDownAfterClass()
70    {
71    /*
72    * remove any sequence mappings created so they don't pollute other tests
73    */
74  1 StructureSelectionManager ssm = StructureSelectionManager
75    .getStructureSelectionManager(Desktop.instance);
76  1 ssm.resetAll();
77    }
78   
 
79  1 toggle @BeforeClass(alwaysRun = true)
80    public void setUpJvOptionPane()
81    {
82  1 JvOptionPane.setInteractiveMode(false);
83  1 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
84    }
85   
 
86  1 toggle @Test(groups = { "Functional" })
87    public void testParse() throws Exception
88    {
89  1 File f = new File("examples/uniref50.fa");
90  1 AlignmentI al = readAlignmentFile(f);
91  1 AlignFrame af = new AlignFrame(al, 500, 500);
92  1 Map<String, FeatureColourI> colours = af.getFeatureRenderer()
93    .getFeatureColours();
94  1 FeaturesFile featuresFile = new FeaturesFile(
95    "examples/exampleFeatures.txt", DataSourceType.FILE);
96  1 assertTrue("Test " + "Features file test"
97    + "\nFailed to parse features file.",
98    featuresFile.parse(al.getDataset(), colours, true));
99   
100    /*
101    * Refetch the colour map from the FeatureRenderer (to confirm it has been
102    * updated - JAL-1904), and verify (some) feature group colours
103    */
104  1 colours = af.getFeatureRenderer().getFeatureColours();
105  1 assertEquals("27 feature group colours not found", 27, colours.size());
106  1 assertEquals(colours.get("Cath").getColour(), new Color(0x93b1d1));
107  1 assertEquals(colours.get("ASX-MOTIF").getColour(), new Color(0x6addbb));
108  1 FeatureColourI kdColour = colours.get("kdHydrophobicity");
109  1 assertTrue(kdColour.isGraduatedColour());
110  1 assertTrue(kdColour.isAboveThreshold());
111  1 assertEquals(-2f, kdColour.getThreshold());
112   
113    /*
114    * verify (some) features on sequences
115    */
116  1 List<SequenceFeature> sfs = al.getSequenceAt(0).getDatasetSequence()
117    .getSequenceFeatures(); // FER_CAPAA
118  1 SequenceFeatures.sortFeatures(sfs, true);
119  1 assertEquals(8, sfs.size());
120   
121    /*
122    * verify (in ascending start position order)
123    */
124  1 SequenceFeature sf = sfs.get(0);
125  1 assertEquals("Pfam family%LINK%", sf.description);
126  1 assertEquals(0, sf.begin);
127  1 assertEquals(0, sf.end);
128  1 assertEquals("uniprot", sf.featureGroup);
129  1 assertEquals("Pfam", sf.type);
130  1 assertEquals(1, sf.links.size());
131  1 assertEquals("Pfam family|http://pfam.xfam.org/family/PF00111",
132    sf.links.get(0));
133   
134  1 sf = sfs.get(1);
135  1 assertEquals("Ferredoxin_fold Status: True Positive ", sf.description);
136  1 assertEquals(3, sf.begin);
137  1 assertEquals(93, sf.end);
138  1 assertEquals("uniprot", sf.featureGroup);
139  1 assertEquals("Cath", sf.type);
140   
141  1 sf = sfs.get(2);
142  1 assertEquals("Fer2 Status: True Positive Pfam 8_8%LINK%",
143    sf.description);
144  1 assertEquals("Pfam 8_8|http://pfam.xfam.org/family/PF00111",
145    sf.links.get(0));
146  1 assertEquals(8, sf.begin);
147  1 assertEquals(83, sf.end);
148  1 assertEquals("uniprot", sf.featureGroup);
149  1 assertEquals("Pfam", sf.type);
150   
151  1 sf = sfs.get(3);
152  1 assertEquals("Iron-sulfur (2Fe-2S)", sf.description);
153  1 assertEquals(39, sf.begin);
154  1 assertEquals(39, sf.end);
155  1 assertEquals("uniprot", sf.featureGroup);
156  1 assertEquals("METAL", sf.type);
157   
158  1 sf = sfs.get(4);
159  1 assertEquals("Iron-sulfur (2Fe-2S)", sf.description);
160  1 assertEquals(44, sf.begin);
161  1 assertEquals(44, sf.end);
162  1 assertEquals("uniprot", sf.featureGroup);
163  1 assertEquals("METAL", sf.type);
164   
165  1 sf = sfs.get(5);
166  1 assertEquals("Iron-sulfur (2Fe-2S)", sf.description);
167  1 assertEquals(47, sf.begin);
168  1 assertEquals(47, sf.end);
169  1 assertEquals("uniprot", sf.featureGroup);
170  1 assertEquals("METAL", sf.type);
171   
172  1 sf = sfs.get(6);
173  1 assertEquals("Iron-sulfur (2Fe-2S)", sf.description);
174  1 assertEquals(77, sf.begin);
175  1 assertEquals(77, sf.end);
176  1 assertEquals("uniprot", sf.featureGroup);
177  1 assertEquals("METAL", sf.type);
178   
179  1 sf = sfs.get(7);
180  1 assertEquals(
181    "High confidence server. Only hits with scores over 0.8 are reported. PHOSPHORYLATION (T) 89_8%LINK%",
182    sf.description);
183  1 assertEquals(
184    "PHOSPHORYLATION (T) 89_8|http://www.cbs.dtu.dk/cgi-bin/proview/webface-link?seqid=P83527&amp;service=NetPhos-2.0",
185    sf.links.get(0));
186  1 assertEquals(89, sf.begin);
187  1 assertEquals(89, sf.end);
188  1 assertEquals("netphos", sf.featureGroup);
189  1 assertEquals("PHOSPHORYLATION (T)", sf.type);
190    }
191   
192    /**
193    * Test parsing a features file with a mix of Jalview and GFF formatted
194    * content
195    *
196    * @throws Exception
197    */
 
198  1 toggle @Test(groups = { "Functional" })
199    public void testParse_mixedJalviewGff() throws Exception
200    {
201  1 File f = new File("examples/uniref50.fa");
202  1 AlignmentI al = readAlignmentFile(f);
203  1 AlignFrame af = new AlignFrame(al, 500, 500);
204  1 Map<String, FeatureColourI> colours = af.getFeatureRenderer()
205    .getFeatureColours();
206    // GFF2 uses space as name/value separator in column 9
207  1 String gffData = "METAL\tcc9900\n"
208    + "GFF\n"
209    + "FER_CAPAA\tuniprot\tMETAL\t44\t45\t4.0\t.\t.\tNote Iron-sulfur; Note 2Fe-2S\n"
210    + "FER1_SOLLC\tuniprot\tPfam\t55\t130\t2.0\t.\t.";
211  1 FeaturesFile featuresFile = new FeaturesFile(gffData,
212    DataSourceType.PASTE);
213  1 assertTrue("Failed to parse features file",
214    featuresFile.parse(al.getDataset(), colours, true));
215   
216    // verify colours read or synthesized
217  1 colours = af.getFeatureRenderer().getFeatureColours();
218  1 assertEquals("1 feature group colours not found", 1, colours.size());
219  1 assertEquals(colours.get("METAL").getColour(), new Color(0xcc9900));
220   
221    // verify feature on FER_CAPAA
222  1 List<SequenceFeature> sfs = al.getSequenceAt(0).getDatasetSequence()
223    .getSequenceFeatures();
224  1 assertEquals(1, sfs.size());
225  1 SequenceFeature sf = sfs.get(0);
226  1 assertEquals("Iron-sulfur,2Fe-2S", sf.description);
227  1 assertEquals(44, sf.begin);
228  1 assertEquals(45, sf.end);
229  1 assertEquals("uniprot", sf.featureGroup);
230  1 assertEquals("METAL", sf.type);
231  1 assertEquals(4f, sf.getScore(), 0.001f);
232   
233    // verify feature on FER1_SOLLC
234  1 sfs = al.getSequenceAt(2).getDatasetSequence().getSequenceFeatures();
235  1 assertEquals(1, sfs.size());
236  1 sf = sfs.get(0);
237  1 assertEquals("uniprot", sf.description);
238  1 assertEquals(55, sf.begin);
239  1 assertEquals(130, sf.end);
240  1 assertEquals("uniprot", sf.featureGroup);
241  1 assertEquals("Pfam", sf.type);
242  1 assertEquals(2f, sf.getScore(), 0.001f);
243    }
244   
 
245  8 toggle public static AlignmentI readAlignmentFile(File f) throws IOException
246    {
247  8 System.out.println("Reading file: " + f);
248  8 String ff = f.getPath();
249  8 FormatAdapter rf = new FormatAdapter();
250   
251  8 AlignmentI al = rf.readFile(ff, DataSourceType.FILE,
252    new IdentifyFile().identify(ff, DataSourceType.FILE));
253   
254  8 al.setDataset(null); // creates dataset sequences
255  8 assertNotNull("Couldn't read supplied alignment data.", al);
256  8 return al;
257    }
258   
259    /**
260    * Test parsing a features file with GFF formatted content only
261    *
262    * @throws Exception
263    */
 
264  1 toggle @Test(groups = { "Functional" })
265    public void testParse_pureGff3() throws Exception
266    {
267  1 File f = new File("examples/uniref50.fa");
268  1 AlignmentI al = readAlignmentFile(f);
269  1 AlignFrame af = new AlignFrame(al, 500, 500);
270  1 Map<String, FeatureColourI> colours = af.getFeatureRenderer()
271    .getFeatureColours();
272    // GFF3 uses '=' separator for name/value pairs in column 9
273    // comma (%2C) equals (%3D) or semi-colon (%3B) should be url-escaped in values
274  1 String gffData = "##gff-version 3\n"
275    + "FER_CAPAA\tuniprot\tMETAL\t39\t39\t0.0\t.\t.\t"
276    + "Note=Iron-sulfur (2Fe-2S);Note=another note,and another;evidence=ECO%3B0000255%2CPROSITE%3DProRule:PRU00465;"
277    + "CSQ=AF=21,POLYPHEN=benign,possibly_damaging,clin_sig=Benign%3Dgood\n"
278    + "FER1_SOLLC\tuniprot\tPfam\t55\t130\t3.0\t.\t.\tID=$23";
279  1 FeaturesFile featuresFile = new FeaturesFile(gffData,
280    DataSourceType.PASTE);
281  1 assertTrue("Failed to parse features file",
282    featuresFile.parse(al.getDataset(), colours, true));
283   
284    // verify feature on FER_CAPAA
285  1 List<SequenceFeature> sfs = al.getSequenceAt(0).getDatasetSequence()
286    .getSequenceFeatures();
287  1 assertEquals(1, sfs.size());
288  1 SequenceFeature sf = sfs.get(0);
289    // description parsed from Note attribute
290  1 assertEquals("Iron-sulfur (2Fe-2S),another note,and another",
291    sf.description);
292  1 assertEquals(39, sf.begin);
293  1 assertEquals(39, sf.end);
294  1 assertEquals("uniprot", sf.featureGroup);
295  1 assertEquals("METAL", sf.type);
296  1 assertEquals(5, sf.otherDetails.size());
297  1 assertEquals("ECO;0000255,PROSITE=ProRule:PRU00465", // url decoded
298    sf.getValue("evidence"));
299  1 assertEquals("Iron-sulfur (2Fe-2S),another note,and another",
300    sf.getValue("Note"));
301  1 assertEquals("21", sf.getValueAsString("CSQ", "AF"));
302  1 assertEquals("benign,possibly_damaging",
303    sf.getValueAsString("CSQ", "POLYPHEN"));
304  1 assertEquals("Benign=good", sf.getValueAsString("CSQ", "clin_sig")); // url decoded
305    // todo change STRAND and !Phase into fields of SequenceFeature instead
306  1 assertEquals(".", sf.otherDetails.get("STRAND"));
307  1 assertEquals(0, sf.getStrand());
308  1 assertEquals(".", sf.getPhase());
309   
310    // verify feature on FER1_SOLLC1
311  1 sfs = al.getSequenceAt(2).getDatasetSequence().getSequenceFeatures();
312  1 assertEquals(1, sfs.size());
313  1 sf = sfs.get(0);
314    // ID used for description if available
315  1 assertEquals("$23", sf.description);
316  1 assertEquals(55, sf.begin);
317  1 assertEquals(130, sf.end);
318  1 assertEquals("uniprot", sf.featureGroup);
319  1 assertEquals("Pfam", sf.type);
320  1 assertEquals(3f, sf.getScore(), 0.001f);
321    }
322   
323    /**
324    * Test parsing a features file with Jalview format features (but no colour
325    * descriptors or startgroup to give the hint not to parse as GFF)
326    *
327    * @throws Exception
328    */
 
329  1 toggle @Test(groups = { "Functional" })
330    public void testParse_jalviewFeaturesOnly() throws Exception
331    {
332  1 File f = new File("examples/uniref50.fa");
333  1 AlignmentI al = readAlignmentFile(f);
334  1 AlignFrame af = new AlignFrame(al, 500, 500);
335  1 Map<String, FeatureColourI> colours = af.getFeatureRenderer()
336    .getFeatureColours();
337   
338    /*
339    * one feature on FER_CAPAA and one on sequence 3 (index 2) FER1_SOLLC
340    */
341  1 String featureData = "Iron-sulfur (2Fe-2S)\tFER_CAPAA\t-1\t39\t39\tMETAL\n"
342    + "Iron-phosphorus (2Fe-P)\tID_NOT_SPECIFIED\t2\t86\t87\tMETALLIC\n";
343  1 FeaturesFile featuresFile = new FeaturesFile(featureData,
344    DataSourceType.PASTE);
345  1 assertTrue("Failed to parse features file",
346    featuresFile.parse(al.getDataset(), colours, true));
347   
348    // verify FER_CAPAA feature
349  1 List<SequenceFeature> sfs = al.getSequenceAt(0).getDatasetSequence()
350    .getSequenceFeatures();
351  1 assertEquals(1, sfs.size());
352  1 SequenceFeature sf = sfs.get(0);
353  1 assertEquals("Iron-sulfur (2Fe-2S)", sf.description);
354  1 assertEquals(39, sf.begin);
355  1 assertEquals(39, sf.end);
356  1 assertEquals("METAL", sf.type);
357   
358    // verify FER1_SOLLC feature
359  1 sfs = al.getSequenceAt(2).getDatasetSequence().getSequenceFeatures();
360  1 assertEquals(1, sfs.size());
361  1 sf = sfs.get(0);
362  1 assertEquals("Iron-phosphorus (2Fe-P)", sf.description);
363  1 assertEquals(86, sf.begin);
364  1 assertEquals(87, sf.end);
365  1 assertEquals("METALLIC", sf.type);
366    }
367   
 
368  4 toggle private void checkDatasetfromSimpleGff3(AlignmentI dataset)
369    {
370  4 assertEquals("no sequences extracted from GFF3 file", 2,
371    dataset.getHeight());
372   
373  4 SequenceI seq1 = dataset.findName("seq1");
374  4 SequenceI seq2 = dataset.findName("seq2");
375  4 assertNotNull(seq1);
376  4 assertNotNull(seq2);
377  4 assertFalse(
378    "Failed to replace dummy seq1 with real sequence",
379    seq1 instanceof SequenceDummy
380    && ((SequenceDummy) seq1).isDummy());
381  4 assertFalse(
382    "Failed to replace dummy seq2 with real sequence",
383    seq2 instanceof SequenceDummy
384    && ((SequenceDummy) seq2).isDummy());
385  4 String placeholderseq = new SequenceDummy("foo").getSequenceAsString();
386  4 assertFalse("dummy replacement buggy for seq1",
387    placeholderseq.equals(seq1.getSequenceAsString()));
388  4 assertFalse("dummy replacement buggy for seq2",
389    placeholderseq.equals(seq2.getSequenceAsString()));
390  4 assertNotNull("No features added to seq1", seq1.getSequenceFeatures());
391  4 assertEquals("Wrong number of features", 3, seq1.getSequenceFeatures()
392    .size());
393  4 assertTrue(seq2.getSequenceFeatures().isEmpty());
394  4 assertEquals(
395    "Wrong number of features",
396    0,
397  4 seq2.getSequenceFeatures() == null ? 0 : seq2
398    .getSequenceFeatures().size());
399  4 assertTrue(
400    "Expected at least one CDNA/Protein mapping for seq1",
401    dataset.getCodonFrame(seq1) != null
402    && dataset.getCodonFrame(seq1).size() > 0);
403   
404    }
405   
 
406  1 toggle @Test(groups = { "Functional" })
407    public void readGff3File() throws IOException
408    {
409  1 FeaturesFile gffreader = new FeaturesFile(true, simpleGffFile,
410    DataSourceType.FILE);
411  1 Alignment dataset = new Alignment(gffreader.getSeqsAsArray());
412  1 gffreader.addProperties(dataset);
413  1 checkDatasetfromSimpleGff3(dataset);
414    }
415   
 
416  1 toggle @Test(groups = { "Functional" })
417    public void simpleGff3FileClass() throws IOException
418    {
419  1 AlignmentI dataset = new Alignment(new SequenceI[] {});
420  1 FeaturesFile ffile = new FeaturesFile(simpleGffFile,
421    DataSourceType.FILE);
422   
423  1 boolean parseResult = ffile.parse(dataset, null, false, false);
424  1 assertTrue("return result should be true", parseResult);
425  1 checkDatasetfromSimpleGff3(dataset);
426    }
427   
 
428  1 toggle @Test(groups = { "Functional" })
429    public void simpleGff3FileLoader() throws IOException
430    {
431  1 AlignFrame af = new FileLoader(false).LoadFileWaitTillLoaded(
432    simpleGffFile, DataSourceType.FILE);
433  1 assertTrue(
434    "Didn't read the alignment into an alignframe from Gff3 File",
435    af != null);
436  1 checkDatasetfromSimpleGff3(af.getViewport().getAlignment());
437    }
438   
 
439  1 toggle @Test(groups = { "Functional" })
440    public void simpleGff3RelaxedIdMatching() throws IOException
441    {
442  1 AlignmentI dataset = new Alignment(new SequenceI[] {});
443  1 FeaturesFile ffile = new FeaturesFile(simpleGffFile,
444    DataSourceType.FILE);
445   
446  1 boolean parseResult = ffile.parse(dataset, null, false, true);
447  1 assertTrue("return result (relaxedID matching) should be true",
448    parseResult);
449  1 checkDatasetfromSimpleGff3(dataset);
450    }
451   
 
452  1 toggle @Test(groups = { "Functional" })
453    public void testPrintJalviewFormat() throws Exception
454    {
455  1 File f = new File("examples/uniref50.fa");
456  1 AlignmentI al = readAlignmentFile(f);
457  1 AlignFrame af = new AlignFrame(al, 500, 500);
458  1 Map<String, FeatureColourI> colours = af.getFeatureRenderer()
459    .getFeatureColours();
460  1 String features = "METAL\tcc9900\n"
461    + "GAMMA-TURN\tred|0,255,255|20.0|95.0|below|66.0\n"
462    + "Pfam\tred\n"
463    + "STARTGROUP\tuniprot\n"
464    + "Cath\tFER_CAPAA\t-1\t0\t0\tDomain\n" // non-positional feature
465    + "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\n"
466    + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\n"
467    + "<html>Pfam domain<a href=\"http://pfam.xfam.org/family/PF00111\">Pfam_3_4</a></html>\tFER_CAPAA\t-1\t20\t20\tPfam\n"
468    + "ENDGROUP\tuniprot\n";
469  1 FeaturesFile featuresFile = new FeaturesFile(features,
470    DataSourceType.PASTE);
471  1 featuresFile.parse(al.getDataset(), colours, false);
472   
473    /*
474    * add positional and non-positional features with null and
475    * empty feature group to check handled correctly
476    */
477  1 SequenceI seq = al.getSequenceAt(1); // FER_CAPAN
478  1 seq.addSequenceFeature(new SequenceFeature("Pfam", "desc1", 0, 0, 1.3f,
479    null));
480  1 seq.addSequenceFeature(new SequenceFeature("Pfam", "desc2", 4, 9,
481    Float.NaN, null));
482  1 seq = al.getSequenceAt(2); // FER1_SOLLC
483  1 seq.addSequenceFeature(new SequenceFeature("Pfam", "desc3", 0, 0,
484    Float.NaN, ""));
485  1 seq.addSequenceFeature(new SequenceFeature("Pfam", "desc4", 5, 8,
486    -2.6f, ""));
487   
488    /*
489    * first with no features displayed, exclude non-positional features
490    */
491  1 FeatureRenderer fr = af.alignPanel.getFeatureRenderer();
492  1 String exported = featuresFile
493    .printJalviewFormat(al.getSequencesArray(), fr, false, false);
494  1 String expected = "No Features Visible";
495  1 assertEquals(expected, exported);
496   
497    /*
498    * include non-positional features, but still no positional features
499    */
500  1 fr.setGroupVisibility("uniprot", true);
501  1 exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
502    true, false);
503  1 expected = "\nSTARTGROUP\tuniprot\n"
504    + "Cath\tFER_CAPAA\t-1\t0\t0\tDomain\t0.0\n"
505    + "ENDGROUP\tuniprot\n\n"
506    + "desc1\tFER_CAPAN\t-1\t0\t0\tPfam\t1.3\n\n"
507    + "desc3\tFER1_SOLLC\t-1\t0\t0\tPfam\n"; // NaN is not output
508  1 assertEquals(expected, exported);
509   
510    /*
511    * set METAL (in uniprot group) and GAMMA-TURN visible, but not Pfam
512    */
513  1 fr.setVisible("METAL");
514  1 fr.setVisible("GAMMA-TURN");
515  1 exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
516    false, false);
517  1 expected = "METAL\tcc9900\n"
518    + "GAMMA-TURN\tscore|ff0000|00ffff|noValueMin|20.0|95.0|below|66.0\n"
519    + "\nSTARTGROUP\tuniprot\n"
520    + "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\t0.0\n"
521    + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
522    + "ENDGROUP\tuniprot\n";
523  1 assertEquals(expected, exported);
524   
525    /*
526    * now set Pfam visible
527    */
528  1 fr.setVisible("Pfam");
529  1 exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
530    false, false);
531    /*
532    * features are output within group, ordered by sequence and type
533    */
534  1 expected = "METAL\tcc9900\n"
535    + "Pfam\tff0000\n"
536    + "GAMMA-TURN\tscore|ff0000|00ffff|noValueMin|20.0|95.0|below|66.0\n"
537    + "\nSTARTGROUP\tuniprot\n"
538    + "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\t0.0\n"
539    + "<html>Pfam domain<a href=\"http://pfam.xfam.org/family/PF00111\">Pfam_3_4</a></html>\tFER_CAPAA\t-1\t20\t20\tPfam\t0.0\n"
540    + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
541    + "ENDGROUP\tuniprot\n"
542    // null / empty group features are output after named groups
543    + "\ndesc2\tFER_CAPAN\t-1\t4\t9\tPfam\n"
544    + "\ndesc4\tFER1_SOLLC\t-1\t5\t8\tPfam\t-2.6\n";
545  1 assertEquals(expected, exported);
546   
547    /*
548    * hide uniprot group
549    */
550  1 fr.setGroupVisibility("uniprot", false);
551  1 expected = "METAL\tcc9900\n" + "Pfam\tff0000\n"
552    + "GAMMA-TURN\tscore|ff0000|00ffff|noValueMin|20.0|95.0|below|66.0\n"
553    + "\ndesc2\tFER_CAPAN\t-1\t4\t9\tPfam\n"
554    + "\ndesc4\tFER1_SOLLC\t-1\t5\t8\tPfam\t-2.6\n";
555  1 exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
556    false, false);
557  1 assertEquals(expected, exported);
558   
559    /*
560    * include non-positional (overrides group not shown)
561    */
562  1 exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
563    true, false);
564  1 expected = "METAL\tcc9900\n" + "Pfam\tff0000\n"
565    + "GAMMA-TURN\tscore|ff0000|00ffff|noValueMin|20.0|95.0|below|66.0\n"
566    + "\nSTARTGROUP\tuniprot\n"
567    + "Cath\tFER_CAPAA\t-1\t0\t0\tDomain\t0.0\n"
568    + "ENDGROUP\tuniprot\n"
569    + "\ndesc1\tFER_CAPAN\t-1\t0\t0\tPfam\t1.3\n"
570    + "desc2\tFER_CAPAN\t-1\t4\t9\tPfam\n"
571    + "\ndesc3\tFER1_SOLLC\t-1\t0\t0\tPfam\n"
572    + "desc4\tFER1_SOLLC\t-1\t5\t8\tPfam\t-2.6\n";
573  1 assertEquals(expected, exported);
574    }
575   
 
576  1 toggle @Test(groups = { "Functional" })
577    public void testPrintGffFormat() throws Exception
578    {
579  1 File f = new File("examples/uniref50.fa");
580  1 AlignmentI al = readAlignmentFile(f);
581  1 AlignFrame af = new AlignFrame(al, 500, 500);
582   
583    /*
584    * no features
585    */
586  1 FeaturesFile featuresFile = new FeaturesFile();
587  1 FeatureRendererModel fr = (FeatureRendererModel) af.alignPanel
588    .getFeatureRenderer();
589  1 String exported = featuresFile.printGffFormat(al.getSequencesArray(),
590    fr, false, false);
591  1 String gffHeader = "##gff-version 2\n";
592  1 assertEquals(gffHeader, exported);
593  1 exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
594    true, false);
595  1 assertEquals(gffHeader, exported);
596   
597    /*
598    * add some features
599    */
600  1 al.getSequenceAt(0).addSequenceFeature(
601    new SequenceFeature("Domain", "Cath", 0, 0, 0f, "Uniprot"));
602  1 al.getSequenceAt(0).addSequenceFeature(
603    new SequenceFeature("METAL", "Cath", 39, 39, 1.2f, null));
604  1 al.getSequenceAt(1)
605    .addSequenceFeature(
606    new SequenceFeature("GAMMA-TURN", "Turn", 36, 38, 2.1f,
607    "s3dm"));
608  1 SequenceFeature sf = new SequenceFeature("Pfam", "", 20, 20, 0f,
609    "Uniprot");
610  1 sf.setStrand("+");
611  1 sf.setPhase("2");
612  1 sf.setValue("x", "y");
613  1 sf.setValue("black", "white");
614  1 Map<String, String> csq = new HashMap<>();
615  1 csq.put("SIFT", "benign,mostly benign,cloudy, with meatballs");
616  1 csq.put("consequence", "missense_variant");
617  1 sf.setValue("CSQ", csq);
618  1 al.getSequenceAt(1).addSequenceFeature(sf);
619   
620    /*
621    * 'discover' features then hide all feature types
622    */
623  1 fr.findAllFeatures(true);
624  1 FeatureSettingsBean[] data = new FeatureSettingsBean[4];
625  1 FeatureColourI fc = new FeatureColour(Color.PINK);
626  1 data[0] = new FeatureSettingsBean("Domain", fc, null, false);
627  1 data[1] = new FeatureSettingsBean("METAL", fc, null, false);
628  1 data[2] = new FeatureSettingsBean("GAMMA-TURN", fc, null, false);
629  1 data[3] = new FeatureSettingsBean("Pfam", fc, null, false);
630  1 fr.setFeaturePriority(data);
631   
632    /*
633    * with no features displayed, exclude non-positional features
634    */
635  1 exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
636    false, false);
637  1 assertEquals(gffHeader, exported);
638   
639    /*
640    * include non-positional features
641    */
642  1 fr.setGroupVisibility("Uniprot", true);
643  1 fr.setGroupVisibility("s3dm", false);
644  1 exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
645    true, false);
646  1 String expected = gffHeader
647    + "FER_CAPAA\tUniprot\tDomain\t0\t0\t0.0\t.\t.\n";
648  1 assertEquals(expected, exported);
649   
650    /*
651    * set METAL (in uniprot group) and GAMMA-TURN visible, but not Pfam
652    * only Uniprot group visible here...
653    */
654  1 fr.setVisible("METAL");
655  1 fr.setVisible("GAMMA-TURN");
656  1 exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
657    false, false);
658    // METAL feature has null group: description used for column 2
659  1 expected = gffHeader + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\n";
660  1 assertEquals(expected, exported);
661   
662    /*
663    * set s3dm group visible
664    */
665  1 fr.setGroupVisibility("s3dm", true);
666  1 exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
667    false, false);
668    // METAL feature has null group: description used for column 2
669  1 expected = gffHeader + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\n"
670    + "FER_CAPAN\ts3dm\tGAMMA-TURN\t36\t38\t2.1\t.\t.\n";
671  1 assertEquals(expected, exported);
672   
673    /*
674    * now set Pfam visible
675    */
676  1 fr.setVisible("Pfam");
677  1 exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
678    false, false);
679    // Pfam feature columns include strand(+), phase(2), attributes
680  1 expected = gffHeader
681    + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\n"
682    // CSQ output as CSQ=att1=value1,att2=value2
683    // note all commas are encoded here which is wrong - it should be
684    // SIFT=benign,mostly benign,cloudy%2C with meatballs
685    + "FER_CAPAN\tUniprot\tPfam\t20\t20\t0.0\t+\t2\tx=y;black=white;"
686    + "CSQ=SIFT=benign%2Cmostly benign%2Ccloudy%2C with meatballs,consequence=missense_variant\n"
687    + "FER_CAPAN\ts3dm\tGAMMA-TURN\t36\t38\t2.1\t.\t.\n";
688  1 assertEquals(expected, exported);
689    }
690   
691    /**
692    * Test for parsing of feature filters as represented in a Jalview features
693    * file
694    *
695    * @throws Exception
696    */
 
697  1 toggle @Test(groups = { "Functional" })
698    public void testParseFilters() throws Exception
699    {
700  1 Map<String, FeatureMatcherSetI> filters = new HashMap<>();
701  1 String text = "sequence_variant\tCSQ:PolyPhen NotContains 'damaging'\n"
702    + "missense_variant\t(label contains foobar) and (Score lt 1.3)";
703  1 FeaturesFile featuresFile = new FeaturesFile(text,
704    DataSourceType.PASTE);
705  1 featuresFile.parseFilters(filters);
706  1 assertEquals(filters.size(), 2);
707   
708  1 FeatureMatcherSetI fm = filters.get("sequence_variant");
709  1 assertNotNull(fm);
710  1 Iterator<FeatureMatcherI> matchers = fm.getMatchers().iterator();
711  1 FeatureMatcherI matcher = matchers.next();
712  1 assertFalse(matchers.hasNext());
713  1 String[] attributes = matcher.getAttribute();
714  1 assertArrayEquals(attributes, new String[] { "CSQ", "PolyPhen" });
715  1 assertSame(matcher.getMatcher().getCondition(), Condition.NotContains);
716  1 assertEquals(matcher.getMatcher().getPattern(), "damaging");
717   
718  1 fm = filters.get("missense_variant");
719  1 assertNotNull(fm);
720  1 matchers = fm.getMatchers().iterator();
721  1 matcher = matchers.next();
722  1 assertTrue(matcher.isByLabel());
723  1 assertSame(matcher.getMatcher().getCondition(), Condition.Contains);
724  1 assertEquals(matcher.getMatcher().getPattern(), "foobar");
725  1 matcher = matchers.next();
726  1 assertTrue(matcher.isByScore());
727  1 assertSame(matcher.getMatcher().getCondition(), Condition.LT);
728  1 assertEquals(matcher.getMatcher().getPattern(), "1.3");
729  1 assertEquals(PA.getValue(matcher.getMatcher(), "floatValue"), 1.3f);
730   
731  1 assertFalse(matchers.hasNext());
732    }
733   
 
734  1 toggle @Test(groups = { "Functional" })
735    public void testOutputFeatureFilters()
736    {
737  1 FeaturesFile ff = new FeaturesFile();
738  1 StringBuilder sb = new StringBuilder();
739  1 Map<String, FeatureColourI> visible = new HashMap<>();
740  1 visible.put("pfam", new FeatureColour(Color.red));
741  1 Map<String, FeatureMatcherSetI> featureFilters = new HashMap<>();
742   
743    // with no filters, nothing is output
744  1 ff.outputFeatureFilters(sb, visible, featureFilters);
745  1 assertEquals("", sb.toString());
746   
747    // with filter for not visible features only, nothing is output
748  1 FeatureMatcherSet filter = new FeatureMatcherSet();
749  1 filter.and(FeatureMatcher.byLabel(Condition.Present, null));
750  1 featureFilters.put("foobar", filter);
751  1 ff.outputFeatureFilters(sb, visible, featureFilters);
752  1 assertEquals("", sb.toString());
753   
754    // with filters for visible feature types
755  1 FeatureMatcherSet filter2 = new FeatureMatcherSet();
756  1 filter2.and(FeatureMatcher.byAttribute(Condition.Present, null, "CSQ",
757    "PolyPhen"));
758  1 filter2.and(FeatureMatcher.byScore(Condition.LE, "-2.4"));
759  1 featureFilters.put("pfam", filter2);
760  1 visible.put("foobar", new FeatureColour(Color.blue));
761  1 ff.outputFeatureFilters(sb, visible, featureFilters);
762  1 String expected = "\nSTARTFILTERS\nfoobar\tLabel Present\npfam\t(CSQ:PolyPhen Present) AND (Score LE -2.4)\nENDFILTERS\n";
763  1 assertEquals(expected, sb.toString());
764    }
765   
766    /**
767    * Output as GFF should not include features which are not visible due to
768    * colour threshold or feature filter settings
769    *
770    * @throws Exception
771    */
 
772  1 toggle @Test(groups = { "Functional" })
773    public void testPrintGffFormat_withFilters() throws Exception
774    {
775  1 File f = new File("examples/uniref50.fa");
776  1 AlignmentI al = readAlignmentFile(f);
777  1 AlignFrame af = new AlignFrame(al, 500, 500);
778  1 SequenceFeature sf1 = new SequenceFeature("METAL", "Cath", 39, 39, 1.2f,
779    null);
780  1 sf1.setValue("clin_sig", "Likely Pathogenic");
781  1 sf1.setValue("AF", "24");
782  1 al.getSequenceAt(0).addSequenceFeature(sf1);
783  1 SequenceFeature sf2 = new SequenceFeature("METAL", "Cath", 41, 41, 0.6f,
784    null);
785  1 sf2.setValue("clin_sig", "Benign");
786  1 sf2.setValue("AF", "46");
787  1 al.getSequenceAt(0).addSequenceFeature(sf2);
788   
789  1 FeaturesFile featuresFile = new FeaturesFile();
790  1 FeatureRenderer fr = af.alignPanel.getFeatureRenderer();
791  1 final String gffHeader = "##gff-version 2\n";
792   
793  1 fr.setVisible("METAL");
794  1 fr.setColour("METAL", new FeatureColour(Color.PINK));
795  1 String exported = featuresFile.printGffFormat(al.getSequencesArray(),
796    fr, false, false);
797  1 String expected = gffHeader
798    + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\tclin_sig=Likely Pathogenic;AF=24\n"
799    + "FER_CAPAA\tCath\tMETAL\t41\t41\t0.6\t.\t.\tclin_sig=Benign;AF=46\n";
800  1 assertEquals(expected, exported);
801   
802    /*
803    * now threshold to Score > 1.1 - should exclude sf2
804    */
805  1 FeatureColourI fc = new FeatureColour(null, Color.white, Color.BLACK,
806    Color.white, 0f, 2f);
807  1 fc.setAboveThreshold(true);
808  1 fc.setThreshold(1.1f);
809  1 fr.setColour("METAL", fc);
810  1 exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
811    false, false);
812  1 expected = gffHeader
813    + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\tclin_sig=Likely Pathogenic;AF=24\n";
814  1 assertEquals(expected, exported);
815   
816    /*
817    * remove threshold and check sf2 is exported
818    */
819  1 fc.setAboveThreshold(false);
820  1 exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
821    false, false);
822  1 expected = gffHeader
823    + "FER_CAPAA\tCath\tMETAL\t39\t39\t1.2\t.\t.\tclin_sig=Likely Pathogenic;AF=24\n"
824    + "FER_CAPAA\tCath\tMETAL\t41\t41\t0.6\t.\t.\tclin_sig=Benign;AF=46\n";
825  1 assertEquals(expected, exported);
826   
827    /*
828    * filter on (clin_sig contains Benign) - should include sf2 and exclude sf1
829    */
830  1 FeatureMatcherSetI filter = new FeatureMatcherSet();
831  1 filter.and(FeatureMatcher.byAttribute(Condition.Contains, "benign",
832    "clin_sig"));
833  1 fr.setFeatureFilter("METAL", filter);
834  1 exported = featuresFile.printGffFormat(al.getSequencesArray(), fr,
835    false, false);
836  1 expected = gffHeader
837    + "FER_CAPAA\tCath\tMETAL\t41\t41\t0.6\t.\t.\tclin_sig=Benign;AF=46\n";
838  1 assertEquals(expected, exported);
839    }
840   
841    /**
842    * Output as Jalview should not include features which are not visible due to
843    * colour threshold or feature filter settings
844    *
845    * @throws Exception
846    */
 
847  1 toggle @Test(groups = { "Functional" })
848    public void testPrintJalviewFormat_withFilters() throws Exception
849    {
850  1 File f = new File("examples/uniref50.fa");
851  1 AlignmentI al = readAlignmentFile(f);
852  1 AlignFrame af = new AlignFrame(al, 500, 500);
853  1 SequenceFeature sf1 = new SequenceFeature("METAL", "Cath", 39, 39, 1.2f,
854    "grp1");
855  1 sf1.setValue("clin_sig", "Likely Pathogenic");
856  1 sf1.setValue("AF", "24");
857  1 al.getSequenceAt(0).addSequenceFeature(sf1);
858  1 SequenceFeature sf2 = new SequenceFeature("METAL", "Cath", 41, 41, 0.6f,
859    "grp2");
860  1 sf2.setValue("clin_sig", "Benign");
861  1 sf2.setValue("AF", "46");
862  1 al.getSequenceAt(0).addSequenceFeature(sf2);
863   
864  1 FeaturesFile featuresFile = new FeaturesFile();
865  1 FeatureRenderer fr = af.alignPanel.getFeatureRenderer();
866  1 fr.findAllFeatures(true);
867   
868  1 fr.setVisible("METAL");
869  1 fr.setColour("METAL", new FeatureColour(Color.PINK));
870  1 String exported = featuresFile.printJalviewFormat(
871    al.getSequencesArray(),
872    fr, false, false);
873  1 String expected = "METAL\tffafaf\n\nSTARTGROUP\tgrp1\n"
874    + "Cath\tFER_CAPAA\t-1\t39\t39\tMETAL\t1.2\n"
875    + "ENDGROUP\tgrp1\n\nSTARTGROUP\tgrp2\n"
876    + "Cath\tFER_CAPAA\t-1\t41\t41\tMETAL\t0.6\n"
877    + "ENDGROUP\tgrp2\n";
878  1 assertEquals(expected, exported);
879   
880    /*
881    * now threshold to Score > 1.1 - should exclude sf2
882    * (and there should be no empty STARTGROUP/ENDGROUP output)
883    */
884  1 FeatureColourI fc = new FeatureColour(null, Color.white, Color.BLACK,
885    Color.white, 0f, 2f);
886  1 fc.setAboveThreshold(true);
887  1 fc.setThreshold(1.1f);
888  1 fr.setColour("METAL", fc);
889  1 exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
890    false, false);
891  1 expected = "METAL\tscore|ffffff|000000|noValueMin|abso|0.0|2.0|above|1.1\n\n"
892    + "STARTGROUP\tgrp1\n"
893    + "Cath\tFER_CAPAA\t-1\t39\t39\tMETAL\t1.2\n"
894    + "ENDGROUP\tgrp1\n";
895  1 assertEquals(expected, exported);
896   
897    /*
898    * remove threshold and check sf2 is exported
899    */
900  1 fc.setAboveThreshold(false);
901  1 exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
902    false, false);
903  1 expected = "METAL\tscore|ffffff|000000|noValueMin|abso|0.0|2.0|none\n\n"
904    + "STARTGROUP\tgrp1\n"
905    + "Cath\tFER_CAPAA\t-1\t39\t39\tMETAL\t1.2\n"
906    + "ENDGROUP\tgrp1\n\nSTARTGROUP\tgrp2\n"
907    + "Cath\tFER_CAPAA\t-1\t41\t41\tMETAL\t0.6\n"
908    + "ENDGROUP\tgrp2\n";
909  1 assertEquals(expected, exported);
910   
911    /*
912    * filter on (clin_sig contains Benign) - should include sf2 and exclude sf1
913    */
914  1 FeatureMatcherSetI filter = new FeatureMatcherSet();
915  1 filter.and(FeatureMatcher.byAttribute(Condition.Contains, "benign",
916    "clin_sig"));
917  1 fr.setFeatureFilter("METAL", filter);
918  1 exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
919    false, false);
920  1 expected = "FER_CAPAA\tCath\tMETAL\t41\t41\t0.6\t.\t.\n";
921  1 expected = "METAL\tscore|ffffff|000000|noValueMin|abso|0.0|2.0|none\n\n"
922    + "STARTFILTERS\nMETAL\tclin_sig Contains benign\nENDFILTERS\n\n"
923    + "STARTGROUP\tgrp2\n"
924    + "Cath\tFER_CAPAA\t-1\t41\t41\tMETAL\t0.6\n"
925    + "ENDGROUP\tgrp2\n";
926  1 assertEquals(expected, exported);
927    }
928    }