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