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