37ef06927a8e79dc20192b70aa788072ed569628
[jalview.git] / test / jalview / project / Jalview2xmlTests.java
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.project;
22
23 import static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertFalse;
25 import static org.testng.Assert.assertNotNull;
26 import static org.testng.Assert.assertNotSame;
27 import static org.testng.Assert.assertNull;
28 import static org.testng.Assert.assertSame;
29 import static org.testng.Assert.assertTrue;
30
31 import java.awt.Color;
32 import java.awt.Rectangle;
33 import java.io.File;
34 import java.io.IOException;
35 import java.math.BigInteger;
36 import java.util.ArrayList;
37 import java.util.BitSet;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Locale;
41 import java.util.Map;
42
43 import javax.swing.JInternalFrame;
44
45 import org.testng.Assert;
46 import org.testng.AssertJUnit;
47 import org.testng.annotations.BeforeClass;
48 import org.testng.annotations.Test;
49
50 import jalview.analysis.scoremodels.SimilarityParams;
51 import jalview.api.AlignViewportI;
52 import jalview.api.AlignmentViewPanel;
53 import jalview.api.FeatureColourI;
54 import jalview.api.ViewStyleI;
55 import jalview.bin.Cache;
56 import jalview.datamodel.AlignmentAnnotation;
57 import jalview.datamodel.AlignmentI;
58 import jalview.datamodel.Annotation;
59 import jalview.datamodel.ContactListI;
60 import jalview.datamodel.ContactMatrix;
61 import jalview.datamodel.ContactMatrixI;
62 import jalview.datamodel.DBRefEntry;
63 import jalview.datamodel.GeneLocus;
64 import jalview.datamodel.GroupSet;
65 import jalview.datamodel.HiddenSequences;
66 import jalview.datamodel.Mapping;
67 import jalview.datamodel.PDBEntry;
68 import jalview.datamodel.PDBEntry.Type;
69 import jalview.datamodel.Sequence.DBModList;
70 import jalview.datamodel.SequenceCollectionI;
71 import jalview.datamodel.SequenceFeature;
72 import jalview.datamodel.SequenceGroup;
73 import jalview.datamodel.SequenceI;
74 import jalview.datamodel.features.FeatureMatcher;
75 import jalview.datamodel.features.FeatureMatcherSet;
76 import jalview.datamodel.features.FeatureMatcherSetI;
77 import jalview.gui.AlignFrame;
78 import jalview.gui.AlignViewport;
79 import jalview.gui.AlignmentPanel;
80 import jalview.gui.Desktop;
81 import jalview.gui.JvOptionPane;
82 import jalview.gui.OverviewPanel;
83 import jalview.gui.PCAPanel;
84 import jalview.gui.PopupMenu;
85 import jalview.gui.Preferences;
86 import jalview.gui.SliderPanel;
87 import jalview.io.DataSourceType;
88 import jalview.io.FileFormat;
89 import jalview.io.FileLoader;
90 import jalview.io.Jalview2xmlBase;
91 import jalview.renderer.ResidueShaderI;
92 import jalview.schemes.AnnotationColourGradient;
93 import jalview.schemes.BuriedColourScheme;
94 import jalview.schemes.ColourSchemeI;
95 import jalview.schemes.ColourSchemeProperty;
96 import jalview.schemes.FeatureColour;
97 import jalview.schemes.JalviewColourScheme;
98 import jalview.schemes.RNAHelicesColour;
99 import jalview.schemes.StrandColourScheme;
100 import jalview.schemes.TCoffeeColourScheme;
101 import jalview.structure.StructureImportSettings;
102 import jalview.util.MapList;
103 import jalview.util.matcher.Condition;
104 import jalview.viewmodel.AlignmentViewport;
105 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
106 import jalview.ws.datamodel.MappableContactMatrixI;
107 import jalview.ws.datamodel.alphafold.PAEContactMatrix;
108
109 @Test(singleThreaded = true)
110 public class Jalview2xmlTests extends Jalview2xmlBase
111 {
112
113   @Override
114   @BeforeClass(alwaysRun = true)
115   public void setUpJvOptionPane()
116   {
117     JvOptionPane.setInteractiveMode(false);
118     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
119   }
120
121   @Test(groups = { "Functional" })
122   public void testRNAStructureRecovery() throws Exception
123   {
124     String inFile = "examples/RF00031_folded.stk";
125     String tfile = File.createTempFile("JalviewTest", ".jvp")
126             .getAbsolutePath();
127     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
128             DataSourceType.FILE);
129     assertNotNull(af, "Didn't read input file " + inFile);
130     int olddsann = countDsAnn(af.getViewport());
131     assertTrue(olddsann > 0, "Didn't find any dataset annotations");
132     af.changeColour_actionPerformed(
133             JalviewColourScheme.RNAHelices.toString());
134     assertTrue(
135             af.getViewport()
136                     .getGlobalColourScheme() instanceof RNAHelicesColour,
137             "Couldn't apply RNA helices colourscheme");
138     af.saveAlignment(tfile, FileFormat.Jalview);
139     assertTrue(af.isSaveAlignmentSuccessful(),
140             "Failed to store as a project.");
141     af.closeMenuItem_actionPerformed(true);
142     af = null;
143     af = new FileLoader().LoadFileWaitTillLoaded(tfile,
144             DataSourceType.FILE);
145     assertNotNull(af, "Failed to import new project");
146     int newdsann = countDsAnn(af.getViewport());
147     assertEquals(olddsann, newdsann,
148             "Differing numbers of dataset sequence annotation\nOriginally "
149                     + olddsann + " and now " + newdsann);
150     System.out.println(
151             "Read in same number of annotations as originally present ("
152                     + olddsann + ")");
153     assertTrue(
154
155             af.getViewport()
156                     .getGlobalColourScheme() instanceof RNAHelicesColour,
157             "RNA helices colourscheme was not applied on import.");
158   }
159
160   @Test(groups = { "Functional" })
161   public void testTCoffeeScores() throws Exception
162   {
163     String inFile = "examples/uniref50.fa",
164             inAnnot = "examples/uniref50.score_ascii";
165     String tfile = File.createTempFile("JalviewTest", ".jvp")
166             .getAbsolutePath();
167     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
168             DataSourceType.FILE);
169     assertNotNull(af, "Didn't read input file " + inFile);
170     af.loadJalviewDataFile(inAnnot, DataSourceType.FILE, null, null);
171     AlignViewport viewport = af.getViewport();
172     assertSame(viewport.getGlobalColourScheme().getClass(),
173             TCoffeeColourScheme.class, "Didn't set T-coffee colourscheme");
174     assertNotNull(
175             ColourSchemeProperty.getColourScheme(viewport,
176                     viewport.getAlignment(),
177                     viewport.getGlobalColourScheme().getSchemeName()),
178             "Recognise T-Coffee score from string");
179
180     af.saveAlignment(tfile, FileFormat.Jalview);
181     assertTrue(af.isSaveAlignmentSuccessful(),
182             "Failed to store as a project.");
183     af.closeMenuItem_actionPerformed(true);
184     af = null;
185     af = new FileLoader().LoadFileWaitTillLoaded(tfile,
186             DataSourceType.FILE);
187     assertNotNull(af, "Failed to import new project");
188     assertSame(af.getViewport().getGlobalColourScheme().getClass(),
189             TCoffeeColourScheme.class,
190             "Didn't set T-coffee colourscheme for imported project.");
191     System.out.println(
192             "T-Coffee score shading successfully recovered from project.");
193   }
194
195   @Test(groups = { "Functional" })
196   public void testColourByAnnotScores() throws Exception
197   {
198     String inFile = "examples/uniref50.fa",
199             inAnnot = "examples/testdata/uniref50_iupred.jva";
200     String tfile = File.createTempFile("JalviewTest", ".jvp")
201             .getAbsolutePath();
202     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
203             DataSourceType.FILE);
204     assertNotNull(af, "Didn't read input file " + inFile);
205     af.loadJalviewDataFile(inAnnot, DataSourceType.FILE, null, null);
206     AlignmentAnnotation[] aa = af.getViewport().getAlignment()
207             .getSequenceAt(0).getAnnotation("IUPredWS (Short)");
208     assertTrue(
209
210             aa != null && aa.length > 0,
211             "Didn't find any IUPred annotation to use to shade alignment.");
212     AnnotationColourGradient cs = new AnnotationColourGradient(aa[0], null,
213             AnnotationColourGradient.ABOVE_THRESHOLD);
214     AnnotationColourGradient gcs = new AnnotationColourGradient(aa[0], null,
215             AnnotationColourGradient.BELOW_THRESHOLD);
216     cs.setSeqAssociated(true);
217     gcs.setSeqAssociated(true);
218     af.changeColour(cs);
219     SequenceGroup sg = new SequenceGroup();
220     sg.setStartRes(57);
221     sg.setEndRes(92);
222     sg.cs.setColourScheme(gcs);
223     af.getViewport().getAlignment().addGroup(sg);
224     sg.addSequence(af.getViewport().getAlignment().getSequenceAt(1), false);
225     sg.addSequence(af.getViewport().getAlignment().getSequenceAt(2), true);
226     af.alignPanel.alignmentChanged();
227     af.saveAlignment(tfile, FileFormat.Jalview);
228     assertTrue(af.isSaveAlignmentSuccessful(),
229             "Failed to store as a project.");
230     af.closeMenuItem_actionPerformed(true);
231     af = null;
232     af = new FileLoader().LoadFileWaitTillLoaded(tfile,
233             DataSourceType.FILE);
234     assertNotNull(af, "Failed to import new project");
235
236     // check for group and alignment colourschemes
237
238     ColourSchemeI _rcs = af.getViewport().getGlobalColourScheme();
239     ColourSchemeI _rgcs = af.getViewport().getAlignment().getGroups().get(0)
240             .getColourScheme();
241     assertNotNull(_rcs, "Didn't recover global colourscheme");
242     assertTrue(_rcs instanceof AnnotationColourGradient,
243             "Didn't recover annotation colour global scheme");
244     AnnotationColourGradient __rcs = (AnnotationColourGradient) _rcs;
245     assertTrue(__rcs.isSeqAssociated(),
246             "Annotation colourscheme wasn't sequence associated");
247
248     boolean diffseqcols = false, diffgseqcols = false;
249     SequenceI[] sqs = af.getViewport().getAlignment().getSequencesArray();
250     for (int p = 0, pSize = af.getViewport().getAlignment()
251             .getWidth(); p < pSize && (!diffseqcols || !diffgseqcols); p++)
252     {
253       if (_rcs.findColour(sqs[0].getCharAt(p), p, sqs[0], null, 0f) != _rcs
254               .findColour(sqs[5].getCharAt(p), p, sqs[5], null, 0f))
255       {
256         diffseqcols = true;
257       }
258     }
259     assertTrue(diffseqcols, "Got Different sequence colours");
260     System.out.println(
261             "Per sequence colourscheme (Background) successfully applied and recovered.");
262
263     assertNotNull(_rgcs, "Didn't recover group colourscheme");
264     assertTrue(_rgcs instanceof AnnotationColourGradient,
265             "Didn't recover annotation colour group colourscheme");
266     __rcs = (AnnotationColourGradient) _rgcs;
267     assertTrue(__rcs.isSeqAssociated(),
268             "Group Annotation colourscheme wasn't sequence associated");
269
270     for (int p = 0, pSize = af.getViewport().getAlignment()
271             .getWidth(); p < pSize && (!diffseqcols || !diffgseqcols); p++)
272     {
273       if (_rgcs.findColour(sqs[1].getCharAt(p), p, sqs[1], null,
274               0f) != _rgcs.findColour(sqs[2].getCharAt(p), p, sqs[2], null,
275                       0f))
276       {
277         diffgseqcols = true;
278       }
279     }
280     assertTrue(diffgseqcols, "Got Different group sequence colours");
281     System.out.println(
282             "Per sequence (Group) colourscheme successfully applied and recovered.");
283   }
284
285   @Test(groups = { "Functional" })
286   public void gatherViewsHere() throws Exception
287   {
288     int origCount = Desktop.getAlignFrames() == null ? 0
289             : Desktop.getAlignFrames().length;
290     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
291             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
292     assertNotNull(af, "Didn't read in the example file correctly.");
293     assertTrue(Desktop.getAlignFrames().length == 1 + origCount,
294             "Didn't gather the views in the example file.");
295
296   }
297
298   /**
299    * Test for JAL-2223 - multiple mappings in View Mapping report
300    * 
301    * @throws Exception
302    */
303   @Test(groups = { "Functional" })
304   public void noDuplicatePdbMappingsMade() throws Exception
305   {
306     StructureImportSettings.setProcessSecondaryStructure(true);
307     StructureImportSettings.setVisibleChainAnnotation(true);
308     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
309             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
310     assertNotNull(af, "Didn't read in the example file correctly.");
311
312     // locate Jmol viewer
313     // count number of PDB mappings the structure selection manager holds -
314     String pdbFile = af.getCurrentView().getStructureSelectionManager()
315             .findFileForPDBId("1A70");
316     assertEquals(
317             af.getCurrentView().getStructureSelectionManager()
318                     .getMapping(pdbFile).length,
319             2, "Expected only two mappings for 1A70");
320
321   }
322
323   @Test(groups = { "Functional" })
324   public void viewRefPdbAnnotation() throws Exception
325   {
326     StructureImportSettings.setProcessSecondaryStructure(true);
327     StructureImportSettings.setVisibleChainAnnotation(true);
328     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
329             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
330     assertNotNull(af, "Didn't read in the example file correctly.");
331     AlignmentViewPanel sps = null;
332     for (AlignmentViewPanel ap : af.alignPanel.alignFrame.getAlignPanels())
333     {
334       if ("Spinach Feredoxin Structure".equals(ap.getViewName()))
335       {
336         sps = ap;
337         break;
338       }
339     }
340     assertNotNull(sps, "Couldn't find the structure view");
341     AlignmentAnnotation refan = null;
342     for (AlignmentAnnotation ra : sps.getAlignment()
343             .getAlignmentAnnotation())
344     {
345       if (ra.graph != 0)
346       {
347         refan = ra;
348         break;
349       }
350     }
351     assertNotNull(refan, "Annotation secondary structure not found.");
352     SequenceI sq = sps.getAlignment().findName("1A70|");
353     assertNotNull(sq, "Couldn't find 1a70 null chain");
354     // compare the manually added temperature factor annotation
355     // to the track automatically transferred from the pdb structure on load
356     assertNotNull(sq.getDatasetSequence().getAnnotation(),
357             "1a70 has no annotation");
358     for (AlignmentAnnotation ala : sq.getDatasetSequence().getAnnotation())
359     {
360       AlignmentAnnotation alaa;
361       sq.addAlignmentAnnotation(alaa = new AlignmentAnnotation(ala));
362       alaa.adjustForAlignment();
363       if (ala.graph == refan.graph)
364       {
365         for (int p = 0; p < ala.annotations.length; p++)
366         {
367           sq.findPosition(p);
368           try
369           {
370             assertTrue((alaa.annotations[p] == null
371                     && refan.annotations[p] == null)
372                     || alaa.annotations[p].value == refan.annotations[p].value,
373                     "Mismatch at alignment position " + p);
374           } catch (NullPointerException q)
375           {
376             Assert.fail("Mismatch of alignment annotations at position " + p
377                     + " Ref seq ann: " + refan.annotations[p]
378                     + " alignment " + alaa.annotations[p]);
379           }
380         }
381       }
382     }
383
384   }
385
386   @Test(groups = { "Functional" })
387   public void testCopyViewSettings() throws Exception
388   {
389     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
390             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
391     assertNotNull(af, "Didn't read in the example file correctly.");
392     AlignmentViewPanel sps = null, groups = null;
393     for (AlignmentViewPanel ap : af.alignPanel.alignFrame.getAlignPanels())
394     {
395       if ("Spinach Feredoxin Structure".equals(ap.getViewName()))
396       {
397         sps = ap;
398       }
399       if (ap.getViewName().contains("MAFFT"))
400       {
401         groups = ap;
402       }
403     }
404     assertNotNull(sps, "Couldn't find the structure view");
405     assertNotNull(groups, "Couldn't find the MAFFT view");
406
407     ViewStyleI structureStyle = sps.getAlignViewport().getViewStyle();
408     ViewStyleI groupStyle = groups.getAlignViewport().getViewStyle();
409     AssertJUnit.assertFalse(structureStyle.sameStyle(groupStyle));
410
411     groups.getAlignViewport().setViewStyle(structureStyle);
412     AssertJUnit.assertFalse(
413             groupStyle.sameStyle(groups.getAlignViewport().getViewStyle()));
414     Assert.assertTrue(structureStyle
415             .sameStyle(groups.getAlignViewport().getViewStyle()));
416
417   }
418
419   /**
420    * test store and recovery of expanded views
421    * 
422    * @throws Exception
423    */
424   @Test(groups = { "Functional" }, enabled = true)
425   public void testStoreAndRecoverExpandedviews() throws Exception
426   {
427     Desktop.instance.closeAll_actionPerformed(null);
428
429     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
430             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
431     Assert.assertEquals(Desktop.getAlignFrames().length, 1);
432     String afid = af.getViewport().getSequenceSetId();
433
434     // check FileLoader returned a reference to the one alignFrame that is
435     // actually on the Desktop
436     assertSame(af, Desktop.getAlignFrameFor(af.getViewport()),
437             "Jalview2XML.loadAlignFrame() didn't return correct AlignFrame reference for multiple view window");
438
439     Desktop.explodeViews(af);
440
441     int oldviews = Desktop.getAlignFrames().length;
442     Assert.assertEquals(Desktop.getAlignFrames().length,
443             Desktop.getAlignmentPanels(afid).length);
444     File tfile = File.createTempFile("testStoreAndRecoverExpanded", ".jvp");
445     try
446     {
447       new Jalview2XML(false).saveState(tfile);
448     } catch (Error e)
449     {
450       Assert.fail("Didn't save the expanded view state", e);
451     } catch (Exception e)
452     {
453       Assert.fail("Didn't save the expanded view state", e);
454     }
455     Desktop.instance.closeAll_actionPerformed(null);
456     if (Desktop.getAlignFrames() != null)
457     {
458       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
459     }
460     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
461             DataSourceType.FILE);
462     Assert.assertNotNull(af);
463     Assert.assertEquals(Desktop.getAlignFrames().length,
464             Desktop.getAlignmentPanels(
465                     af.getViewport().getSequenceSetId()).length);
466     Assert.assertEquals(Desktop
467             .getAlignmentPanels(af.getViewport().getSequenceSetId()).length,
468             oldviews);
469   }
470
471   /**
472    * Test save and reload of a project with a different representative sequence
473    * in each view.
474    * 
475    * @throws Exception
476    */
477   @Test(groups = { "Functional" })
478   public void testStoreAndRecoverReferenceSeqSettings() throws Exception
479   {
480     Desktop.instance.closeAll_actionPerformed(null);
481     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
482             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
483     assertNotNull(af, "Didn't read in the example file correctly.");
484     String afid = af.getViewport().getSequenceSetId();
485
486     // remember reference sequence for each panel
487     Map<String, SequenceI> refseqs = new HashMap<>();
488
489     /*
490      * mark sequence 2, 3, 4.. in panels 1, 2, 3...
491      * as reference sequence for itself and the preceding sequence
492      */
493     int n = 1;
494     for (AlignmentViewPanel ap : Desktop.getAlignmentPanels(afid))
495     {
496       AlignViewportI av = ap.getAlignViewport();
497       AlignmentI alignment = ap.getAlignment();
498       int repIndex = n % alignment.getHeight();
499       SequenceI rep = alignment.getSequenceAt(repIndex);
500       refseqs.put(ap.getViewName(), rep);
501
502       // code from mark/unmark sequence as reference in jalview.gui.PopupMenu
503       // todo refactor this to an alignment view controller
504       av.setDisplayReferenceSeq(true);
505       av.setColourByReferenceSeq(true);
506       av.getAlignment().setSeqrep(rep);
507
508       n++;
509     }
510     File tfile = File.createTempFile("testStoreAndRecoverReferenceSeq",
511             ".jvp");
512     try
513     {
514       new Jalview2XML(false).saveState(tfile);
515     } catch (Throwable e)
516     {
517       Assert.fail("Didn't save the expanded view state", e);
518     }
519     Desktop.instance.closeAll_actionPerformed(null);
520     if (Desktop.getAlignFrames() != null)
521     {
522       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
523     }
524
525     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
526             DataSourceType.FILE);
527     afid = af.getViewport().getSequenceSetId();
528
529     for (AlignmentViewPanel ap : Desktop.getAlignmentPanels(afid))
530     {
531       // check representative
532       AlignmentI alignment = ap.getAlignment();
533       SequenceI rep = alignment.getSeqrep();
534       Assert.assertNotNull(rep,
535               "Couldn't restore sequence representative from project");
536       // can't use a strong equals here, because by definition, the sequence IDs
537       // will be different.
538       // could set vamsas session save/restore flag to preserve IDs across
539       // load/saves.
540       Assert.assertEquals(refseqs.get(ap.getViewName()).toString(),
541               rep.toString(),
542               "Representative wasn't the same when recovered.");
543       Assert.assertTrue(ap.getAlignViewport().isDisplayReferenceSeq(),
544               "Display reference sequence view setting not set.");
545       Assert.assertTrue(ap.getAlignViewport().isColourByReferenceSeq(),
546               "Colour By Reference Seq view setting not set.");
547     }
548   }
549
550   @Test(groups = { "Functional" })
551   public void testIsVersionStringLaterThan()
552   {
553     /*
554      * No version / development / test / autobuild is leniently assumed to be
555      * compatible
556      */
557     assertTrue(Jalview2XML.isVersionStringLaterThan(null, null));
558     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", null));
559     assertTrue(Jalview2XML.isVersionStringLaterThan(null, "2.8.3"));
560     assertTrue(Jalview2XML.isVersionStringLaterThan(null,
561             "Development Build"));
562     assertTrue(Jalview2XML.isVersionStringLaterThan(null,
563             "DEVELOPMENT BUILD"));
564     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3",
565             "Development Build"));
566     assertTrue(Jalview2XML.isVersionStringLaterThan(null, "Test"));
567     assertTrue(Jalview2XML.isVersionStringLaterThan(null, "TEST"));
568     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "Test"));
569     assertTrue(
570             Jalview2XML.isVersionStringLaterThan(null, "Automated Build"));
571     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3",
572             "Automated Build"));
573     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3",
574             "AUTOMATED BUILD"));
575
576     /*
577      * same version returns true i.e. compatible
578      */
579     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8", "2.8"));
580     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.3"));
581     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3b1", "2.8.3b1"));
582     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3B1", "2.8.3b1"));
583     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3b1", "2.8.3B1"));
584
585     /*
586      * later version returns true
587      */
588     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.4"));
589     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.9"));
590     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.9.2"));
591     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8", "2.8.3"));
592     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.3b1"));
593
594     /*
595      * earlier version returns false
596      */
597     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8"));
598     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.4", "2.8.3"));
599     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.3b1", "2.8.3"));
600     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.2b1"));
601     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.0b2", "2.8.0b1"));
602     /*
603      * test for patch release versions
604      */
605     assertFalse(Jalview2XML.isVersionStringLaterThan("2.11.3.0", "2.11.2"));
606     assertTrue(Jalview2XML.isVersionStringLaterThan("2.11.3.0", "2.11.4"));
607     assertFalse(
608             Jalview2XML.isVersionStringLaterThan("2.12.2.0b1", "2.12.2.0"));
609     assertFalse(
610             Jalview2XML.isVersionStringLaterThan("2.12.2.3", "2.12.2.2"));
611
612   }
613
614   /**
615    * Test save and reload of a project with a different sequence group (and
616    * representative sequence) in each view.
617    * 
618    * @throws Exception
619    */
620   @Test(groups = { "Functional" })
621   public void testStoreAndRecoverGroupRepSeqs() throws Exception
622   {
623     Desktop.instance.closeAll_actionPerformed(null);
624     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
625             "examples/uniref50.fa", DataSourceType.FILE);
626     assertNotNull(af, "Didn't read in the example file correctly.");
627     String afid = af.getViewport().getSequenceSetId();
628     // make a second view of the alignment
629     af.newView_actionPerformed(null);
630
631     /*
632      * remember representative and hidden sequences marked 
633      * on each panel
634      */
635     Map<String, SequenceI> repSeqs = new HashMap<>();
636     Map<String, List<String>> hiddenSeqNames = new HashMap<>();
637
638     /*
639      * mark sequence 2, 3, 4.. in panels 1, 2, 3...
640      * as reference sequence for itself and the preceding sequence
641      */
642     int n = 1;
643     for (AlignmentViewPanel ap : Desktop.getAlignmentPanels(afid))
644     {
645       AlignViewportI av = ap.getAlignViewport();
646       AlignmentI alignment = ap.getAlignment();
647       int repIndex = n % alignment.getHeight();
648       // ensure at least one preceding sequence i.e. index >= 1
649       repIndex = Math.max(repIndex, 1);
650       SequenceI repSeq = alignment.getSequenceAt(repIndex);
651       repSeqs.put(ap.getViewName(), repSeq);
652       List<String> hiddenNames = new ArrayList<>();
653       hiddenSeqNames.put(ap.getViewName(), hiddenNames);
654
655       /*
656        * have rep sequence represent itself and the one before it
657        * this hides the group (except for the rep seq)
658        */
659       SequenceGroup sg = new SequenceGroup();
660       sg.addSequence(repSeq, false);
661       SequenceI precedingSeq = alignment.getSequenceAt(repIndex - 1);
662       sg.addSequence(precedingSeq, false);
663       sg.setSeqrep(repSeq);
664       assertTrue(sg.getSequences().contains(repSeq));
665       assertTrue(sg.getSequences().contains(precedingSeq));
666       av.setSelectionGroup(sg);
667       assertSame(repSeq, sg.getSeqrep());
668
669       /*
670        * represent group with sequence adds to a map of hidden rep sequences
671        * (it does not create a group on the alignment) 
672        */
673       ((AlignmentViewport) av).hideSequences(repSeq, true);
674       assertSame(repSeq, sg.getSeqrep());
675       assertTrue(sg.getSequences().contains(repSeq));
676       assertTrue(sg.getSequences().contains(precedingSeq));
677       assertTrue(alignment.getGroups().isEmpty(), "alignment has groups");
678       Map<SequenceI, SequenceCollectionI> hiddenRepSeqsMap = av
679               .getHiddenRepSequences();
680       assertNotNull(hiddenRepSeqsMap);
681       assertEquals(1, hiddenRepSeqsMap.size());
682       assertSame(sg, hiddenRepSeqsMap.get(repSeq));
683       assertTrue(alignment.getHiddenSequences().isHidden(precedingSeq));
684       assertFalse(alignment.getHiddenSequences().isHidden(repSeq));
685       hiddenNames.add(precedingSeq.getName());
686
687       n++;
688     }
689     File tfile = File.createTempFile("testStoreAndRecoverGroupReps",
690             ".jvp");
691     try
692     {
693       new Jalview2XML(false).saveState(tfile);
694     } catch (Throwable e)
695     {
696       Assert.fail("Didn't save the expanded view state", e);
697     }
698     Desktop.instance.closeAll_actionPerformed(null);
699     if (Desktop.getAlignFrames() != null)
700     {
701       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
702     }
703
704     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
705             DataSourceType.FILE);
706     afid = af.getViewport().getSequenceSetId();
707
708     for (AlignmentViewPanel ap : Desktop.getAlignmentPanels(afid))
709     {
710       String viewName = ap.getViewName();
711       AlignViewportI av = ap.getAlignViewport();
712       AlignmentI alignment = ap.getAlignment();
713       List<SequenceGroup> groups = alignment.getGroups();
714       assertNotNull(groups);
715       assertTrue(groups.isEmpty(), "Alignment has groups");
716       Map<SequenceI, SequenceCollectionI> hiddenRepSeqsMap = av
717               .getHiddenRepSequences();
718       assertNotNull(hiddenRepSeqsMap, "No hidden represented sequences");
719       assertEquals(1, hiddenRepSeqsMap.size());
720       assertEquals(repSeqs.get(viewName).getDisplayId(true),
721               hiddenRepSeqsMap.keySet().iterator().next()
722                       .getDisplayId(true));
723
724       /*
725        * verify hidden sequences in restored panel
726        */
727       List<String> hidden = hiddenSeqNames.get(ap.getViewName());
728       HiddenSequences hs = alignment.getHiddenSequences();
729       assertEquals(hidden.size(), hs.getSize(),
730               "wrong number of restored hidden sequences in "
731                       + ap.getViewName());
732     }
733   }
734
735   /**
736    * Test save and reload of PDBEntry in Jalview project
737    * 
738    * @throws Exception
739    */
740   @Test(groups = { "Functional" })
741   public void testStoreAndRecoverPDBEntry() throws Exception
742   {
743     Desktop.instance.closeAll_actionPerformed(null);
744     String exampleFile = "examples/3W5V.pdb";
745     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(exampleFile,
746             DataSourceType.FILE);
747     assertNotNull(af, "Didn't read in the example file correctly.");
748     String afid = af.getViewport().getSequenceSetId();
749
750     AlignmentPanel[] alignPanels = Desktop.getAlignmentPanels(afid);
751     System.out.println();
752     AlignmentViewPanel ap = alignPanels[0];
753     String tfileBase = new File(".").getAbsolutePath().replace(".", "");
754     String testFile = tfileBase + exampleFile;
755     AlignmentI alignment = ap.getAlignment();
756     System.out.println("blah");
757     SequenceI[] seqs = alignment.getSequencesArray();
758     Assert.assertNotNull(seqs[0]);
759     Assert.assertNotNull(seqs[1]);
760     Assert.assertNotNull(seqs[2]);
761     Assert.assertNotNull(seqs[3]);
762     Assert.assertNotNull(seqs[0].getDatasetSequence());
763     Assert.assertNotNull(seqs[1].getDatasetSequence());
764     Assert.assertNotNull(seqs[2].getDatasetSequence());
765     Assert.assertNotNull(seqs[3].getDatasetSequence());
766     PDBEntry[] pdbEntries = new PDBEntry[4];
767     pdbEntries[0] = new PDBEntry("3W5V", "A", Type.PDB, testFile);
768     pdbEntries[1] = new PDBEntry("3W5V", "B", Type.PDB, testFile);
769     pdbEntries[2] = new PDBEntry("3W5V", "C", Type.PDB, testFile);
770     pdbEntries[3] = new PDBEntry("3W5V", "D", Type.PDB, testFile);
771     Assert.assertEquals(
772             seqs[0].getDatasetSequence().getAllPDBEntries().get(0),
773             pdbEntries[0]);
774     Assert.assertEquals(
775             seqs[1].getDatasetSequence().getAllPDBEntries().get(0),
776             pdbEntries[1]);
777     Assert.assertEquals(
778             seqs[2].getDatasetSequence().getAllPDBEntries().get(0),
779             pdbEntries[2]);
780     Assert.assertEquals(
781             seqs[3].getDatasetSequence().getAllPDBEntries().get(0),
782             pdbEntries[3]);
783
784     File tfile = File.createTempFile("testStoreAndRecoverPDBEntry", ".jvp");
785     try
786     {
787       new Jalview2XML(false).saveState(tfile);
788     } catch (Throwable e)
789     {
790       Assert.fail("Didn't save the state", e);
791     }
792     Desktop.instance.closeAll_actionPerformed(null);
793     if (Desktop.getAlignFrames() != null)
794     {
795       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
796     }
797
798     AlignFrame restoredFrame = new FileLoader().LoadFileWaitTillLoaded(
799             tfile.getAbsolutePath(), DataSourceType.FILE);
800     String rfid = restoredFrame.getViewport().getSequenceSetId();
801     AlignmentPanel[] rAlignPanels = Desktop.getAlignmentPanels(rfid);
802     AlignmentViewPanel rap = rAlignPanels[0];
803     AlignmentI rAlignment = rap.getAlignment();
804     System.out.println("blah");
805     SequenceI[] rseqs = rAlignment.getSequencesArray();
806     Assert.assertNotNull(rseqs[0]);
807     Assert.assertNotNull(rseqs[1]);
808     Assert.assertNotNull(rseqs[2]);
809     Assert.assertNotNull(rseqs[3]);
810     Assert.assertNotNull(rseqs[0].getDatasetSequence());
811     Assert.assertNotNull(rseqs[1].getDatasetSequence());
812     Assert.assertNotNull(rseqs[2].getDatasetSequence());
813     Assert.assertNotNull(rseqs[3].getDatasetSequence());
814
815     // The Asserts below are expected to fail until the PDB chainCode is
816     // recoverable from a Jalview projects
817     for (int chain = 0; chain < 4; chain++)
818     {
819       PDBEntry recov = rseqs[chain].getDatasetSequence().getAllPDBEntries()
820               .get(0);
821       PDBEntry expected = pdbEntries[chain];
822       Assert.assertEquals(recov.getId(), expected.getId(),
823               "Mismatch PDB ID");
824       Assert.assertEquals(recov.getChainCode(), expected.getChainCode(),
825               "Mismatch PDB ID");
826       Assert.assertEquals(recov.getType(), expected.getType(),
827               "Mismatch PDBEntry 'Type'");
828       Assert.assertNotNull(recov.getFile(),
829               "Recovered PDBEntry should have a non-null file entry");
830       Assert.assertEquals(
831               recov.getFile().toLowerCase(Locale.ENGLISH)
832                       .lastIndexOf("pdb"),
833               recov.getFile().length() - 3,
834               "Recovered PDBEntry file should have PDB suffix");
835     }
836   }
837
838   /**
839    * Configure an alignment and a sub-group each with distinct colour schemes,
840    * Conservation and PID thresholds, and confirm these are restored from the
841    * saved project.
842    * 
843    * @throws IOException
844    */
845   @Test(groups = { "Functional" })
846   public void testStoreAndRecoverAnnotationRowElementColours()
847           throws IOException
848   {
849     Desktop.instance.closeAll_actionPerformed(null);
850     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded("SEQ\tMNQ",
851             DataSourceType.PASTE);
852
853     AlignViewport av = af.getViewport();
854     AlignmentI al = av.getAlignment();
855     SequenceI fsq;
856     fsq = al.getSequenceAt(0);
857     Annotation annots[] = new Annotation[fsq.getLength()];
858     AlignmentAnnotation ala = new AlignmentAnnotation("Colour", "Annots",
859             annots);
860     annots[0] = new Annotation(1.0f);
861     annots[1] = new Annotation(2.0f);
862     annots[2] = new Annotation(3.0f);
863     annots[0].colour = Color.RED;
864     annots[1].colour = Color.GREEN;
865     annots[2].colour = Color.BLUE;
866     ala.validateRangeAndDisplay();
867     al.getSequenceAt(0).addAlignmentAnnotation(ala);
868     al.addAnnotation(ala);
869     /*
870      * and colour by annotation
871      */
872     AnnotationColourGradient acg = new AnnotationColourGradient(ala,
873             af.alignPanel.av.getGlobalColourScheme(), 0);
874     acg.setSeqAssociated(true);
875     acg.setPredefinedColours(true);
876     af.changeColour(acg);
877     Color seqcol[] = new Color[3];
878     for (int iStart = fsq.findIndex(fsq.getStart()), i = 0; i < 3; i++)
879     {
880       seqcol[i] = af.alignPanel.getSeqPanel().seqCanvas
881               .getSequenceRenderer()
882               .getResidueColour(fsq, iStart + i, null);
883     }
884     /*
885      * save project, close windows, reload project, verify
886      */
887     File tfile = File.createTempFile(
888             "testStoreAndRecoverAnnotRowElemColors", ".jvp");
889     tfile.deleteOnExit();
890     new Jalview2XML(false).saveState(tfile);
891     // Desktop.instance.closeAll_actionPerformed(null);
892     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
893             DataSourceType.FILE);
894     Assert.assertNotNull(af, "Failed to reload project");
895     /*
896      * verify alignment annotation has colors
897      */
898     av = af.getViewport();
899
900     ColourSchemeI loadedCscheme = av.getGlobalColourScheme();
901     Assert.assertTrue(loadedCscheme instanceof AnnotationColourGradient,
902             "Didn't apply Annotation colour gradient");
903     acg = (AnnotationColourGradient) loadedCscheme;
904     assertTrue(acg.isSeqAssociated());
905     assertTrue(acg.isPredefinedColours());
906
907     al = av.getAlignment();
908     fsq = al.getSequenceAt(0);
909     ala = fsq.getAnnotation()[0];
910     Assert.assertNotNull(ala, "No annotation row recovered");
911     Assert.assertNotNull(ala.annotations);
912     for (int iStart = al.getSequenceAt(0)
913             .findIndex(al.getSequenceAt(0).getStart()), i = 0; i < 3; i++)
914     {
915       Assert.assertTrue(ala.annotations[i].colour != null);
916       Assert.assertTrue(ala.annotations[i].colour.equals(annots[i].colour));
917       Color newseqcol = af.alignPanel.getSeqPanel().seqCanvas
918               .getSequenceRenderer()
919               .getResidueColour(fsq, iStart + i, null);
920       Assert.assertTrue(seqcol[i].equals(newseqcol),
921               "Sequence shading is different");
922
923     }
924
925   }
926
927   /**
928    * Configure an alignment and a sub-group each with distinct colour schemes,
929    * Conservation and PID thresholds, and confirm these are restored from the
930    * saved project.
931    * 
932    * @throws IOException
933    */
934   @Test(groups = { "Functional" })
935   public void testStoreAndRecoverColourThresholds() throws IOException
936   {
937     Desktop.instance.closeAll_actionPerformed(null);
938     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
939             "examples/uniref50.fa", DataSourceType.FILE);
940
941     AlignViewport av = af.getViewport();
942     AlignmentI al = av.getAlignment();
943
944     /*
945      * Colour alignment by Buried Index, Above 10% PID, By Conservation 20%
946      */
947     av.setColourAppliesToAllGroups(false);
948     af.changeColour_actionPerformed(JalviewColourScheme.Buried.toString());
949     assertTrue(av.getGlobalColourScheme() instanceof BuriedColourScheme);
950     af.abovePIDThreshold_actionPerformed(true);
951     SliderPanel sp = SliderPanel.getSliderPanel();
952     assertFalse(sp.isForConservation());
953     sp.valueChanged(10);
954     af.conservationMenuItem_actionPerformed(true);
955     sp = SliderPanel.getSliderPanel();
956     assertTrue(sp.isForConservation());
957     sp.valueChanged(20);
958     ResidueShaderI rs = av.getResidueShading();
959     assertEquals(rs.getThreshold(), 10);
960     assertTrue(rs.conservationApplied());
961     assertEquals(rs.getConservationInc(), 20);
962
963     /*
964      * create a group with Strand colouring, 30% Conservation
965      * and 40% PID threshold
966      * (notice menu action applies to selection group even if mouse click
967      * is at a sequence not in the group)
968      */
969     SequenceGroup sg = new SequenceGroup();
970     sg.addSequence(al.getSequenceAt(0), false);
971     sg.setStartRes(15);
972     sg.setEndRes(25);
973     av.setSelectionGroup(sg);
974     PopupMenu popupMenu = new PopupMenu(af.alignPanel, al.getSequenceAt(2),
975             null);
976     popupMenu.changeColour_actionPerformed(
977             JalviewColourScheme.Strand.toString());
978     assertTrue(sg.getColourScheme() instanceof StrandColourScheme);
979     assertEquals(al.getGroups().size(), 1);
980     assertSame(al.getGroups().get(0), sg);
981     popupMenu.conservationMenuItem_actionPerformed(true);
982     sp = SliderPanel.getSliderPanel();
983     assertTrue(sp.isForConservation());
984     sp.valueChanged(30);
985     popupMenu.abovePIDColour_actionPerformed(true);
986     sp = SliderPanel.getSliderPanel();
987     assertFalse(sp.isForConservation());
988     sp.valueChanged(40);
989     assertTrue(sg.getGroupColourScheme().conservationApplied());
990     assertEquals(sg.getGroupColourScheme().getConservationInc(), 30);
991     assertEquals(sg.getGroupColourScheme().getThreshold(), 40);
992
993     /*
994      * save project, close windows, reload project, verify
995      */
996     File tfile = File.createTempFile("testStoreAndRecoverColourThresholds",
997             ".jvp");
998     tfile.deleteOnExit();
999     new Jalview2XML(false).saveState(tfile);
1000     Desktop.instance.closeAll_actionPerformed(null);
1001     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
1002             DataSourceType.FILE);
1003     Assert.assertNotNull(af, "Failed to reload project");
1004
1005     /*
1006      * verify alignment (background) colouring
1007      */
1008     rs = af.getViewport().getResidueShading();
1009     assertTrue(rs.getColourScheme() instanceof BuriedColourScheme);
1010     assertEquals(rs.getThreshold(), 10);
1011     assertTrue(rs.conservationApplied());
1012     assertEquals(rs.getConservationInc(), 20);
1013
1014     /*
1015      * verify group colouring
1016      */
1017     assertEquals(1, af.getViewport().getAlignment().getGroups().size(), 1);
1018     rs = af.getViewport().getAlignment().getGroups().get(0)
1019             .getGroupColourScheme();
1020     assertTrue(rs.getColourScheme() instanceof StrandColourScheme);
1021     assertEquals(rs.getThreshold(), 40);
1022     assertTrue(rs.conservationApplied());
1023     assertEquals(rs.getConservationInc(), 30);
1024   }
1025
1026   /**
1027    * Test save and reload of feature colour schemes and filter settings
1028    * 
1029    * @throws IOException
1030    */
1031   @Test(groups = { "Functional" })
1032   public void testSaveLoadFeatureColoursAndFilters() throws IOException
1033   {
1034     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
1035             ">Seq1\nACDEFGHIKLM", DataSourceType.PASTE);
1036     SequenceI seq1 = af.getViewport().getAlignment().getSequenceAt(0);
1037
1038     /*
1039      * add some features to the sequence
1040      */
1041     int score = 1;
1042     addFeatures(seq1, "type1", score++);
1043     addFeatures(seq1, "type2", score++);
1044     addFeatures(seq1, "type3", score++);
1045     addFeatures(seq1, "type4", score++);
1046     addFeatures(seq1, "type5", score++);
1047
1048     /*
1049      * set colour schemes for features
1050      */
1051     FeatureRendererModel fr = af.getFeatureRenderer();
1052     fr.findAllFeatures(true);
1053
1054     // type1: red
1055     fr.setColour("type1", new FeatureColour(Color.red));
1056
1057     // type2: by label
1058     FeatureColourI byLabel = new FeatureColour();
1059     byLabel.setColourByLabel(true);
1060     fr.setColour("type2", byLabel);
1061
1062     // type3: by score above threshold
1063     FeatureColourI byScore = new FeatureColour(null, Color.BLACK,
1064             Color.BLUE, null, 1, 10);
1065     byScore.setAboveThreshold(true);
1066     byScore.setThreshold(2f);
1067     fr.setColour("type3", byScore);
1068
1069     // type4: by attribute AF
1070     FeatureColourI byAF = new FeatureColour();
1071     byAF.setColourByLabel(true);
1072     byAF.setAttributeName("AF");
1073     fr.setColour("type4", byAF);
1074
1075     // type5: by attribute CSQ:PolyPhen below threshold
1076     FeatureColourI byPolyPhen = new FeatureColour(null, Color.BLACK,
1077             Color.BLUE, null, 1, 10);
1078     byPolyPhen.setBelowThreshold(true);
1079     byPolyPhen.setThreshold(3f);
1080     byPolyPhen.setAttributeName("CSQ", "PolyPhen");
1081     fr.setColour("type5", byPolyPhen);
1082
1083     /*
1084      * set filters for feature types
1085      */
1086
1087     // filter type1 features by (label contains "x")
1088     FeatureMatcherSetI filterByX = new FeatureMatcherSet();
1089     filterByX.and(FeatureMatcher.byLabel(Condition.Contains, "x"));
1090     fr.setFeatureFilter("type1", filterByX);
1091
1092     // filter type2 features by (score <= 2.4 and score > 1.1)
1093     FeatureMatcherSetI filterByScore = new FeatureMatcherSet();
1094     filterByScore.and(FeatureMatcher.byScore(Condition.LE, "2.4"));
1095     filterByScore.and(FeatureMatcher.byScore(Condition.GT, "1.1"));
1096     fr.setFeatureFilter("type2", filterByScore);
1097
1098     // filter type3 features by (AF contains X OR CSQ:PolyPhen != 0)
1099     FeatureMatcherSetI filterByXY = new FeatureMatcherSet();
1100     filterByXY
1101             .and(FeatureMatcher.byAttribute(Condition.Contains, "X", "AF"));
1102     filterByXY.or(FeatureMatcher.byAttribute(Condition.NE, "0", "CSQ",
1103             "PolyPhen"));
1104     fr.setFeatureFilter("type3", filterByXY);
1105
1106     /*
1107      * save as Jalview project
1108      */
1109     File tfile = File.createTempFile("JalviewTest", ".jvp");
1110     tfile.deleteOnExit();
1111     String filePath = tfile.getAbsolutePath();
1112     af.saveAlignment(filePath, FileFormat.Jalview);
1113     assertTrue(af.isSaveAlignmentSuccessful(),
1114             "Failed to store as a project.");
1115
1116     /*
1117      * close current alignment and load the saved project
1118      */
1119     af.closeMenuItem_actionPerformed(true);
1120     af = null;
1121     af = new FileLoader().LoadFileWaitTillLoaded(filePath,
1122             DataSourceType.FILE);
1123     assertNotNull(af, "Failed to import new project");
1124
1125     /*
1126      * verify restored feature colour schemes and filters
1127      */
1128     fr = af.getFeatureRenderer();
1129     FeatureColourI fc = fr.getFeatureStyle("type1");
1130     assertTrue(fc.isSimpleColour());
1131     assertEquals(fc.getColour(), Color.red);
1132     fc = fr.getFeatureStyle("type2");
1133     assertTrue(fc.isColourByLabel());
1134     fc = fr.getFeatureStyle("type3");
1135     assertTrue(fc.isGraduatedColour());
1136     assertNull(fc.getAttributeName());
1137     assertTrue(fc.isAboveThreshold());
1138     assertEquals(fc.getThreshold(), 2f);
1139     fc = fr.getFeatureStyle("type4");
1140     assertTrue(fc.isColourByLabel());
1141     assertTrue(fc.isColourByAttribute());
1142     assertEquals(fc.getAttributeName(), new String[] { "AF" });
1143     fc = fr.getFeatureStyle("type5");
1144     assertTrue(fc.isGraduatedColour());
1145     assertTrue(fc.isColourByAttribute());
1146     assertEquals(fc.getAttributeName(), new String[] { "CSQ", "PolyPhen" });
1147     assertTrue(fc.isBelowThreshold());
1148     assertEquals(fc.getThreshold(), 3f);
1149
1150     assertEquals(fr.getFeatureFilter("type1").toStableString(),
1151             "Label Contains x");
1152     assertEquals(fr.getFeatureFilter("type2").toStableString(),
1153             "(Score LE 2.4) AND (Score GT 1.1)");
1154     assertEquals(fr.getFeatureFilter("type3").toStableString(),
1155             "(AF Contains X) OR (CSQ:PolyPhen NE 0)");
1156   }
1157
1158   private void addFeature(SequenceI seq, String featureType, int score)
1159   {
1160     SequenceFeature sf = new SequenceFeature(featureType, "desc", 1, 2,
1161             score, "grp");
1162     sf.setValue("AF", score);
1163     sf.setValue("CSQ", new HashMap<String, String>()
1164     {
1165       {
1166         put("PolyPhen", Integer.toString(score));
1167       }
1168     });
1169     seq.addSequenceFeature(sf);
1170   }
1171
1172   /**
1173    * Adds two features of the given type to the given sequence, also setting the
1174    * score as the value of attribute "AF" and sub-attribute "CSQ:PolyPhen"
1175    * 
1176    * @param seq
1177    * @param featureType
1178    * @param score
1179    */
1180   private void addFeatures(SequenceI seq, String featureType, int score)
1181   {
1182     addFeature(seq, featureType, score++);
1183     addFeature(seq, featureType, score);
1184   }
1185
1186   /**
1187    * pre 2.11 - jalview 2.10 erroneously created new dataset entries for each
1188    * view (JAL-3171) this test ensures we can import and merge those views
1189    */
1190   @Test(groups = { "Functional" })
1191   public void testMergeDatasetsforViews() throws IOException
1192   {
1193     // simple project - two views on one alignment
1194     AlignFrame af = new FileLoader(false).LoadFileWaitTillLoaded(
1195             "examples/testdata/projects/twoViews.jvp", DataSourceType.FILE);
1196     assertNotNull(af);
1197     assertTrue(af.getAlignPanels().size() > 1);
1198     verifyDs(af);
1199   }
1200
1201   /**
1202    * pre 2.11 - jalview 2.10 erroneously created new dataset entries for each
1203    * view (JAL-3171) this test ensures we can import and merge those views This
1204    * is a more complex project
1205    */
1206   @Test(groups = { "Functional" })
1207   public void testMergeDatasetsforManyViews() throws IOException
1208   {
1209     Desktop.instance.closeAll_actionPerformed(null);
1210
1211     // complex project - one dataset, several views on several alignments
1212     AlignFrame af = new FileLoader(false).LoadFileWaitTillLoaded(
1213             "examples/testdata/projects/manyViews.jvp",
1214             DataSourceType.FILE);
1215     assertNotNull(af);
1216
1217     AlignmentI ds = null;
1218     for (AlignFrame alignFrame : Desktop.getAlignFrames())
1219     {
1220       if (ds == null)
1221       {
1222         ds = verifyDs(alignFrame);
1223       }
1224       else
1225       {
1226         // check that this frame's dataset matches the last
1227         assertTrue(ds == verifyDs(alignFrame));
1228       }
1229     }
1230   }
1231
1232   private AlignmentI verifyDs(AlignFrame af)
1233   {
1234     AlignmentI ds = null;
1235     for (AlignmentViewPanel ap : af.getAlignPanels())
1236     {
1237       if (ds == null)
1238       {
1239         ds = ap.getAlignment().getDataset();
1240       }
1241       else
1242       {
1243         assertTrue(ap.getAlignment().getDataset() == ds,
1244                 "Dataset was not the same for imported 2.10.5 project with several alignment views");
1245       }
1246     }
1247     return ds;
1248   }
1249
1250   @Test(groups = "Functional")
1251   public void testPcaViewAssociation() throws IOException
1252   {
1253     Desktop.instance.closeAll_actionPerformed(null);
1254     final String PCAVIEWNAME = "With PCA";
1255     // create a new tempfile
1256     File tempfile = File.createTempFile("jvPCAviewAssoc", "jvp");
1257
1258     {
1259       String exampleFile = "examples/uniref50.fa";
1260       AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(exampleFile,
1261               DataSourceType.FILE);
1262       assertNotNull(af, "Didn't read in the example file correctly.");
1263       AlignmentPanel origView = (AlignmentPanel) af.getAlignPanels().get(0);
1264       AlignmentPanel newview = af.newView(PCAVIEWNAME, true);
1265       // create another for good measure
1266       af.newView("Not the PCA View", true);
1267       PCAPanel pcaPanel = new PCAPanel(origView, "BLOSUM62",
1268               new SimilarityParams(true, true, true, false));
1269       // we're in the test exec thread, so we can just run synchronously here
1270       pcaPanel.run();
1271
1272       // now switch the linked view
1273       pcaPanel.selectAssociatedView(newview);
1274
1275       assertTrue(pcaPanel.getAlignViewport() == newview.getAlignViewport(),
1276               "PCA should be associated with 'With PCA' view: test is broken");
1277
1278       // now save and reload project
1279       Jalview2XML jv2xml = new jalview.project.Jalview2XML(false);
1280       tempfile.delete();
1281       jv2xml.saveState(tempfile);
1282       assertTrue(jv2xml.errorMessage == null,
1283               "Failed to save dummy project with PCA: test broken");
1284     }
1285
1286     // load again.
1287     Desktop.instance.closeAll_actionPerformed(null);
1288     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
1289             tempfile.getCanonicalPath(), DataSourceType.FILE);
1290     JInternalFrame[] frames = Desktop.instance.getAllFrames();
1291     // PCA and the tabbed alignment view should be the only two windows on the
1292     // desktop
1293     assertEquals(frames.length, 2,
1294             "PCA and the tabbed alignment view should be the only two windows on the desktop");
1295     PCAPanel pcaPanel = (PCAPanel) frames[frames[0] == af ? 1 : 0];
1296
1297     AlignmentViewPanel restoredNewView = null;
1298     for (AlignmentViewPanel alignpanel : Desktop.getAlignmentPanels(null))
1299     {
1300       if (alignpanel.getAlignViewport() == pcaPanel.getAlignViewport())
1301       {
1302         restoredNewView = alignpanel;
1303       }
1304     }
1305     assertEquals(restoredNewView.getViewName(), PCAVIEWNAME);
1306     assertTrue(
1307             restoredNewView.getAlignViewport() == pcaPanel
1308                     .getAlignViewport(),
1309             "Didn't restore correct view association for the PCA view");
1310   }
1311
1312   /**
1313    * Test save and reload of DBRefEntry including GeneLocus in project
1314    * 
1315    * @throws Exception
1316    */
1317   @Test(groups = { "Functional" })
1318   public void testStoreAndRecoverGeneLocus() throws Exception
1319   {
1320     Desktop.instance.closeAll_actionPerformed(null);
1321     String seqData = ">P30419\nACDE\n>X1235\nGCCTGTGACGAA";
1322     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
1323             DataSourceType.PASTE);
1324     assertNotNull(af, "Didn't read in the example file correctly.");
1325
1326     AlignmentViewPanel ap = Desktop.getAlignmentPanels(null)[0];
1327     SequenceI pep = ap.getAlignment().getSequenceAt(0);
1328     SequenceI cds = ap.getAlignment().getSequenceAt(1);
1329
1330     /*
1331      * give 'protein' a dbref to self, a dbref with map to CDS,
1332      * and a dbref with map to gene 'locus'
1333      */
1334     DBRefEntry dbref1 = new DBRefEntry("Uniprot", "1", "P30419", null);
1335     pep.addDBRef(dbref1);
1336     Mapping cdsmap = new Mapping(cds,
1337             new MapList(new int[]
1338             { 1, 4 }, new int[] { 1, 12 }, 1, 3));
1339     DBRefEntry dbref2 = new DBRefEntry("EMBLCDS", "2", "X1235", cdsmap);
1340     pep.addDBRef(dbref2);
1341     Mapping locusmap = new Mapping(null,
1342             new MapList(new int[]
1343             { 1, 4 }, new int[] { 2674123, 2674135 }, 1, 3));
1344     DBRefEntry dbref3 = new GeneLocus("human", "GRCh38", "5", locusmap);
1345     pep.addDBRef(dbref3);
1346
1347     File tfile = File.createTempFile("testStoreAndRecoverGeneLocus",
1348             ".jvp");
1349     try
1350     {
1351       new Jalview2XML(false).saveState(tfile);
1352     } catch (Throwable e)
1353     {
1354       Assert.fail("Didn't save the state", e);
1355     }
1356     Desktop.instance.closeAll_actionPerformed(null);
1357
1358     new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
1359             DataSourceType.FILE);
1360     AlignmentViewPanel rap = Desktop.getAlignmentPanels(null)[0];
1361     SequenceI rpep = rap.getAlignment().getSequenceAt(0);
1362     DBModList<DBRefEntry> dbrefs = rpep.getDBRefs();
1363     assertEquals(rpep.getName(), "P30419");
1364     assertEquals(dbrefs.size(), 3);
1365     DBRefEntry dbRef = dbrefs.get(0);
1366     assertFalse(dbRef instanceof GeneLocus);
1367     assertNull(dbRef.getMap());
1368     assertEquals(dbRef, dbref1);
1369
1370     /*
1371      * restored dbrefs with mapping have a different 'map to'
1372      * sequence but otherwise match the original dbrefs
1373      */
1374     dbRef = dbrefs.get(1);
1375     assertFalse(dbRef instanceof GeneLocus);
1376     assertTrue(dbRef.equalRef(dbref2));
1377     assertNotNull(dbRef.getMap());
1378     SequenceI rcds = rap.getAlignment().getSequenceAt(1);
1379     assertSame(dbRef.getMap().getTo(), rcds);
1380     // compare MapList but not map.to
1381     assertEquals(dbRef.getMap().getMap(), dbref2.getMap().getMap());
1382
1383     /*
1384      * GeneLocus map.to is null so can compare Mapping objects
1385      */
1386     dbRef = dbrefs.get(2);
1387     assertTrue(dbRef instanceof GeneLocus);
1388     assertEquals(dbRef, dbref3);
1389   }
1390
1391   /**
1392    * test store and recovery of Overview windows
1393    * 
1394    * @throws Exception
1395    */
1396   @Test(groups = { "Functional" }, enabled = true)
1397   public void testStoreAndRecoverOverview() throws Exception
1398   {
1399     Desktop.instance.closeAll_actionPerformed(null);
1400
1401     Cache.setProperty("SHOW_OVERVIEW", "false");
1402     Cache.setProperty(Preferences.USE_LEGACY_GAP, "false");
1403     Cache.setColourProperty(Preferences.GAP_COLOUR, Color.green);
1404     Cache.setColourProperty(Preferences.HIDDEN_COLOUR, Color.yellow);
1405     Cache.setProperty(Preferences.SHOW_OV_HIDDEN_AT_START, "true");
1406
1407     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
1408             "examples/uniref50.fa", DataSourceType.FILE);
1409
1410     /*
1411      * open and resize / reposition overview 
1412      */
1413     af.overviewMenuItem_actionPerformed(null);
1414     OverviewPanel ov1 = af.alignPanel.getOverviewPanel();
1415     assertNotNull(ov1);
1416     ov1.setFrameBounds(20, 30, 200, 400);
1417     assertEquals(ov1.getTitle(), "Overview examples/uniref50.fa");
1418     assertTrue(ov1.isShowHiddenRegions());
1419
1420     /*
1421      * open a New View and its Overview and reposition it
1422      */
1423     af.newView_actionPerformed(null);
1424     af.overviewMenuItem_actionPerformed(null);
1425     OverviewPanel ov2 = af.alignPanel.getOverviewPanel();
1426     assertNotNull(ov2);
1427     assertNotSame(ov1, ov2);
1428     ov2.setFrameBounds(25, 35, 205, 405);
1429     assertEquals(ov1.getTitle(), "Overview examples/uniref50.fa Original");
1430     assertEquals(ov2.getTitle(), "Overview examples/uniref50.fa View 1");
1431
1432     File tfile = File.createTempFile("testStoreAndRecoverOverview", ".jvp");
1433     new Jalview2XML(false).saveState(tfile);
1434     Desktop.instance.closeAll_actionPerformed(null);
1435
1436     /*
1437      * change preferences (should _not_ affect reloaded Overviews)
1438      */
1439     Cache.setProperty("SHOW_OVERVIEW", "true");
1440     Cache.setProperty(Preferences.USE_LEGACY_GAP, "true");
1441     Cache.setColourProperty(Preferences.GAP_COLOUR, Color.blue);
1442     Cache.setColourProperty(Preferences.HIDDEN_COLOUR, Color.orange);
1443     Cache.setProperty(Preferences.SHOW_OV_HIDDEN_AT_START, "false");
1444
1445     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
1446             DataSourceType.FILE);
1447
1448     /*
1449      * workaround: explicitly select View 1 (not in focus after restore)
1450      */
1451     af.tabSelectionChanged(1);
1452
1453     /*
1454      * verify restored overview for View 1
1455      */
1456     ov2 = af.alignPanel.getOverviewPanel();
1457     assertEquals(ov2.getCanvas().getGapColour(), Color.green);
1458     // 'non-legacy' colouring uses white for non-gapped residues
1459     assertEquals(ov2.getCanvas().getResidueColour(), Color.white);
1460     assertEquals(ov2.getCanvas().getHiddenColour(), Color.yellow);
1461     assertEquals(ov2.getTitle(), "Overview examples/uniref50.fa View 1");
1462     assertEquals(ov2.getFrameBounds(), new Rectangle(25, 35, 205, 405));
1463     assertTrue(ov2.isShowHiddenRegions());
1464
1465     /*
1466      * verify restored overview for Original view
1467      */
1468     af.tabSelectionChanged(0);
1469     ov1 = af.alignPanel.getOverviewPanel();
1470     assertEquals(ov1.getCanvas().getGapColour(), Color.green);
1471     // 'non-legacy' colouring uses white for non-gapped residues
1472     assertEquals(ov1.getCanvas().getResidueColour(), Color.white);
1473     assertEquals(ov1.getCanvas().getHiddenColour(), Color.yellow);
1474     assertEquals(ov1.getTitle(), "Overview examples/uniref50.fa Original");
1475     assertEquals(ov1.getFrameBounds(), new Rectangle(20, 30, 200, 400));
1476     assertTrue(ov1.isShowHiddenRegions());
1477   }
1478
1479   /**
1480    * Test that a view with no Overview is restored with no Overview, even if
1481    * 'Open Overview' is selected in Preferences
1482    * 
1483    * @throws Exception
1484    */
1485   @Test(groups = { "Functional" }, enabled = true)
1486   public void testStoreAndRecoverNoOverview() throws Exception
1487   {
1488     Cache.setProperty("SHOW_OVERVIEW", "false");
1489     Desktop.instance.closeAll_actionPerformed(null);
1490     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
1491             ">seq1\nMATRSQFLVNF\n", DataSourceType.PASTE);
1492
1493     File tfile = File.createTempFile("testStoreAndRecoverOverview", ".jvp");
1494     new Jalview2XML(false).saveState(tfile);
1495     Desktop.instance.closeAll_actionPerformed(null);
1496
1497     Cache.setProperty("SHOW_OVERVIEW", "true");
1498     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
1499             DataSourceType.FILE);
1500
1501     assertNull(af.alignPanel.getOverviewPanel());
1502   }
1503
1504   /**
1505    * Test that a view from an older version of Jalview is restored with Overview
1506    * automatically shown when the preference is set
1507    * 
1508    * @throws Exception
1509    */
1510   @Test(groups = { "Functional" }, enabled = true)
1511   public void testAutoShowOverviewForLegacyProjects() throws Exception
1512   {
1513     Desktop.instance.closeAll_actionPerformed(null);
1514     Cache.setProperty("SHOW_OVERVIEW", "true");
1515     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
1516             "examples/exampleFile.jvp", DataSourceType.FILE);
1517
1518     Cache.setProperty("SHOW_OVERVIEW", "false");
1519     assertNotNull(af.alignPanel.getOverviewPanel());
1520   }
1521
1522   /**
1523    * Test that loading example.jvp, doing some stuff, then hitting reload
1524    * doesn't leave the modified window still open
1525    * 
1526    * See JAL-4127 - interactively performing the same actions and reloading
1527    * works fine, but programmatically they do not
1528    * 
1529    * @throws Exception
1530    */
1531   @Test(groups = { "Functional" }, enabled = false)
1532   public void testReloadActuallyReloads() throws Exception
1533   {
1534     Desktop.instance.closeAll_actionPerformed(null);
1535     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
1536             "examples/exampleFile.jvp", DataSourceType.FILE);
1537     af.getViewport().getColumnSelection().addElement(3);
1538     af.hideSelColumns_actionPerformed(null);
1539     af.newView("new", true);
1540     af.reload_actionPerformed(null);
1541     Thread.sleep(30);
1542     // af exists still but isn't shown
1543     assertTrue(af.isClosed());
1544   }
1545
1546   @Test(groups = { "Functional" })
1547   public void testPAEsaveRestore() throws Exception
1548   {
1549     Desktop.instance.closeAll_actionPerformed(null);
1550     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
1551             ">seq1\nMATRSQFLVNF\n", DataSourceType.PASTE);
1552     AlignmentI al = af.getViewport().getAlignment();
1553     // PAE matrices are added as reference annotation to the dataset sequence
1554     // at least for now.
1555     SequenceI sq = al.getSequenceAt(0).getDatasetSequence();
1556     int i = sq.getLength();
1557     float[][] paevals = new float[i][i];
1558     for (i = i - 1; i >= 0; i--)
1559     {
1560       for (int j = 0; j <= i; j++)
1561       {
1562         paevals[i][j] = ((i - j < 2)
1563                 || ((i > 1 && i < 5) && (j > 1 && i < 5))) ? 1 : 0f;
1564         paevals[j][i] = paevals[i][j];
1565       }
1566     }
1567     PAEContactMatrix dummyMat = new PAEContactMatrix(sq, paevals);
1568     String content = ContactMatrix.contactToFloatString(dummyMat);
1569     Assert.assertTrue(content.contains("\t1.")); // at least one element must be
1570                                                  // 1
1571     float[][] vals = ContactMatrix.fromFloatStringToContacts(content,
1572             sq.getLength(), sq.getLength());
1573     assertEquals(vals[3][4], paevals[3][4]);
1574     dummyMat.setGroupSet(GroupSet.makeGroups(dummyMat, 0.5f, false));
1575     Assert.assertNotSame(dummyMat.getNewick(), "");
1576     AlignmentAnnotation paeCm = sq.addContactList(dummyMat);
1577     al.addAnnotation(paeCm);
1578     // verify store/restore of group bitsets
1579     for (BitSet gp : dummyMat.getGroups())
1580     {
1581       StringBuilder sb = new StringBuilder();
1582       for (long val : gp.toLongArray())
1583       {
1584         if (sb.length() > 0)
1585         {
1586           sb.append(",");
1587         }
1588         sb.append(val);
1589       }
1590       String[] longvals = sb.toString().split(",");
1591       long[] newlongvals = new long[longvals.length];
1592       for (int lv = 0; lv < longvals.length; lv++)
1593       {
1594         try
1595         {
1596           newlongvals[lv] = Long.valueOf(longvals[lv]);
1597         } catch (Exception x)
1598         {
1599           Assert.fail("failed to deserialise bitset element ");
1600         }
1601       }
1602       BitSet newGp = BitSet.valueOf(newlongvals);
1603       assertTrue(gp.equals(newGp));
1604     }
1605
1606     File tfile = File.createTempFile("testStoreAndRecoverPAEmatrix",
1607             ".jvp");
1608     new Jalview2XML(false).saveState(tfile);
1609     Desktop.instance.closeAll_actionPerformed(null);
1610
1611     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
1612             DataSourceType.FILE);
1613     AlignmentI newAl = af.getViewport().getAlignment();
1614     SequenceI newSeq = newAl.getSequenceAt(0).getDatasetSequence();
1615     // check annotation of the expected type exists
1616     Assert.assertEquals(newSeq.getAnnotation().length, 1);
1617     Assert.assertEquals(newSeq.getAnnotation()[0].graph, paeCm.graph);
1618
1619     // check a contact matrix was recovered
1620     Assert.assertEquals(newSeq.getContactMaps().size(), 1);
1621     // and can be found for the annotation on the sequence
1622     ContactMatrixI restoredMat = newSeq
1623             .getContactMatrixFor(newSeq.getAnnotation()[0]);
1624     Assert.assertNotNull(restoredMat);
1625     MapList oldMap = ((MappableContactMatrixI) dummyMat).getMapFor(sq);
1626     MapList newMap = ((MappableContactMatrixI) restoredMat)
1627             .getMapFor(newSeq);
1628     Assert.assertEquals(oldMap.getFromRanges(), newMap.getFromRanges());
1629     Assert.assertEquals(oldMap.getToRanges(), newMap.getToRanges());
1630     Assert.assertEquals(oldMap.getFromRatio(), newMap.getFromRatio());
1631     Assert.assertEquals(oldMap.getToRatio(), newMap.getToRatio());
1632
1633     for (i = sq.getLength() - 1; i >= 0; i--)
1634     {
1635       ContactListI oldCM = dummyMat.getContactList(i),
1636               newCM = restoredMat.getContactList(i);
1637       for (int j = oldCM.getContactHeight(); j >= 0; j--)
1638       {
1639         double old_j = oldCM.getContactAt(j);
1640         double new_j = newCM.getContactAt(j);
1641         Assert.assertEquals(old_j, new_j);
1642       }
1643     }
1644     Assert.assertEquals(restoredMat.hasGroups(), dummyMat.hasGroups());
1645     Assert.assertEquals(restoredMat.getGroups(), dummyMat.getGroups());
1646     Assert.assertEquals(restoredMat.hasTree(), dummyMat.hasTree());
1647     Assert.assertEquals(restoredMat.getNewick(), dummyMat.getNewick());
1648
1649   }
1650
1651 }