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