57cade53b80ddc32fd9fe7b34e51ee93475a2579
[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.assertNull;
27 import static org.testng.Assert.assertSame;
28 import static org.testng.Assert.assertTrue;
29
30 import jalview.analysis.scoremodels.SimilarityParams;
31 import jalview.api.AlignViewportI;
32 import jalview.api.AlignmentViewPanel;
33 import jalview.api.FeatureColourI;
34 import jalview.api.ViewStyleI;
35 import jalview.bin.Cache;
36 import jalview.datamodel.AlignmentAnnotation;
37 import jalview.datamodel.AlignmentI;
38 import jalview.datamodel.Annotation;
39 import jalview.datamodel.HiddenSequences;
40 import jalview.datamodel.PDBEntry;
41 import jalview.datamodel.PDBEntry.Type;
42 import jalview.datamodel.SequenceCollectionI;
43 import jalview.datamodel.SequenceFeature;
44 import jalview.datamodel.SequenceGroup;
45 import jalview.datamodel.SequenceI;
46 import jalview.datamodel.features.FeatureMatcher;
47 import jalview.datamodel.features.FeatureMatcherSet;
48 import jalview.datamodel.features.FeatureMatcherSetI;
49 import jalview.gui.AlignFrame;
50 import jalview.gui.AlignViewport;
51 import jalview.gui.AlignmentPanel;
52 import jalview.gui.Desktop;
53 import jalview.gui.FeatureRenderer;
54 import jalview.gui.JvOptionPane;
55 import jalview.gui.PCAPanel;
56 import jalview.gui.PopupMenu;
57 import jalview.gui.SliderPanel;
58 import jalview.io.DataSourceType;
59 import jalview.io.FileFormat;
60 import jalview.io.FileLoader;
61 import jalview.io.Jalview2xmlBase;
62 import jalview.renderer.ResidueShaderI;
63 import jalview.schemes.AnnotationColourGradient;
64 import jalview.schemes.BuriedColourScheme;
65 import jalview.schemes.ColourSchemeI;
66 import jalview.schemes.ColourSchemeProperty;
67 import jalview.schemes.FeatureColour;
68 import jalview.schemes.JalviewColourScheme;
69 import jalview.schemes.RNAHelicesColour;
70 import jalview.schemes.StrandColourScheme;
71 import jalview.schemes.TCoffeeColourScheme;
72 import jalview.structure.StructureImportSettings;
73 import jalview.util.matcher.Condition;
74 import jalview.viewmodel.AlignmentViewport;
75
76 import java.awt.Color;
77 import java.io.File;
78 import java.io.IOException;
79 import java.util.ArrayList;
80 import java.util.HashMap;
81 import java.util.List;
82 import java.util.Map;
83
84 import javax.swing.JInternalFrame;
85
86 import org.testng.Assert;
87 import org.testng.AssertJUnit;
88 import org.testng.annotations.BeforeClass;
89 import org.testng.annotations.Test;
90
91 @Test(singleThreaded = true)
92 public class Jalview2xmlTests extends Jalview2xmlBase
93 {
94
95   @Override
96   @BeforeClass(alwaysRun = true)
97   public void setUpJvOptionPane()
98   {
99     JvOptionPane.setInteractiveMode(false);
100     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
101   }
102
103   @Test(groups = { "Functional" })
104   public void testRestoreRNAStructure() throws Exception
105   {
106     String inFile = "examples/RF00031_folded.stk";
107     String tfile = File.createTempFile("JalviewTest", ".jvp")
108             .getAbsolutePath();
109     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
110             DataSourceType.FILE);
111     assertNotNull(af, "Didn't read input file " + inFile);
112     int olddsann = countDsAnn(af.getViewport());
113     assertTrue(olddsann > 0, "Didn't find any dataset annotations");
114     af.changeColour_actionPerformed(
115             JalviewColourScheme.RNAHelices.toString());
116     assertTrue(
117             af.getViewport()
118                     .getGlobalColourScheme() instanceof RNAHelicesColour,
119             "Couldn't apply RNA helices colourscheme");
120     assertTrue(af.saveAlignment(tfile, FileFormat.Jalview),
121             "Failed to store as a project.");
122     af.closeMenuItem_actionPerformed(true);
123     af = null;
124     af = new FileLoader().LoadFileWaitTillLoaded(tfile,
125             DataSourceType.FILE);
126     assertNotNull(af, "Failed to import new project");
127     int newdsann = countDsAnn(af.getViewport());
128     assertEquals(olddsann, newdsann,
129             "Differing numbers of dataset sequence annotation\nOriginally "
130                     + olddsann + " and now " + newdsann);
131     System.out.println(
132             "Read in same number of annotations as originally present ("
133                     + olddsann + ")");
134     assertTrue(
135
136             af.getViewport()
137                     .getGlobalColourScheme() instanceof RNAHelicesColour,
138             "RNA helices colourscheme was not applied on import.");
139   }
140
141   @Test(groups = { "Functional" })
142   public void testRestoreTCoffeeColouring() throws Exception
143   {
144     String inFile = "examples/uniref50.fa",
145             inAnnot = "examples/uniref50.score_ascii";
146     String tfile = File.createTempFile("JalviewTest", ".jvp")
147             .getAbsolutePath();
148     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
149             DataSourceType.FILE);
150     assertNotNull(af, "Didn't read input file " + inFile);
151     af.loadJalviewDataFile(inAnnot, DataSourceType.FILE, null, null);
152     AlignViewport viewport = af.getViewport();
153     assertSame(viewport.getGlobalColourScheme().getClass(),
154             TCoffeeColourScheme.class, "Didn't set T-coffee colourscheme");
155     assertNotNull(
156             ColourSchemeProperty.getColourScheme(viewport,
157                     viewport.getAlignment(),
158                     viewport.getGlobalColourScheme()
159                             .getSchemeName()),
160             "Recognise T-Coffee score from string");
161
162     assertTrue(af.saveAlignment(tfile, FileFormat.Jalview),
163             "Failed to store as a project.");
164     af.closeMenuItem_actionPerformed(true);
165     af = null;
166     af = new FileLoader().LoadFileWaitTillLoaded(tfile,
167             DataSourceType.FILE);
168     assertNotNull(af, "Failed to import new project");
169     assertSame(af.getViewport().getGlobalColourScheme().getClass(),
170             TCoffeeColourScheme.class,
171             "Didn't set T-coffee colourscheme for imported project.");
172     System.out.println(
173             "T-Coffee score shading successfully recovered from project.");
174   }
175
176   @Test(groups = { "Functional" })
177   public void testRestoreColourByAnnotion() throws Exception
178   {
179     String inFile = "examples/uniref50.fa",
180             inAnnot = "examples/testdata/uniref50_iupred.jva";
181     String tfile = File.createTempFile("JalviewTest", ".jvp")
182             .getAbsolutePath();
183     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
184             DataSourceType.FILE);
185     assertNotNull(af, "Didn't read input file " + inFile);
186     af.loadJalviewDataFile(inAnnot, DataSourceType.FILE, null, null);
187     AlignmentAnnotation[] aa = af.getViewport().getAlignment()
188             .getSequenceAt(0).getAnnotation("IUPredWS (Short)");
189     assertTrue(
190
191             aa != null && aa.length > 0,
192             "Didn't find any IUPred annotation to use to shade alignment.");
193     AnnotationColourGradient cs = new AnnotationColourGradient(aa[0], null,
194             AnnotationColourGradient.ABOVE_THRESHOLD);
195     AnnotationColourGradient gcs = new AnnotationColourGradient(aa[0], null,
196             AnnotationColourGradient.BELOW_THRESHOLD);
197     cs.setSeqAssociated(true);
198     gcs.setSeqAssociated(true);
199     af.changeColour(cs);
200     SequenceGroup sg = new SequenceGroup();
201     sg.setStartRes(57);
202     sg.setEndRes(92);
203     sg.cs.setColourScheme(gcs);
204     af.getViewport().getAlignment().addGroup(sg);
205     sg.addSequence(af.getViewport().getAlignment().getSequenceAt(1), false);
206     sg.addSequence(af.getViewport().getAlignment().getSequenceAt(2), true);
207     af.alignPanel.alignmentChanged();
208     assertTrue(af.saveAlignment(tfile, FileFormat.Jalview),
209             "Failed to store as a project.");
210     af.closeMenuItem_actionPerformed(true);
211     af = null;
212     af = new FileLoader().LoadFileWaitTillLoaded(tfile,
213             DataSourceType.FILE);
214     assertNotNull(af, "Failed to import new project");
215
216     // check for group and alignment colourschemes
217
218     ColourSchemeI _rcs = af.getViewport().getGlobalColourScheme();
219     ColourSchemeI _rgcs = af.getViewport().getAlignment().getGroups().get(0)
220             .getColourScheme();
221     assertNotNull(_rcs, "Didn't recover global colourscheme");
222     assertTrue(_rcs instanceof AnnotationColourGradient,
223             "Didn't recover annotation colour global scheme");
224     AnnotationColourGradient __rcs = (AnnotationColourGradient) _rcs;
225     assertTrue(__rcs.isSeqAssociated(),
226             "Annotation colourscheme wasn't sequence associated");
227
228     boolean diffseqcols = false, diffgseqcols = false;
229     SequenceI[] sqs = af.getViewport().getAlignment().getSequencesArray();
230     for (int p = 0, pSize = af.getViewport().getAlignment()
231             .getWidth(); p < pSize && (!diffseqcols || !diffgseqcols); p++)
232     {
233       if (_rcs.findColour(sqs[0].getCharAt(p), p, sqs[0], null, 0f) != _rcs
234               .findColour(sqs[5].getCharAt(p), p, sqs[5], null, 0f))
235       {
236         diffseqcols = true;
237       }
238     }
239     assertTrue(diffseqcols, "Got Different sequence colours");
240     System.out.println(
241             "Per sequence colourscheme (Background) successfully applied and recovered.");
242
243     assertNotNull(_rgcs, "Didn't recover group colourscheme");
244     assertTrue(_rgcs instanceof AnnotationColourGradient,
245             "Didn't recover annotation colour group colourscheme");
246     __rcs = (AnnotationColourGradient) _rgcs;
247     assertTrue(__rcs.isSeqAssociated(),
248             "Group Annotation colourscheme wasn't sequence associated");
249
250     for (int p = 0, pSize = af.getViewport().getAlignment()
251             .getWidth(); p < pSize && (!diffseqcols || !diffgseqcols); p++)
252     {
253       if (_rgcs.findColour(sqs[1].getCharAt(p), p, sqs[1], null,
254               0f) != _rgcs.findColour(sqs[2].getCharAt(p), p, sqs[2], null,
255                       0f))
256       {
257         diffgseqcols = true;
258       }
259     }
260     assertTrue(diffgseqcols, "Got Different group sequence colours");
261     System.out.println(
262             "Per sequence (Group) colourscheme successfully applied and recovered.");
263   }
264
265   @Test(groups = { "Functional" })
266   public void gatherViewsHere() throws Exception
267   {
268     int origCount = Desktop.getAlignFrames() == null ? 0
269             : Desktop.getAlignFrames().length;
270     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
271             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
272     assertNotNull(af, "Didn't read in the example file correctly.");
273     assertTrue(Desktop.getAlignFrames().length == 1 + origCount,
274             "Didn't gather the views in the example file.");
275
276   }
277
278   /**
279    * Test for JAL-2223 - multiple mappings in View Mapping report
280    * 
281    * @throws Exception
282    */
283   @Test(groups = { "Functional" })
284   public void noDuplicatePdbMappingsMade() throws Exception
285   {
286     StructureImportSettings.setProcessSecondaryStructure(true);
287     StructureImportSettings.setVisibleChainAnnotation(true);
288     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
289             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
290     assertNotNull(af, "Didn't read in the example file correctly.");
291
292     // locate Jmol viewer
293     // count number of PDB mappings the structure selection manager holds -
294     String pdbFile = af.getCurrentView().getStructureSelectionManager()
295             .findFileForPDBId("1A70");
296     assertEquals(
297             af.getCurrentView().getStructureSelectionManager()
298                     .getMapping(pdbFile).length,
299             2, "Expected only two mappings for 1A70");
300
301   }
302
303   @Test(groups = { "Functional" })
304   public void viewRefPdbAnnotation() 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     AlignmentViewPanel sps = null;
312     for (AlignmentViewPanel ap : af.alignPanel.alignFrame.getAlignPanels())
313     {
314       if ("Spinach Feredoxin Structure".equals(ap.getViewName()))
315       {
316         sps = ap;
317         break;
318       }
319     }
320     assertNotNull(sps, "Couldn't find the structure view");
321     AlignmentAnnotation refan = null;
322     for (AlignmentAnnotation ra : sps.getAlignment()
323             .getAlignmentAnnotation())
324     {
325       if (ra.graph != 0)
326       {
327         refan = ra;
328         break;
329       }
330     }
331     assertNotNull(refan, "Annotation secondary structure not found.");
332     SequenceI sq = sps.getAlignment().findName("1A70|");
333     assertNotNull(sq, "Couldn't find 1a70 null chain");
334     // compare the manually added temperature factor annotation
335     // to the track automatically transferred from the pdb structure on load
336     assertNotNull(sq.getDatasetSequence().getAnnotation(),
337             "1a70 has no annotation");
338     for (AlignmentAnnotation ala : sq.getDatasetSequence().getAnnotation())
339     {
340       AlignmentAnnotation alaa;
341       sq.addAlignmentAnnotation(alaa = new AlignmentAnnotation(ala));
342       alaa.adjustForAlignment();
343       if (ala.graph == refan.graph)
344       {
345         for (int p = 0; p < ala.annotations.length; p++)
346         {
347           sq.findPosition(p);
348           try
349           {
350             assertTrue((alaa.annotations[p] == null
351                     && refan.annotations[p] == null)
352                     || alaa.annotations[p].value == refan.annotations[p].value,
353                     "Mismatch at alignment position " + p);
354           } catch (NullPointerException q)
355           {
356             Assert.fail("Mismatch of alignment annotations at position " + p
357                     + " Ref seq ann: " + refan.annotations[p]
358                     + " alignment " + alaa.annotations[p]);
359           }
360         }
361       }
362     }
363
364   }
365
366   @Test(groups = { "Functional" })
367   public void testCopyViewSettings() throws Exception
368   {
369     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
370             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
371     assertNotNull(af, "Didn't read in the example file correctly.");
372     AlignmentViewPanel sps = null, groups = null;
373     for (AlignmentViewPanel ap : af.alignPanel.alignFrame.getAlignPanels())
374     {
375       if ("Spinach Feredoxin Structure".equals(ap.getViewName()))
376       {
377         sps = ap;
378       }
379       if (ap.getViewName().contains("MAFFT"))
380       {
381         groups = ap;
382       }
383     }
384     assertNotNull(sps, "Couldn't find the structure view");
385     assertNotNull(groups, "Couldn't find the MAFFT view");
386
387     ViewStyleI structureStyle = sps.getAlignViewport().getViewStyle();
388     ViewStyleI groupStyle = groups.getAlignViewport().getViewStyle();
389     AssertJUnit.assertFalse(structureStyle.sameStyle(groupStyle));
390
391     groups.getAlignViewport().setViewStyle(structureStyle);
392     AssertJUnit.assertFalse(
393             groupStyle.sameStyle(groups.getAlignViewport().getViewStyle()));
394     Assert.assertTrue(structureStyle
395             .sameStyle(groups.getAlignViewport().getViewStyle()));
396
397   }
398
399   /**
400    * test store and recovery of expanded views
401    * 
402    * @throws Exception
403    */
404   @Test(groups = { "Functional" }, enabled = true)
405   public void testRestoreExpandedviews() throws Exception
406   {
407     Desktop.instance.closeAll_actionPerformed(null);
408
409     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
410             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
411     Assert.assertEquals(Desktop.getAlignFrames().length, 1);
412     String afid = af.getViewport().getSequenceSetId();
413
414     // check FileLoader returned a reference to the one alignFrame that is
415     // actually on the Desktop
416     assertSame(af, Desktop.getAlignFrameFor(af.getViewport()),
417             "Jalview2XML.loadAlignFrame() didn't return correct AlignFrame reference for multiple view window");
418
419     Desktop.explodeViews(af);
420
421     int oldviews = Desktop.getAlignFrames().length;
422     Assert.assertEquals(Desktop.getAlignFrames().length,
423             Desktop.getAlignmentPanels(afid).length);
424     File tfile = File.createTempFile("testRestoreExpandedviews", ".jvp");
425     try
426     {
427       new Jalview2XML(false).saveState(tfile);
428     } catch (Error e)
429     {
430       Assert.fail("Didn't save the expanded view state", e);
431     } catch (Exception e)
432     {
433       Assert.fail("Didn't save the expanded view state", e);
434     }
435     Desktop.instance.closeAll_actionPerformed(null);
436     if (Desktop.getAlignFrames() != null)
437     {
438       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
439     }
440     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
441             DataSourceType.FILE);
442     Assert.assertNotNull(af);
443     Assert.assertEquals(Desktop.getAlignFrames().length,
444             Desktop.getAlignmentPanels(
445                     af.getViewport().getSequenceSetId()).length);
446     Assert.assertEquals(
447             Desktop.getAlignmentPanels(
448                     af.getViewport().getSequenceSetId()).length,
449             oldviews);
450   }
451
452   /**
453    * Test save and reload of a project with a different representative sequence
454    * in each view.
455    * 
456    * @throws Exception
457    */
458   @Test(groups = { "Functional" })
459   public void testRestoreReferenceSeqSettings() throws Exception
460   {
461     Desktop.instance.closeAll_actionPerformed(null);
462     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
463             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
464     assertNotNull(af, "Didn't read in the example file correctly.");
465     String afid = af.getViewport().getSequenceSetId();
466
467     // remember reference sequence for each panel
468     Map<String, SequenceI> refseqs = new HashMap<>();
469
470     /*
471      * mark sequence 2, 3, 4.. in panels 1, 2, 3...
472      * as reference sequence for itself and the preceding sequence
473      */
474     int n = 1;
475     for (AlignmentViewPanel ap : Desktop.getAlignmentPanels(afid))
476     {
477       AlignViewportI av = ap.getAlignViewport();
478       AlignmentI alignment = ap.getAlignment();
479       int repIndex = n % alignment.getHeight();
480       SequenceI rep = alignment.getSequenceAt(repIndex);
481       refseqs.put(ap.getViewName(), rep);
482
483       // code from mark/unmark sequence as reference in jalview.gui.PopupMenu
484       // todo refactor this to an alignment view controller
485       av.setDisplayReferenceSeq(true);
486       av.setColourByReferenceSeq(true);
487       av.getAlignment().setSeqrep(rep);
488
489       n++;
490     }
491     File tfile = File.createTempFile("testRestoreReferenceSeqSettings",
492             ".jvp");
493     try
494     {
495       new Jalview2XML(false).saveState(tfile);
496     } catch (Throwable e)
497     {
498       Assert.fail("Didn't save the expanded view state", e);
499     }
500     Desktop.instance.closeAll_actionPerformed(null);
501     if (Desktop.getAlignFrames() != null)
502     {
503       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
504     }
505
506     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
507             DataSourceType.FILE);
508     afid = af.getViewport().getSequenceSetId();
509
510     for (AlignmentViewPanel ap : Desktop.getAlignmentPanels(afid))
511     {
512       // check representative
513       AlignmentI alignment = ap.getAlignment();
514       SequenceI rep = alignment.getSeqrep();
515       Assert.assertNotNull(rep,
516               "Couldn't restore sequence representative from project");
517       // can't use a strong equals here, because by definition, the sequence IDs
518       // will be different.
519       // could set vamsas session save/restore flag to preserve IDs across
520       // load/saves.
521       Assert.assertEquals(refseqs.get(ap.getViewName()).toString(),
522               rep.toString(),
523               "Representative wasn't the same when recovered.");
524       Assert.assertTrue(ap.getAlignViewport().isDisplayReferenceSeq(),
525               "Display reference sequence view setting not set.");
526       Assert.assertTrue(ap.getAlignViewport().isColourByReferenceSeq(),
527               "Colour By Reference Seq view setting not set.");
528     }
529   }
530
531   @Test(groups = { "Functional" })
532   public void testIsVersionStringLaterThan()
533   {
534     /*
535      * No version / development / test / autobuild is leniently assumed to be
536      * compatible
537      */
538     assertTrue(Jalview2XML.isVersionStringLaterThan(null, null));
539     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", null));
540     assertTrue(Jalview2XML.isVersionStringLaterThan(null, "2.8.3"));
541     assertTrue(Jalview2XML.isVersionStringLaterThan(null,
542             "Development Build"));
543     assertTrue(Jalview2XML.isVersionStringLaterThan(null,
544             "DEVELOPMENT BUILD"));
545     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3",
546             "Development Build"));
547     assertTrue(Jalview2XML.isVersionStringLaterThan(null, "Test"));
548     assertTrue(Jalview2XML.isVersionStringLaterThan(null, "TEST"));
549     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "Test"));
550     assertTrue(
551             Jalview2XML.isVersionStringLaterThan(null, "Automated Build"));
552     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3",
553             "Automated Build"));
554     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3",
555             "AUTOMATED BUILD"));
556
557     /*
558      * same version returns true i.e. compatible
559      */
560     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8", "2.8"));
561     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.3"));
562     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3b1", "2.8.3b1"));
563     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3B1", "2.8.3b1"));
564     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3b1", "2.8.3B1"));
565
566     /*
567      * later version returns true
568      */
569     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.4"));
570     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.9"));
571     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.9.2"));
572     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8", "2.8.3"));
573     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.3b1"));
574
575     /*
576      * earlier version returns false
577      */
578     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8"));
579     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.4", "2.8.3"));
580     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.3b1", "2.8.3"));
581     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.2b1"));
582     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.0b2", "2.8.0b1"));
583   }
584
585   /**
586    * Test save and reload of a project with a different sequence group (and
587    * representative sequence) in each view.
588    * 
589    * @throws Exception
590    */
591   @Test(groups = { "Functional" })
592   public void testRestoreGroupRepSeqs() throws Exception
593   {
594     Desktop.instance.closeAll_actionPerformed(null);
595     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
596             "examples/uniref50.fa", DataSourceType.FILE);
597     assertNotNull(af, "Didn't read in the example file correctly.");
598     String afid = af.getViewport().getSequenceSetId();
599     // make a second view of the alignment
600     af.newView_actionPerformed(null);
601
602     /*
603      * remember representative and hidden sequences marked 
604      * on each panel
605      */
606     Map<String, SequenceI> repSeqs = new HashMap<>();
607     Map<String, List<String>> hiddenSeqNames = new HashMap<>();
608
609     /*
610      * mark sequence 2, 3, 4.. in panels 1, 2, 3...
611      * as reference sequence for itself and the preceding sequence
612      */
613     int n = 1;
614     for (AlignmentViewPanel ap : Desktop.getAlignmentPanels(afid))
615     {
616       AlignViewportI av = ap.getAlignViewport();
617       AlignmentI alignment = ap.getAlignment();
618       int repIndex = n % alignment.getHeight();
619       // ensure at least one preceding sequence i.e. index >= 1
620       repIndex = Math.max(repIndex, 1);
621       SequenceI repSeq = alignment.getSequenceAt(repIndex);
622       repSeqs.put(ap.getViewName(), repSeq);
623       List<String> hiddenNames = new ArrayList<>();
624       hiddenSeqNames.put(ap.getViewName(), hiddenNames);
625
626       /*
627        * have rep sequence represent itself and the one before it
628        * this hides the group (except for the rep seq)
629        */
630       SequenceGroup sg = new SequenceGroup();
631       sg.addSequence(repSeq, false);
632       SequenceI precedingSeq = alignment.getSequenceAt(repIndex - 1);
633       sg.addSequence(precedingSeq, false);
634       sg.setSeqrep(repSeq);
635       assertTrue(sg.getSequences().contains(repSeq));
636       assertTrue(sg.getSequences().contains(precedingSeq));
637       av.setSelectionGroup(sg);
638       assertSame(repSeq, sg.getSeqrep());
639
640       /*
641        * represent group with sequence adds to a map of hidden rep sequences
642        * (it does not create a group on the alignment) 
643        */
644       ((AlignmentViewport) av).hideSequences(repSeq, true);
645       assertSame(repSeq, sg.getSeqrep());
646       assertTrue(sg.getSequences().contains(repSeq));
647       assertTrue(sg.getSequences().contains(precedingSeq));
648       assertTrue(alignment.getGroups().isEmpty(), "alignment has groups");
649       Map<SequenceI, SequenceCollectionI> hiddenRepSeqsMap = av
650               .getHiddenRepSequences();
651       assertNotNull(hiddenRepSeqsMap);
652       assertEquals(1, hiddenRepSeqsMap.size());
653       assertSame(sg, hiddenRepSeqsMap.get(repSeq));
654       assertTrue(alignment.getHiddenSequences().isHidden(precedingSeq));
655       assertFalse(alignment.getHiddenSequences().isHidden(repSeq));
656       hiddenNames.add(precedingSeq.getName());
657
658       n++;
659     }
660     File tfile = File.createTempFile("testRestoreGroupRepSeqs", ".jvp");
661     try
662     {
663       new Jalview2XML(false).saveState(tfile);
664     } catch (Throwable e)
665     {
666       Assert.fail("Didn't save the expanded view state", e);
667     }
668     Desktop.instance.closeAll_actionPerformed(null);
669     if (Desktop.getAlignFrames() != null)
670     {
671       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
672     }
673
674     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
675             DataSourceType.FILE);
676     afid = af.getViewport().getSequenceSetId();
677
678     for (AlignmentViewPanel ap : Desktop.getAlignmentPanels(afid))
679     {
680       String viewName = ap.getViewName();
681       AlignViewportI av = ap.getAlignViewport();
682       AlignmentI alignment = ap.getAlignment();
683       List<SequenceGroup> groups = alignment.getGroups();
684       assertNotNull(groups);
685       assertTrue(groups.isEmpty(), "Alignment has groups");
686       Map<SequenceI, SequenceCollectionI> hiddenRepSeqsMap = av
687               .getHiddenRepSequences();
688       assertNotNull(hiddenRepSeqsMap, "No hidden represented sequences");
689       assertEquals(1, hiddenRepSeqsMap.size());
690       assertEquals(repSeqs.get(viewName).getDisplayId(true),
691               hiddenRepSeqsMap.keySet().iterator().next()
692                       .getDisplayId(true));
693
694       /*
695        * verify hidden sequences in restored panel
696        */
697       List<String> hidden = hiddenSeqNames.get(ap.getViewName());
698       HiddenSequences hs = alignment.getHiddenSequences();
699       assertEquals(hidden.size(), hs.getSize(),
700               "wrong number of restored hidden sequences in "
701                       + ap.getViewName());
702     }
703   }
704
705   /**
706    * Test save and reload of PDBEntry in Jalview project
707    * 
708    * @throws Exception
709    */
710   @Test(groups = { "Functional" })
711   public void testRestorePDBEntry() throws Exception
712   {
713     Desktop.instance.closeAll_actionPerformed(null);
714     String exampleFile = "examples/3W5V.pdb";
715     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(exampleFile,
716             DataSourceType.FILE);
717     assertNotNull(af, "Didn't read in the example file correctly.");
718     String afid = af.getViewport().getSequenceSetId();
719
720     AlignmentPanel[] alignPanels = Desktop.getAlignmentPanels(afid);
721     System.out.println();
722     AlignmentViewPanel ap = alignPanels[0];
723     String tfileBase = new File(".").getAbsolutePath().replace(".", "");
724     String testFile = tfileBase + exampleFile;
725     AlignmentI alignment = ap.getAlignment();
726     System.out.println("blah");
727     SequenceI[] seqs = alignment.getSequencesArray();
728     Assert.assertNotNull(seqs[0]);
729     Assert.assertNotNull(seqs[1]);
730     Assert.assertNotNull(seqs[2]);
731     Assert.assertNotNull(seqs[3]);
732     Assert.assertNotNull(seqs[0].getDatasetSequence());
733     Assert.assertNotNull(seqs[1].getDatasetSequence());
734     Assert.assertNotNull(seqs[2].getDatasetSequence());
735     Assert.assertNotNull(seqs[3].getDatasetSequence());
736     PDBEntry[] pdbEntries = new PDBEntry[4];
737     pdbEntries[0] = new PDBEntry("3W5V", "A", Type.PDB, testFile);
738     pdbEntries[1] = new PDBEntry("3W5V", "B", Type.PDB, testFile);
739     pdbEntries[2] = new PDBEntry("3W5V", "C", Type.PDB, testFile);
740     pdbEntries[3] = new PDBEntry("3W5V", "D", Type.PDB, testFile);
741     Assert.assertEquals(
742             seqs[0].getDatasetSequence().getAllPDBEntries().get(0),
743             pdbEntries[0]);
744     Assert.assertEquals(
745             seqs[1].getDatasetSequence().getAllPDBEntries().get(0),
746             pdbEntries[1]);
747     Assert.assertEquals(
748             seqs[2].getDatasetSequence().getAllPDBEntries().get(0),
749             pdbEntries[2]);
750     Assert.assertEquals(
751             seqs[3].getDatasetSequence().getAllPDBEntries().get(0),
752             pdbEntries[3]);
753
754     File tfile = File.createTempFile("testRestorePDBEntry", ".jvp");
755     try
756     {
757       new Jalview2XML(false).saveState(tfile);
758     } catch (Throwable e)
759     {
760       Assert.fail("Didn't save the state", e);
761     }
762     Desktop.instance.closeAll_actionPerformed(null);
763     if (Desktop.getAlignFrames() != null)
764     {
765       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
766     }
767
768     AlignFrame restoredFrame = new FileLoader().LoadFileWaitTillLoaded(
769             tfile.getAbsolutePath(), DataSourceType.FILE);
770     String rfid = restoredFrame.getViewport().getSequenceSetId();
771     AlignmentPanel[] rAlignPanels = Desktop.getAlignmentPanels(rfid);
772     AlignmentViewPanel rap = rAlignPanels[0];
773     AlignmentI rAlignment = rap.getAlignment();
774     System.out.println("blah");
775     SequenceI[] rseqs = rAlignment.getSequencesArray();
776     Assert.assertNotNull(rseqs[0]);
777     Assert.assertNotNull(rseqs[1]);
778     Assert.assertNotNull(rseqs[2]);
779     Assert.assertNotNull(rseqs[3]);
780     Assert.assertNotNull(rseqs[0].getDatasetSequence());
781     Assert.assertNotNull(rseqs[1].getDatasetSequence());
782     Assert.assertNotNull(rseqs[2].getDatasetSequence());
783     Assert.assertNotNull(rseqs[3].getDatasetSequence());
784
785     // The Asserts below are expected to fail until the PDB chainCode is
786     // recoverable from a Jalview projects
787     for (int chain = 0; chain < 4; chain++)
788     {
789       PDBEntry recov = rseqs[chain].getDatasetSequence().getAllPDBEntries()
790               .get(0);
791       PDBEntry expected = pdbEntries[chain];
792       Assert.assertEquals(recov.getId(), expected.getId(),
793               "Mismatch PDB ID");
794       Assert.assertEquals(recov.getChainCode(), expected.getChainCode(),
795               "Mismatch PDB ID");
796       Assert.assertEquals(recov.getType(), expected.getType(),
797               "Mismatch PDBEntry 'Type'");
798       Assert.assertNotNull(recov.getFile(),
799               "Recovered PDBEntry should have a non-null file entry");
800     }
801   }
802
803   /**
804    * Configure an alignment and a sub-group each with distinct colour schemes,
805    * Conservation and PID thresholds, and confirm these are restored from the
806    * saved project.
807    * 
808    * @throws IOException
809    */
810   @Test(groups = { "Functional" })
811   public void testRestoreColourThresholds() throws IOException
812   {
813     Desktop.instance.closeAll_actionPerformed(null);
814     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
815             "examples/uniref50.fa", DataSourceType.FILE);
816
817     AlignViewport av = af.getViewport();
818     AlignmentI al = av.getAlignment();
819
820     /*
821      * Colour alignment by Buried Index, Above 10% PID, By Conservation 20%
822      */
823     av.setColourAppliesToAllGroups(false);
824     af.changeColour_actionPerformed(JalviewColourScheme.Buried.toString());
825     assertTrue(av.getGlobalColourScheme() instanceof BuriedColourScheme);
826     af.abovePIDThreshold_actionPerformed(true);
827     SliderPanel sp = SliderPanel.getSliderPanel();
828     assertFalse(sp.isForConservation());
829     sp.valueChanged(10);
830     af.conservationMenuItem_actionPerformed(true);
831     sp = SliderPanel.getSliderPanel();
832     assertTrue(sp.isForConservation());
833     sp.valueChanged(20);
834     ResidueShaderI rs = av.getResidueShading();
835     assertEquals(rs.getThreshold(), 10);
836     assertTrue(rs.conservationApplied());
837     assertEquals(rs.getConservationInc(), 20);
838
839     /*
840      * create a group with Strand colouring, 30% Conservation
841      * and 40% PID threshold
842      */
843     SequenceGroup sg = new SequenceGroup();
844     sg.addSequence(al.getSequenceAt(0), false);
845     sg.setStartRes(15);
846     sg.setEndRes(25);
847     av.setSelectionGroup(sg);
848     PopupMenu popupMenu = new PopupMenu(af.alignPanel, null, null);
849     popupMenu.changeColour_actionPerformed(
850             JalviewColourScheme.Strand.toString());
851     assertTrue(sg.getColourScheme() instanceof StrandColourScheme);
852     assertEquals(al.getGroups().size(), 1);
853     assertSame(al.getGroups().get(0), sg);
854     popupMenu.conservationMenuItem_actionPerformed(true);
855     sp = SliderPanel.getSliderPanel();
856     assertTrue(sp.isForConservation());
857     sp.valueChanged(30);
858     popupMenu.abovePIDColour_actionPerformed(true);
859     sp = SliderPanel.getSliderPanel();
860     assertFalse(sp.isForConservation());
861     sp.valueChanged(40);
862     assertTrue(sg.getGroupColourScheme().conservationApplied());
863     assertEquals(sg.getGroupColourScheme().getConservationInc(), 30);
864     assertEquals(sg.getGroupColourScheme().getThreshold(), 40);
865
866     /*
867      * save project, close windows, reload project, verify
868      */
869     File tfile = File.createTempFile("testRestoreColourThresholds",
870             ".jvp");
871     tfile.deleteOnExit();
872     new Jalview2XML(false).saveState(tfile);
873     Desktop.instance.closeAll_actionPerformed(null);
874     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
875             DataSourceType.FILE);
876     Assert.assertNotNull(af, "Failed to reload project");
877
878     /*
879      * verify alignment (background) colouring
880      */
881     rs = af.getViewport().getResidueShading();
882     assertTrue(rs.getColourScheme() instanceof BuriedColourScheme);
883     assertEquals(rs.getThreshold(), 10);
884     assertTrue(rs.conservationApplied());
885     assertEquals(rs.getConservationInc(), 20);
886
887     /*
888      * verify group colouring
889      */
890     assertEquals(1, af.getViewport().getAlignment().getGroups().size(), 1);
891     rs = af.getViewport().getAlignment().getGroups().get(0)
892             .getGroupColourScheme();
893     assertTrue(rs.getColourScheme() instanceof StrandColourScheme);
894     assertEquals(rs.getThreshold(), 40);
895     assertTrue(rs.conservationApplied());
896     assertEquals(rs.getConservationInc(), 30);
897   }
898
899   /**
900    * Test save and reload of feature colour schemes and filter settings
901    * 
902    * @throws IOException
903    */
904   @Test(groups = { "Functional" })
905   public void testRestoreFeatureColoursAndFilters() throws IOException
906   {
907     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
908             ">Seq1\nACDEFGHIKLM", DataSourceType.PASTE);
909     SequenceI seq1 = af.getViewport().getAlignment().getSequenceAt(0);
910
911     /*
912      * add some features to the sequence
913      */
914     int score = 1;
915     addFeatures(seq1, "type1", score++);
916     addFeatures(seq1, "type2", score++);
917     addFeatures(seq1, "type3", score++);
918     addFeatures(seq1, "type4", score++);
919     addFeatures(seq1, "type5", score++);
920
921     /*
922      * set colour schemes for features
923      */
924     FeatureRenderer fr = af.getFeatureRenderer();
925     fr.findAllFeatures(true);
926
927     // type1: red
928     fr.setColour("type1", new FeatureColour(Color.red));
929
930     // type2: by label
931     FeatureColourI byLabel = new FeatureColour();
932     byLabel.setColourByLabel(true);
933     fr.setColour("type2", byLabel);
934
935     // type3: by score above threshold
936     FeatureColourI byScore = new FeatureColour(null, Color.BLACK,
937             Color.BLUE, null, 1, 10);
938     byScore.setAboveThreshold(true);
939     byScore.setThreshold(2f);
940     fr.setColour("type3", byScore);
941
942     // type4: by attribute AF
943     FeatureColourI byAF = new FeatureColour();
944     byAF.setColourByLabel(true);
945     byAF.setAttributeName("AF");
946     fr.setColour("type4", byAF);
947
948     // type5: by attribute CSQ:PolyPhen below threshold
949     FeatureColourI byPolyPhen = new FeatureColour(null, Color.BLACK,
950             Color.BLUE, null, 1, 10);
951     byPolyPhen.setBelowThreshold(true);
952     byPolyPhen.setThreshold(3f);
953     byPolyPhen.setAttributeName("CSQ", "PolyPhen");
954     fr.setColour("type5", byPolyPhen);
955
956     /*
957      * set filters for feature types
958      */
959
960     // filter type1 features by (label contains "x")
961     FeatureMatcherSetI filterByX = new FeatureMatcherSet();
962     filterByX.and(FeatureMatcher.byLabel(Condition.Contains, "x"));
963     fr.setFeatureFilter("type1", filterByX);
964
965     // filter type2 features by (score <= 2.4 and score > 1.1)
966     FeatureMatcherSetI filterByScore = new FeatureMatcherSet();
967     filterByScore.and(FeatureMatcher.byScore(Condition.LE, "2.4"));
968     filterByScore.and(FeatureMatcher.byScore(Condition.GT, "1.1"));
969     fr.setFeatureFilter("type2", filterByScore);
970
971     // filter type3 features by (AF contains X OR CSQ:PolyPhen != 0)
972     FeatureMatcherSetI filterByXY = new FeatureMatcherSet();
973     filterByXY
974             .and(FeatureMatcher.byAttribute(Condition.Contains, "X", "AF"));
975     filterByXY.or(FeatureMatcher.byAttribute(Condition.NE, "0", "CSQ",
976             "PolyPhen"));
977     fr.setFeatureFilter("type3", filterByXY);
978
979     /*
980      * save as Jalview project
981      */
982     File tfile = File.createTempFile("JalviewTest", ".jvp");
983     tfile.deleteOnExit();
984     String filePath = tfile.getAbsolutePath();
985     assertTrue(af.saveAlignment(filePath, FileFormat.Jalview),
986             "Failed to store as a project.");
987
988     /*
989      * close current alignment and load the saved project
990      */
991     af.closeMenuItem_actionPerformed(true);
992     af = null;
993     af = new FileLoader().LoadFileWaitTillLoaded(filePath,
994             DataSourceType.FILE);
995     assertNotNull(af, "Failed to import new project");
996
997     /*
998      * verify restored feature colour schemes and filters
999      */
1000     fr = af.getFeatureRenderer();
1001     FeatureColourI fc = fr.getFeatureStyle("type1");
1002     assertTrue(fc.isSimpleColour());
1003     assertEquals(fc.getColour(), Color.red);
1004     fc = fr.getFeatureStyle("type2");
1005     assertTrue(fc.isColourByLabel());
1006     fc = fr.getFeatureStyle("type3");
1007     assertTrue(fc.isGraduatedColour());
1008     assertNull(fc.getAttributeName());
1009     assertTrue(fc.isAboveThreshold());
1010     assertEquals(fc.getThreshold(), 2f);
1011     fc = fr.getFeatureStyle("type4");
1012     assertTrue(fc.isColourByLabel());
1013     assertTrue(fc.isColourByAttribute());
1014     assertEquals(fc.getAttributeName(), new String[] { "AF" });
1015     fc = fr.getFeatureStyle("type5");
1016     assertTrue(fc.isGraduatedColour());
1017     assertTrue(fc.isColourByAttribute());
1018     assertEquals(fc.getAttributeName(), new String[] { "CSQ", "PolyPhen" });
1019     assertTrue(fc.isBelowThreshold());
1020     assertEquals(fc.getThreshold(), 3f);
1021
1022     assertEquals(fr.getFeatureFilter("type1").toStableString(),
1023             "Label Contains x");
1024     assertEquals(fr.getFeatureFilter("type2").toStableString(),
1025             "(Score LE 2.4) AND (Score GT 1.1)");
1026     assertEquals(fr.getFeatureFilter("type3").toStableString(),
1027             "(AF Contains X) OR (CSQ:PolyPhen NE 0.0)");
1028   }
1029
1030   private void addFeature(SequenceI seq, String featureType, int score)
1031   {
1032     SequenceFeature sf = new SequenceFeature(featureType, "desc", 1, 2,
1033             score, "grp");
1034     sf.setValue("AF", score);
1035     sf.setValue("CSQ", new HashMap<String, String>()
1036     {
1037       {
1038         put("PolyPhen", Integer.toString(score));
1039       }
1040     });
1041     seq.addSequenceFeature(sf);
1042   }
1043
1044   /**
1045    * Adds two features of the given type to the given sequence, also setting the
1046    * score as the value of attribute "AF" and sub-attribute "CSQ:PolyPhen"
1047    * 
1048    * @param seq
1049    * @param featureType
1050    * @param score
1051    */
1052   private void addFeatures(SequenceI seq, String featureType, int score)
1053   {
1054     addFeature(seq, featureType, score++);
1055     addFeature(seq, featureType, score);
1056   }
1057
1058   /**
1059    * pre 2.11 - jalview 2.10 erroneously created new dataset entries for each
1060    * view (JAL-3171) this test ensures we can import and merge those views
1061    */
1062   @Test(groups = { "Functional" })
1063   public void testMergeDatasetsforViews() throws IOException
1064   {
1065     // simple project - two views on one alignment
1066     AlignFrame af = new FileLoader(false).LoadFileWaitTillLoaded(
1067             "examples/testdata/projects/twoViews.jvp", DataSourceType.FILE);
1068     assertNotNull(af);
1069     assertTrue(af.getAlignPanels().size() > 1);
1070     verifyDs(af);
1071   }
1072
1073   /**
1074    * pre 2.11 - jalview 2.10 erroneously created new dataset entries for each
1075    * view (JAL-3171) this test ensures we can import and merge those views This
1076    * is a more complex project
1077    */
1078   @Test(groups = { "Functional" })
1079   public void testMergeDatasetsforManyViews() throws IOException
1080   {
1081     Desktop.instance.closeAll_actionPerformed(null);
1082
1083     // complex project - one dataset, several views on several alignments
1084     AlignFrame af = new FileLoader(false).LoadFileWaitTillLoaded(
1085             "examples/testdata/projects/manyViews.jvp",
1086             DataSourceType.FILE);
1087     assertNotNull(af);
1088
1089     AlignmentI ds = null;
1090     for (AlignFrame alignFrame : Desktop.getAlignFrames())
1091     {
1092       if (ds == null)
1093       {
1094         ds = verifyDs(alignFrame);
1095       }
1096       else
1097       {
1098         // check that this frame's dataset matches the last
1099         assertTrue(ds == verifyDs(alignFrame));
1100       }
1101     }
1102   }
1103
1104   private AlignmentI verifyDs(AlignFrame af)
1105   {
1106     AlignmentI ds = null;
1107     for (AlignmentViewPanel ap : af.getAlignPanels())
1108     {
1109       if (ds == null)
1110       {
1111         ds = ap.getAlignment().getDataset();
1112       }
1113       else
1114       {
1115         assertTrue(ap.getAlignment().getDataset() == ds,
1116                 "Dataset was not the same for imported 2.10.5 project with several alignment views");
1117       }
1118     }
1119     return ds;
1120   }
1121
1122   @Test(groups = "Functional")
1123   public void testRestorePCAViewAssociation() throws IOException
1124   {
1125     Desktop.instance.closeAll_actionPerformed(null);
1126     final String PCAVIEWNAME = "With PCA";
1127     // create a new tempfile
1128     File tempfile = File.createTempFile("testRestorePCAViewAssociation",
1129             "jvp");
1130
1131     {
1132       String exampleFile = "examples/uniref50.fa";
1133       AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(exampleFile,
1134               DataSourceType.FILE);
1135       assertNotNull(af, "Didn't read in the example file correctly.");
1136       AlignmentPanel origView = (AlignmentPanel) af.getAlignPanels().get(0);
1137       AlignmentPanel newview = af.newView(PCAVIEWNAME, true);
1138       // create another for good measure
1139       af.newView("Not the PCA View", true);
1140       PCAPanel pcaPanel = new PCAPanel(origView, "BLOSUM62",
1141               new SimilarityParams(true, true, true, false));
1142       // we're in the test exec thread, so we can just run synchronously here
1143       pcaPanel.run();
1144
1145       // now switch the linked view
1146       pcaPanel.selectAssociatedView(newview);
1147
1148       assertTrue(pcaPanel.getAlignViewport() == newview.getAlignViewport(),
1149               "PCA should be associated with 'With PCA' view: test is broken");
1150
1151       // now save and reload project
1152       Jalview2XML jv2xml = new jalview.project.Jalview2XML(false);
1153       tempfile.delete();
1154       jv2xml.saveState(tempfile);
1155       assertTrue(jv2xml.errorMessage == null,
1156               "Failed to save dummy project with PCA: test broken");
1157     }
1158
1159     // load again.
1160     Desktop.instance.closeAll_actionPerformed(null);
1161     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
1162             tempfile.getCanonicalPath(), DataSourceType.FILE);
1163     JInternalFrame[] frames = Desktop.instance.getAllFrames();
1164     // PCA and the tabbed alignment view should be the only two windows on the
1165     // desktop
1166     assertEquals(frames.length, 2,
1167             "PCA and the tabbed alignment view should be the only two windows on the desktop");
1168     PCAPanel pcaPanel = (PCAPanel) frames[frames[0] == af ? 1 : 0];
1169
1170     AlignmentViewPanel restoredNewView = null;
1171     for (AlignmentViewPanel alignpanel : Desktop.getAlignmentPanels(null))
1172     {
1173       if (alignpanel.getAlignViewport() == pcaPanel.getAlignViewport())
1174       {
1175         restoredNewView = alignpanel;
1176       }
1177     }
1178     assertEquals(restoredNewView.getViewName(), PCAVIEWNAME);
1179     assertTrue(
1180             restoredNewView.getAlignViewport() == pcaPanel
1181                     .getAlignViewport(),
1182             "Didn't restore correct view association for the PCA view");
1183   }
1184
1185   /**
1186    * Test that annotation ordering is faithfully restored
1187    * 
1188    * @throws Exception
1189    */
1190   @Test(groups = { "Functional" })
1191   public void testRestoreAnnotationOrdering() throws Exception
1192   {
1193     Desktop.instance.closeAll_actionPerformed(null);
1194     Cache.setProperty("SHOW_ANNOTATION", "true");
1195     Cache.setProperty("SHOW_CONSERVATION", "true");
1196     Cache.setProperty("SHOW_QUALITY", "false");
1197     Cache.setProperty("SHOW_IDENTITY", "false");
1198     Cache.setProperty("SHOW_OCCUPANCY", "true");
1199     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
1200             "examples/uniref50.fa", DataSourceType.FILE);
1201     assertNotNull(af, "Didn't read in the example file correctly.");
1202
1203     AlignFrame[] afs = Desktop.getAlignFrames();
1204     assertEquals(afs.length, 1);
1205     AlignmentI alignment = afs[0].getViewport().getAlignment();
1206     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
1207     assertEquals(anns.length, 2);
1208
1209     /*
1210      * this order is hard wired in AlignmentViewport.initAutoAnnotation()
1211      */
1212     assertEquals(anns[0].label, "Conservation");
1213     assertEquals(anns[1].label, "Occupancy");
1214
1215     /*
1216      * add two user annotations, adjust order, height and visibility
1217      */
1218     AlignmentAnnotation tmp = anns[0];
1219     anns[0] = anns[1];
1220     anns[1] = tmp;
1221     Annotation[] an1 = new Annotation[] { new Annotation(2f) };
1222     Annotation[] an2 = new Annotation[] { new Annotation(3f) };
1223     alignment.addAnnotation(new AlignmentAnnotation("hello", "desc1", an1),
1224             1);
1225     alignment.addAnnotation(new AlignmentAnnotation("world", "desc2", an2),
1226             3);
1227     anns = alignment.getAlignmentAnnotation();
1228     assertEquals(anns[0].label, "Occupancy");
1229     assertEquals(anns[1].label, "hello");
1230     assertEquals(anns[2].label, "Conservation");
1231     assertEquals(anns[3].label, "world");
1232     anns[0].graphHeight = 60;
1233     anns[0].visible = false;
1234     anns[1].graphHeight = 70;
1235     anns[2].graphHeight = 80;
1236     anns[3].graphHeight = 90;
1237     anns[3].visible = false;
1238
1239     /*
1240      * save project to temporary file and reload on to an empty desktop,
1241      * verify annotation order, height and visibility is restored
1242      */
1243     File projectFile = File.createTempFile("jvTest", ".jvp");
1244     new Jalview2XML(false).saveState(projectFile);
1245     Desktop.instance.closeAll_actionPerformed(null);
1246     if (Desktop.getAlignFrames() != null)
1247     {
1248       Assert.assertEquals(Desktop.getAlignFrames().length, 0);
1249     }
1250   
1251     af = new FileLoader().LoadFileWaitTillLoaded(
1252             projectFile.getAbsolutePath(),
1253             DataSourceType.FILE);
1254     anns = af.getViewport().getAlignment().getAlignmentAnnotation();
1255     assertEquals(anns.length, 4);
1256     assertEquals(anns[0].label, "Occupancy");
1257     assertEquals(anns[1].label, "hello");
1258     assertEquals(anns[2].label, "Conservation");
1259     assertEquals(anns[3].label, "world");
1260     assertEquals(anns[0].graphHeight, 60);
1261     assertEquals(anns[1].graphHeight, 70);
1262     assertEquals(anns[2].graphHeight, 80);
1263     assertEquals(anns[3].graphHeight, 90);
1264     assertFalse(anns[0].visible);
1265     assertTrue(anns[1].visible);
1266     assertTrue(anns[2].visible);
1267     assertFalse(anns[3].visible);
1268
1269     /*
1270      * reload the project with Preferences set to not create Occupancy;
1271      * this should still appear, as it is in the saved project
1272      */
1273     Desktop.instance.closeAll_actionPerformed(null);
1274     Cache.setProperty("SHOW_OCCUPANCY", "false");
1275     af = new FileLoader().LoadFileWaitTillLoaded(
1276             projectFile.getAbsolutePath(), DataSourceType.FILE);
1277     anns = af.getViewport().getAlignment().getAlignmentAnnotation();
1278     assertEquals(anns.length, 4);
1279     assertEquals(anns[0].label, "Occupancy");
1280     assertEquals(anns[1].label, "hello");
1281     assertEquals(anns[2].label, "Conservation");
1282     assertEquals(anns[3].label, "world");
1283
1284     /*
1285      * reload the project with Preferences set to create Quality annotation;
1286      * this should not appear, as it is not in the saved project
1287      */
1288     Desktop.instance.closeAll_actionPerformed(null);
1289     Cache.setProperty("SHOW_OCCUPANCY", "false");
1290     Cache.setProperty("SHOW_QUALITY", "true");
1291     af = new FileLoader().LoadFileWaitTillLoaded(
1292             projectFile.getAbsolutePath(), DataSourceType.FILE);
1293     anns = af.getViewport().getAlignment().getAlignmentAnnotation();
1294     assertEquals(anns.length, 4);
1295     assertEquals(anns[0].label, "Occupancy");
1296     assertEquals(anns[1].label, "hello");
1297     assertEquals(anns[2].label, "Conservation");
1298     assertEquals(anns[3].label, "world");
1299   }
1300 }