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