JAL-3858 simple test for PAE matrix recovery
[jalview.git] / test / jalview / project / Jalview2xmlTests.java
index fb2d3e9..84af3e4 100644 (file)
@@ -23,20 +23,46 @@ package jalview.project;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNotSame;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.swing.JInternalFrame;
+
+import org.testng.Assert;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
 import jalview.analysis.scoremodels.SimilarityParams;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureColourI;
 import jalview.api.ViewStyleI;
+import jalview.bin.Cache;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.ContactListI;
+import jalview.datamodel.ContactMatrixI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.GeneLocus;
 import jalview.datamodel.HiddenSequences;
+import jalview.datamodel.Mapping;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.PDBEntry.Type;
+import jalview.datamodel.Sequence.DBModList;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
@@ -48,10 +74,11 @@ import jalview.gui.AlignFrame;
 import jalview.gui.AlignViewport;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.Desktop;
-import jalview.gui.FeatureRenderer;
 import jalview.gui.JvOptionPane;
+import jalview.gui.OverviewPanel;
 import jalview.gui.PCAPanel;
 import jalview.gui.PopupMenu;
+import jalview.gui.Preferences;
 import jalview.gui.SliderPanel;
 import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
@@ -68,23 +95,11 @@ import jalview.schemes.RNAHelicesColour;
 import jalview.schemes.StrandColourScheme;
 import jalview.schemes.TCoffeeColourScheme;
 import jalview.structure.StructureImportSettings;
+import jalview.util.MapList;
 import jalview.util.matcher.Condition;
 import jalview.viewmodel.AlignmentViewport;
-
-import java.awt.Color;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.swing.JInternalFrame;
-
-import org.testng.Assert;
-import org.testng.AssertJUnit;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
+import jalview.viewmodel.seqfeatures.FeatureRendererModel;
+import jalview.ws.datamodel.alphafold.PAEContactMatrix;
 
 @Test(singleThreaded = true)
 public class Jalview2xmlTests extends Jalview2xmlBase
@@ -109,65 +124,74 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     assertNotNull(af, "Didn't read input file " + inFile);
     int olddsann = countDsAnn(af.getViewport());
     assertTrue(olddsann > 0, "Didn't find any dataset annotations");
-    af.changeColour_actionPerformed(JalviewColourScheme.RNAHelices
-            .toString());
+    af.changeColour_actionPerformed(
+            JalviewColourScheme.RNAHelices.toString());
     assertTrue(
-            af.getViewport().getGlobalColourScheme() instanceof RNAHelicesColour,
+            af.getViewport()
+                    .getGlobalColourScheme() instanceof RNAHelicesColour,
             "Couldn't apply RNA helices colourscheme");
-    assertTrue(af.saveAlignment(tfile, FileFormat.Jalview),
+    af.saveAlignment(tfile, FileFormat.Jalview);
+    assertTrue(af.isSaveAlignmentSuccessful(),
             "Failed to store as a project.");
     af.closeMenuItem_actionPerformed(true);
     af = null;
-    af = new FileLoader()
-            .LoadFileWaitTillLoaded(tfile, DataSourceType.FILE);
+    af = new FileLoader().LoadFileWaitTillLoaded(tfile,
+            DataSourceType.FILE);
     assertNotNull(af, "Failed to import new project");
     int newdsann = countDsAnn(af.getViewport());
     assertEquals(olddsann, newdsann,
             "Differing numbers of dataset sequence annotation\nOriginally "
                     + olddsann + " and now " + newdsann);
-    System.out
-            .println("Read in same number of annotations as originally present ("
+    System.out.println(
+            "Read in same number of annotations as originally present ("
                     + olddsann + ")");
     assertTrue(
 
-    af.getViewport().getGlobalColourScheme() instanceof RNAHelicesColour,
+            af.getViewport()
+                    .getGlobalColourScheme() instanceof RNAHelicesColour,
             "RNA helices colourscheme was not applied on import.");
   }
 
   @Test(groups = { "Functional" })
   public void testTCoffeeScores() throws Exception
   {
-    String inFile = "examples/uniref50.fa", inAnnot = "examples/uniref50.score_ascii";
+    String inFile = "examples/uniref50.fa",
+            inAnnot = "examples/uniref50.score_ascii";
     String tfile = File.createTempFile("JalviewTest", ".jvp")
             .getAbsolutePath();
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
             DataSourceType.FILE);
     assertNotNull(af, "Didn't read input file " + inFile);
     af.loadJalviewDataFile(inAnnot, DataSourceType.FILE, null, null);
-    assertSame(af.getViewport().getGlobalColourScheme().getClass(),
+    AlignViewport viewport = af.getViewport();
+    assertSame(viewport.getGlobalColourScheme().getClass(),
             TCoffeeColourScheme.class, "Didn't set T-coffee colourscheme");
-    assertNotNull(ColourSchemeProperty.getColourScheme(af.getViewport()
-            .getAlignment(), af.getViewport().getGlobalColourScheme()
-            .getSchemeName()), "Recognise T-Coffee score from string");
-
-    assertTrue(af.saveAlignment(tfile, FileFormat.Jalview),
+    assertNotNull(
+            ColourSchemeProperty.getColourScheme(viewport,
+                    viewport.getAlignment(),
+                    viewport.getGlobalColourScheme().getSchemeName()),
+            "Recognise T-Coffee score from string");
+
+    af.saveAlignment(tfile, FileFormat.Jalview);
+    assertTrue(af.isSaveAlignmentSuccessful(),
             "Failed to store as a project.");
     af.closeMenuItem_actionPerformed(true);
     af = null;
-    af = new FileLoader()
-            .LoadFileWaitTillLoaded(tfile, DataSourceType.FILE);
+    af = new FileLoader().LoadFileWaitTillLoaded(tfile,
+            DataSourceType.FILE);
     assertNotNull(af, "Failed to import new project");
     assertSame(af.getViewport().getGlobalColourScheme().getClass(),
             TCoffeeColourScheme.class,
             "Didn't set T-coffee colourscheme for imported project.");
-    System.out
-            .println("T-Coffee score shading successfully recovered from project.");
+    System.out.println(
+            "T-Coffee score shading successfully recovered from project.");
   }
 
   @Test(groups = { "Functional" })
   public void testColourByAnnotScores() throws Exception
   {
-    String inFile = "examples/uniref50.fa", inAnnot = "examples/testdata/uniref50_iupred.jva";
+    String inFile = "examples/uniref50.fa",
+            inAnnot = "examples/testdata/uniref50_iupred.jva";
     String tfile = File.createTempFile("JalviewTest", ".jvp")
             .getAbsolutePath();
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
@@ -178,12 +202,12 @@ public class Jalview2xmlTests extends Jalview2xmlBase
             .getSequenceAt(0).getAnnotation("IUPredWS (Short)");
     assertTrue(
 
-    aa != null && aa.length > 0,
+            aa != null && aa.length > 0,
             "Didn't find any IUPred annotation to use to shade alignment.");
     AnnotationColourGradient cs = new AnnotationColourGradient(aa[0], null,
             AnnotationColourGradient.ABOVE_THRESHOLD);
-    AnnotationColourGradient gcs = new AnnotationColourGradient(aa[0],
-            null, AnnotationColourGradient.BELOW_THRESHOLD);
+    AnnotationColourGradient gcs = new AnnotationColourGradient(aa[0], null,
+            AnnotationColourGradient.BELOW_THRESHOLD);
     cs.setSeqAssociated(true);
     gcs.setSeqAssociated(true);
     af.changeColour(cs);
@@ -195,19 +219,20 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     sg.addSequence(af.getViewport().getAlignment().getSequenceAt(1), false);
     sg.addSequence(af.getViewport().getAlignment().getSequenceAt(2), true);
     af.alignPanel.alignmentChanged();
-    assertTrue(af.saveAlignment(tfile, FileFormat.Jalview),
+    af.saveAlignment(tfile, FileFormat.Jalview);
+    assertTrue(af.isSaveAlignmentSuccessful(),
             "Failed to store as a project.");
     af.closeMenuItem_actionPerformed(true);
     af = null;
-    af = new FileLoader()
-            .LoadFileWaitTillLoaded(tfile, DataSourceType.FILE);
+    af = new FileLoader().LoadFileWaitTillLoaded(tfile,
+            DataSourceType.FILE);
     assertNotNull(af, "Failed to import new project");
 
     // check for group and alignment colourschemes
 
     ColourSchemeI _rcs = af.getViewport().getGlobalColourScheme();
-    ColourSchemeI _rgcs = af.getViewport().getAlignment().getGroups()
-            .get(0).getColourScheme();
+    ColourSchemeI _rgcs = af.getViewport().getAlignment().getGroups().get(0)
+            .getColourScheme();
     assertNotNull(_rcs, "Didn't recover global colourscheme");
     assertTrue(_rcs instanceof AnnotationColourGradient,
             "Didn't recover annotation colour global scheme");
@@ -217,8 +242,9 @@ public class Jalview2xmlTests extends Jalview2xmlBase
 
     boolean diffseqcols = false, diffgseqcols = false;
     SequenceI[] sqs = af.getViewport().getAlignment().getSequencesArray();
-    for (int p = 0, pSize = af.getViewport().getAlignment().getWidth(); p < pSize
-            && (!diffseqcols || !diffgseqcols); p++)
+    for (int p = 0,
+            pSize = af.getViewport().getAlignment().getWidth(); p < pSize
+                    && (!diffseqcols || !diffgseqcols); p++)
     {
       if (_rcs.findColour(sqs[0].getCharAt(p), p, sqs[0], null, 0f) != _rcs
               .findColour(sqs[5].getCharAt(p), p, sqs[5], null, 0f))
@@ -227,8 +253,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
       }
     }
     assertTrue(diffseqcols, "Got Different sequence colours");
-    System.out
-            .println("Per sequence colourscheme (Background) successfully applied and recovered.");
+    System.out.println(
+            "Per sequence colourscheme (Background) successfully applied and recovered.");
 
     assertNotNull(_rgcs, "Didn't recover group colourscheme");
     assertTrue(_rgcs instanceof AnnotationColourGradient,
@@ -237,25 +263,27 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     assertTrue(__rcs.isSeqAssociated(),
             "Group Annotation colourscheme wasn't sequence associated");
 
-    for (int p = 0, pSize = af.getViewport().getAlignment().getWidth(); p < pSize
-            && (!diffseqcols || !diffgseqcols); p++)
+    for (int p = 0,
+            pSize = af.getViewport().getAlignment().getWidth(); p < pSize
+                    && (!diffseqcols || !diffgseqcols); p++)
     {
-      if (_rgcs.findColour(sqs[1].getCharAt(p), p, sqs[1], null, 0f) != _rgcs
-              .findColour(sqs[2].getCharAt(p), p, sqs[2], null, 0f))
+      if (_rgcs.findColour(sqs[1].getCharAt(p), p, sqs[1], null,
+              0f) != _rgcs.findColour(sqs[2].getCharAt(p), p, sqs[2], null,
+                      0f))
       {
         diffgseqcols = true;
       }
     }
     assertTrue(diffgseqcols, "Got Different group sequence colours");
-    System.out
-            .println("Per sequence (Group) colourscheme successfully applied and recovered.");
+    System.out.println(
+            "Per sequence (Group) colourscheme successfully applied and recovered.");
   }
 
   @Test(groups = { "Functional" })
   public void gatherViewsHere() throws Exception
   {
-    int origCount = Desktop.getAlignFrames() == null ? 0 : Desktop
-            .getAlignFrames().length;
+    int origCount = Desktop.getAlignFrames() == null ? 0
+            : Desktop.getAlignFrames().length;
     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
             "examples/exampleFile_2_7.jar", DataSourceType.FILE);
     assertNotNull(af, "Didn't read in the example file correctly.");
@@ -336,14 +364,14 @@ public class Jalview2xmlTests extends Jalview2xmlBase
           sq.findPosition(p);
           try
           {
-            assertTrue(
-                    (alaa.annotations[p] == null && refan.annotations[p] == null)
-                            || alaa.annotations[p].value == refan.annotations[p].value,
+            assertTrue((alaa.annotations[p] == null
+                    && refan.annotations[p] == null)
+                    || alaa.annotations[p].value == refan.annotations[p].value,
                     "Mismatch at alignment position " + p);
           } catch (NullPointerException q)
           {
-            Assert.fail("Mismatch of alignment annotations at position "
-                    + p + " Ref seq ann: " + refan.annotations[p]
+            Assert.fail("Mismatch of alignment annotations at position " + p
+                    + " Ref seq ann: " + refan.annotations[p]
                     + " alignment " + alaa.annotations[p]);
           }
         }
@@ -378,10 +406,10 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     AssertJUnit.assertFalse(structureStyle.sameStyle(groupStyle));
 
     groups.getAlignViewport().setViewStyle(structureStyle);
-    AssertJUnit.assertFalse(groupStyle.sameStyle(groups.getAlignViewport()
-            .getViewStyle()));
-    Assert.assertTrue(structureStyle.sameStyle(groups.getAlignViewport()
-            .getViewStyle()));
+    AssertJUnit.assertFalse(
+            groupStyle.sameStyle(groups.getAlignViewport().getViewStyle()));
+    Assert.assertTrue(structureStyle
+            .sameStyle(groups.getAlignViewport().getViewStyle()));
 
   }
 
@@ -402,9 +430,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
 
     // check FileLoader returned a reference to the one alignFrame that is
     // actually on the Desktop
-    assertSame(
-            af,
-            Desktop.getAlignFrameFor(af.getViewport()),
+    assertSame(af, Desktop.getAlignFrameFor(af.getViewport()),
             "Jalview2XML.loadAlignFrame() didn't return correct AlignFrame reference for multiple view window");
 
     Desktop.explodeViews(af);
@@ -431,11 +457,11 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
             DataSourceType.FILE);
     Assert.assertNotNull(af);
-    Assert.assertEquals(
-            Desktop.getAlignFrames().length,
-            Desktop.getAlignmentPanels(af.getViewport().getSequenceSetId()).length);
-    Assert.assertEquals(
-            Desktop.getAlignmentPanels(af.getViewport().getSequenceSetId()).length,
+    Assert.assertEquals(Desktop.getAlignFrames().length,
+            Desktop.getAlignmentPanels(
+                    af.getViewport().getSequenceSetId()).length);
+    Assert.assertEquals(Desktop
+            .getAlignmentPanels(af.getViewport().getSequenceSetId()).length,
             oldviews);
   }
 
@@ -537,8 +563,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     assertTrue(Jalview2XML.isVersionStringLaterThan(null, "Test"));
     assertTrue(Jalview2XML.isVersionStringLaterThan(null, "TEST"));
     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", "Test"));
-    assertTrue(Jalview2XML
-            .isVersionStringLaterThan(null, "Automated Build"));
+    assertTrue(
+            Jalview2XML.isVersionStringLaterThan(null, "Automated Build"));
     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3",
             "Automated Build"));
     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3",
@@ -570,6 +596,14 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.3b1", "2.8.3"));
     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.3", "2.8.2b1"));
     assertFalse(Jalview2XML.isVersionStringLaterThan("2.8.0b2", "2.8.0b1"));
+    /*
+     * test for patch release versions
+     */
+    assertFalse(Jalview2XML.isVersionStringLaterThan("2.11.3.0", "2.11.2"));
+    assertTrue(Jalview2XML.isVersionStringLaterThan("2.11.3.0","2.11.4"));
+    assertFalse(Jalview2XML.isVersionStringLaterThan("2.12.2.0b1","2.12.2.0"));
+    assertFalse(Jalview2XML.isVersionStringLaterThan("2.12.2.3","2.12.2.2"));
+
   }
 
   /**
@@ -647,8 +681,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
 
       n++;
     }
-    File tfile = File
-            .createTempFile("testStoreAndRecoverGroupReps", ".jvp");
+    File tfile = File.createTempFile("testStoreAndRecoverGroupReps",
+            ".jvp");
     try
     {
       new Jalview2XML(false).saveState(tfile);
@@ -687,9 +721,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
        */
       List<String> hidden = hiddenSeqNames.get(ap.getViewName());
       HiddenSequences hs = alignment.getHiddenSequences();
-      assertEquals(
-              hidden.size(),
-              hs.getSize(),
+      assertEquals(hidden.size(), hs.getSize(),
               "wrong number of restored hidden sequences in "
                       + ap.getViewName());
     }
@@ -731,14 +763,18 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     pdbEntries[1] = new PDBEntry("3W5V", "B", Type.PDB, testFile);
     pdbEntries[2] = new PDBEntry("3W5V", "C", Type.PDB, testFile);
     pdbEntries[3] = new PDBEntry("3W5V", "D", Type.PDB, testFile);
-    Assert.assertEquals(seqs[0].getDatasetSequence().getAllPDBEntries()
-            .get(0), pdbEntries[0]);
-    Assert.assertEquals(seqs[1].getDatasetSequence().getAllPDBEntries()
-            .get(0), pdbEntries[1]);
-    Assert.assertEquals(seqs[2].getDatasetSequence().getAllPDBEntries()
-            .get(0), pdbEntries[2]);
-    Assert.assertEquals(seqs[3].getDatasetSequence().getAllPDBEntries()
-            .get(0), pdbEntries[3]);
+    Assert.assertEquals(
+            seqs[0].getDatasetSequence().getAllPDBEntries().get(0),
+            pdbEntries[0]);
+    Assert.assertEquals(
+            seqs[1].getDatasetSequence().getAllPDBEntries().get(0),
+            pdbEntries[1]);
+    Assert.assertEquals(
+            seqs[2].getDatasetSequence().getAllPDBEntries().get(0),
+            pdbEntries[2]);
+    Assert.assertEquals(
+            seqs[3].getDatasetSequence().getAllPDBEntries().get(0),
+            pdbEntries[3]);
 
     File tfile = File.createTempFile("testStoreAndRecoverPDBEntry", ".jvp");
     try
@@ -786,7 +822,94 @@ public class Jalview2xmlTests extends Jalview2xmlBase
               "Mismatch PDBEntry 'Type'");
       Assert.assertNotNull(recov.getFile(),
               "Recovered PDBEntry should have a non-null file entry");
+      Assert.assertEquals(
+              recov.getFile().toLowerCase(Locale.ENGLISH)
+                      .lastIndexOf("pdb"),
+              recov.getFile().length() - 3,
+              "Recovered PDBEntry file should have PDB suffix");
+    }
+  }
+
+  /**
+   * Configure an alignment and a sub-group each with distinct colour schemes,
+   * Conservation and PID thresholds, and confirm these are restored from the
+   * saved project.
+   * 
+   * @throws IOException
+   */
+  @Test(groups = { "Functional" })
+  public void testStoreAndRecoverAnnotationRowElementColours()
+          throws IOException
+  {
+    Desktop.instance.closeAll_actionPerformed(null);
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded("SEQ\tMNQ",
+            DataSourceType.PASTE);
+
+    AlignViewport av = af.getViewport();
+    AlignmentI al = av.getAlignment();
+    SequenceI fsq;
+    fsq = al.getSequenceAt(0);
+    Annotation annots[] = new Annotation[fsq.getLength()];
+    AlignmentAnnotation ala = new AlignmentAnnotation("Colour", "Annots",
+            annots);
+    annots[0] = new Annotation(1.0f);
+    annots[1] = new Annotation(2.0f);
+    annots[2] = new Annotation(3.0f);
+    annots[0].colour = Color.RED;
+    annots[1].colour = Color.GREEN;
+    annots[2].colour = Color.BLUE;
+    ala.validateRangeAndDisplay();
+    al.getSequenceAt(0).addAlignmentAnnotation(ala);
+    al.addAnnotation(ala);
+    /*
+     * and colour by annotation
+     */
+    AnnotationColourGradient acg = new AnnotationColourGradient(ala,
+            af.alignPanel.av.getGlobalColourScheme(), 0);
+    acg.setSeqAssociated(true);
+    acg.setPredefinedColours(true);
+    af.changeColour(acg);
+    Color seqcol[] = new Color[3];
+    for (int iStart=fsq.findIndex(fsq.getStart()),i=0;i<3;i++) {
+      seqcol[i] = af.alignPanel.getSeqPanel().seqCanvas.getSequenceRenderer().getResidueColour(fsq, iStart+i, null);
+    }
+    /*
+     * save project, close windows, reload project, verify
+     */
+    File tfile = File.createTempFile(
+            "testStoreAndRecoverAnnotRowElemColors", ".jvp");
+    tfile.deleteOnExit();
+    new Jalview2XML(false).saveState(tfile);
+    //Desktop.instance.closeAll_actionPerformed(null);
+    af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
+            DataSourceType.FILE);
+    Assert.assertNotNull(af, "Failed to reload project");
+    /*
+     * verify alignment annotation has colors
+     */
+    av = af.getViewport();
+    
+    ColourSchemeI loadedCscheme = av.getGlobalColourScheme();
+    Assert.assertTrue(loadedCscheme instanceof AnnotationColourGradient,"Didn't apply Annotation colour gradient");
+    acg = (AnnotationColourGradient) loadedCscheme;
+    assertTrue(acg.isSeqAssociated());
+    assertTrue(acg.isPredefinedColours());
+
+    al = av.getAlignment();
+    fsq = al.getSequenceAt(0);
+    ala = fsq.getAnnotation()[0];
+    Assert.assertNotNull(ala, "No annotation row recovered");
+    Assert.assertNotNull(ala.annotations);
+    for (int iStart = al.getSequenceAt(0)
+            .findIndex(al.getSequenceAt(0).getStart()), i = 0; i < 3; i++)
+    {
+      Assert.assertTrue(ala.annotations[i].colour!=null);
+      Assert.assertTrue(ala.annotations[i].colour.equals(annots[i].colour));
+      Color newseqcol = af.alignPanel.getSeqPanel().seqCanvas.getSequenceRenderer().getResidueColour(fsq, iStart+i, null);
+      Assert.assertTrue(seqcol[i].equals(newseqcol),"Sequence shading is different");
+
     }
+    
   }
 
   /**
@@ -809,6 +932,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     /*
      * Colour alignment by Buried Index, Above 10% PID, By Conservation 20%
      */
+    av.setColourAppliesToAllGroups(false);
     af.changeColour_actionPerformed(JalviewColourScheme.Buried.toString());
     assertTrue(av.getGlobalColourScheme() instanceof BuriedColourScheme);
     af.abovePIDThreshold_actionPerformed(true);
@@ -827,15 +951,18 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     /*
      * create a group with Strand colouring, 30% Conservation
      * and 40% PID threshold
+     * (notice menu action applies to selection group even if mouse click
+     * is at a sequence not in the group)
      */
     SequenceGroup sg = new SequenceGroup();
     sg.addSequence(al.getSequenceAt(0), false);
     sg.setStartRes(15);
     sg.setEndRes(25);
     av.setSelectionGroup(sg);
-    PopupMenu popupMenu = new PopupMenu(af.alignPanel, null, null);
-    popupMenu.changeColour_actionPerformed(JalviewColourScheme.Strand
-            .toString());
+    PopupMenu popupMenu = new PopupMenu(af.alignPanel, al.getSequenceAt(2),
+            null);
+    popupMenu.changeColour_actionPerformed(
+            JalviewColourScheme.Strand.toString());
     assertTrue(sg.getColourScheme() instanceof StrandColourScheme);
     assertEquals(al.getGroups().size(), 1);
     assertSame(al.getGroups().get(0), sg);
@@ -909,7 +1036,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     /*
      * set colour schemes for features
      */
-    FeatureRenderer fr = af.getFeatureRenderer();
+    FeatureRendererModel fr = af.getFeatureRenderer();
     fr.findAllFeatures(true);
 
     // type1: red
@@ -921,8 +1048,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     fr.setColour("type2", byLabel);
 
     // type3: by score above threshold
-    FeatureColourI byScore = new FeatureColour(Color.BLACK, Color.BLUE, 1,
-            10);
+    FeatureColourI byScore = new FeatureColour(null, Color.BLACK,
+            Color.BLUE, null, 1, 10);
     byScore.setAboveThreshold(true);
     byScore.setThreshold(2f);
     fr.setColour("type3", byScore);
@@ -934,8 +1061,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     fr.setColour("type4", byAF);
 
     // type5: by attribute CSQ:PolyPhen below threshold
-    FeatureColourI byPolyPhen = new FeatureColour(Color.BLACK, Color.BLUE,
-            1, 10);
+    FeatureColourI byPolyPhen = new FeatureColour(null, Color.BLACK,
+            Color.BLUE, null, 1, 10);
     byPolyPhen.setBelowThreshold(true);
     byPolyPhen.setThreshold(3f);
     byPolyPhen.setAttributeName("CSQ", "PolyPhen");
@@ -970,7 +1097,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     File tfile = File.createTempFile("JalviewTest", ".jvp");
     tfile.deleteOnExit();
     String filePath = tfile.getAbsolutePath();
-    assertTrue(af.saveAlignment(filePath, FileFormat.Jalview),
+    af.saveAlignment(filePath, FileFormat.Jalview);
+    assertTrue(af.isSaveAlignmentSuccessful(),
             "Failed to store as a project.");
 
     /*
@@ -978,8 +1106,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
      */
     af.closeMenuItem_actionPerformed(true);
     af = null;
-    af = new FileLoader()
-            .LoadFileWaitTillLoaded(filePath, DataSourceType.FILE);
+    af = new FileLoader().LoadFileWaitTillLoaded(filePath,
+            DataSourceType.FILE);
     assertNotNull(af, "Failed to import new project");
 
     /*
@@ -1012,7 +1140,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     assertEquals(fr.getFeatureFilter("type2").toStableString(),
             "(Score LE 2.4) AND (Score GT 1.1)");
     assertEquals(fr.getFeatureFilter("type3").toStableString(),
-            "(AF Contains X) OR (CSQ:PolyPhen NE 0.0)");
+            "(AF Contains X) OR (CSQ:PolyPhen NE 0)");
   }
 
   private void addFeature(SequenceI seq, String featureType, int score)
@@ -1120,15 +1248,18 @@ public class Jalview2xmlTests extends Jalview2xmlBase
       AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(exampleFile,
               DataSourceType.FILE);
       assertNotNull(af, "Didn't read in the example file correctly.");
+      AlignmentPanel origView = (AlignmentPanel) af.getAlignPanels().get(0);
       AlignmentPanel newview = af.newView(PCAVIEWNAME, true);
       // create another for good measure
       af.newView("Not the PCA View", true);
-      PCAPanel pcaPanel = new PCAPanel(newview, "BLOSUM62",
+      PCAPanel pcaPanel = new PCAPanel(origView, "BLOSUM62",
               new SimilarityParams(true, true, true, false));
-
       // we're in the test exec thread, so we can just run synchronously here
       pcaPanel.run();
 
+      // now switch the linked view
+      pcaPanel.selectAssociatedView(newview);
+
       assertTrue(pcaPanel.getAlignViewport() == newview.getAlignViewport(),
               "PCA should be associated with 'With PCA' view: test is broken");
 
@@ -1165,4 +1296,290 @@ public class Jalview2xmlTests extends Jalview2xmlBase
                     .getAlignViewport(),
             "Didn't restore correct view association for the PCA view");
   }
+
+  /**
+   * Test save and reload of DBRefEntry including GeneLocus in project
+   * 
+   * @throws Exception
+   */
+  @Test(groups = { "Functional" })
+  public void testStoreAndRecoverGeneLocus() throws Exception
+  {
+    Desktop.instance.closeAll_actionPerformed(null);
+    String seqData = ">P30419\nACDE\n>X1235\nGCCTGTGACGAA";
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
+            DataSourceType.PASTE);
+    assertNotNull(af, "Didn't read in the example file correctly.");
+
+    AlignmentViewPanel ap = Desktop.getAlignmentPanels(null)[0];
+    SequenceI pep = ap.getAlignment().getSequenceAt(0);
+    SequenceI cds = ap.getAlignment().getSequenceAt(1);
+
+    /*
+     * give 'protein' a dbref to self, a dbref with map to CDS,
+     * and a dbref with map to gene 'locus'
+     */
+    DBRefEntry dbref1 = new DBRefEntry("Uniprot", "1", "P30419", null);
+    pep.addDBRef(dbref1);
+    Mapping cdsmap = new Mapping(cds,
+            new MapList(new int[]
+            { 1, 4 }, new int[] { 1, 12 }, 1, 3));
+    DBRefEntry dbref2 = new DBRefEntry("EMBLCDS", "2", "X1235", cdsmap);
+    pep.addDBRef(dbref2);
+    Mapping locusmap = new Mapping(null,
+            new MapList(new int[]
+            { 1, 4 }, new int[] { 2674123, 2674135 }, 1, 3));
+    DBRefEntry dbref3 = new GeneLocus("human", "GRCh38", "5", locusmap);
+    pep.addDBRef(dbref3);
+
+    File tfile = File.createTempFile("testStoreAndRecoverGeneLocus",
+            ".jvp");
+    try
+    {
+      new Jalview2XML(false).saveState(tfile);
+    } catch (Throwable e)
+    {
+      Assert.fail("Didn't save the state", e);
+    }
+    Desktop.instance.closeAll_actionPerformed(null);
+
+    new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
+            DataSourceType.FILE);
+    AlignmentViewPanel rap = Desktop.getAlignmentPanels(null)[0];
+    SequenceI rpep = rap.getAlignment().getSequenceAt(0);
+    DBModList<DBRefEntry> dbrefs = rpep.getDBRefs();
+    assertEquals(rpep.getName(), "P30419");
+    assertEquals(dbrefs.size(), 3);
+    DBRefEntry dbRef = dbrefs.get(0);
+    assertFalse(dbRef instanceof GeneLocus);
+    assertNull(dbRef.getMap());
+    assertEquals(dbRef, dbref1);
+
+    /*
+     * restored dbrefs with mapping have a different 'map to'
+     * sequence but otherwise match the original dbrefs
+     */
+    dbRef = dbrefs.get(1);
+    assertFalse(dbRef instanceof GeneLocus);
+    assertTrue(dbRef.equalRef(dbref2));
+    assertNotNull(dbRef.getMap());
+    SequenceI rcds = rap.getAlignment().getSequenceAt(1);
+    assertSame(dbRef.getMap().getTo(), rcds);
+    // compare MapList but not map.to
+    assertEquals(dbRef.getMap().getMap(), dbref2.getMap().getMap());
+
+    /*
+     * GeneLocus map.to is null so can compare Mapping objects
+     */
+    dbRef = dbrefs.get(2);
+    assertTrue(dbRef instanceof GeneLocus);
+    assertEquals(dbRef, dbref3);
+  }
+
+  /**
+   * test store and recovery of Overview windows
+   * 
+   * @throws Exception
+   */
+  @Test(groups = { "Functional" }, enabled = true)
+  public void testStoreAndRecoverOverview() throws Exception
+  {
+    Desktop.instance.closeAll_actionPerformed(null);
+
+    Cache.setProperty("SHOW_OVERVIEW", "false");
+    Cache.setProperty(Preferences.USE_LEGACY_GAP, "false");
+    Cache.setColourProperty(Preferences.GAP_COLOUR, Color.green);
+    Cache.setColourProperty(Preferences.HIDDEN_COLOUR, Color.yellow);
+    Cache.setProperty(Preferences.SHOW_OV_HIDDEN_AT_START, "true");
+
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.fa", DataSourceType.FILE);
+
+    /*
+     * open and resize / reposition overview 
+     */
+    af.overviewMenuItem_actionPerformed(null);
+    OverviewPanel ov1 = af.alignPanel.getOverviewPanel();
+    assertNotNull(ov1);
+    ov1.setFrameBounds(20, 30, 200, 400);
+    assertEquals(ov1.getTitle(), "Overview examples/uniref50.fa");
+    assertTrue(ov1.isShowHiddenRegions());
+
+    /*
+     * open a New View and its Overview and reposition it
+     */
+    af.newView_actionPerformed(null);
+    af.overviewMenuItem_actionPerformed(null);
+    OverviewPanel ov2 = af.alignPanel.getOverviewPanel();
+    assertNotNull(ov2);
+    assertNotSame(ov1, ov2);
+    ov2.setFrameBounds(25, 35, 205, 405);
+    assertEquals(ov1.getTitle(), "Overview examples/uniref50.fa Original");
+    assertEquals(ov2.getTitle(), "Overview examples/uniref50.fa View 1");
+
+    File tfile = File.createTempFile("testStoreAndRecoverOverview", ".jvp");
+    new Jalview2XML(false).saveState(tfile);
+    Desktop.instance.closeAll_actionPerformed(null);
+
+    /*
+     * change preferences (should _not_ affect reloaded Overviews)
+     */
+    Cache.setProperty("SHOW_OVERVIEW", "true");
+    Cache.setProperty(Preferences.USE_LEGACY_GAP, "true");
+    Cache.setColourProperty(Preferences.GAP_COLOUR, Color.blue);
+    Cache.setColourProperty(Preferences.HIDDEN_COLOUR, Color.orange);
+    Cache.setProperty(Preferences.SHOW_OV_HIDDEN_AT_START, "false");
+
+    af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
+            DataSourceType.FILE);
+
+    /*
+     * workaround: explicitly select View 1 (not in focus after restore)
+     */
+    af.tabSelectionChanged(1);
+
+    /*
+     * verify restored overview for View 1
+     */
+    ov2 = af.alignPanel.getOverviewPanel();
+    assertEquals(ov2.getCanvas().getGapColour(), Color.green);
+    // 'non-legacy' colouring uses white for non-gapped residues
+    assertEquals(ov2.getCanvas().getResidueColour(), Color.white);
+    assertEquals(ov2.getCanvas().getHiddenColour(), Color.yellow);
+    assertEquals(ov2.getTitle(), "Overview examples/uniref50.fa View 1");
+    assertEquals(ov2.getFrameBounds(), new Rectangle(25, 35, 205, 405));
+    assertTrue(ov2.isShowHiddenRegions());
+
+    /*
+     * verify restored overview for Original view
+     */
+    af.tabSelectionChanged(0);
+    ov1 = af.alignPanel.getOverviewPanel();
+    assertEquals(ov1.getCanvas().getGapColour(), Color.green);
+    // 'non-legacy' colouring uses white for non-gapped residues
+    assertEquals(ov1.getCanvas().getResidueColour(), Color.white);
+    assertEquals(ov1.getCanvas().getHiddenColour(), Color.yellow);
+    assertEquals(ov1.getTitle(), "Overview examples/uniref50.fa Original");
+    assertEquals(ov1.getFrameBounds(), new Rectangle(20, 30, 200, 400));
+    assertTrue(ov1.isShowHiddenRegions());
+  }
+
+  /**
+   * Test that a view with no Overview is restored with no Overview, even if
+   * 'Open Overview' is selected in Preferences
+   * 
+   * @throws Exception
+   */
+  @Test(groups = { "Functional" }, enabled = true)
+  public void testStoreAndRecoverNoOverview() throws Exception
+  {
+    Cache.setProperty("SHOW_OVERVIEW", "false");
+    Desktop.instance.closeAll_actionPerformed(null);
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            ">seq1\nMATRSQFLVNF\n", DataSourceType.PASTE);
+
+    File tfile = File.createTempFile("testStoreAndRecoverOverview", ".jvp");
+    new Jalview2XML(false).saveState(tfile);
+    Desktop.instance.closeAll_actionPerformed(null);
+
+    Cache.setProperty("SHOW_OVERVIEW", "true");
+    af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
+            DataSourceType.FILE);
+
+    assertNull(af.alignPanel.getOverviewPanel());
+  }
+
+  /**
+   * Test that a view from an older version of Jalview is restored with Overview automatically shown when the preference is set
+   * 
+   * @throws Exception
+   */
+  @Test(groups = { "Functional" }, enabled = true)
+  public void testAutoShowOverviewForLegacyProjects() throws Exception
+  {
+    Desktop.instance.closeAll_actionPerformed(null);
+    Cache.setProperty("SHOW_OVERVIEW", "true");
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/exampleFile.jvp", DataSourceType.FILE);
+
+    Cache.setProperty("SHOW_OVERVIEW", "false");
+    assertNotNull(af.alignPanel.getOverviewPanel());
+  }
+
+  /**
+   * Test that loading example.jvp, doing some stuff, then hitting reload
+   * doesn't leave the modified window still open
+   * 
+   * See JAL-4127 - interactively performing the same actions and reloading
+   * works fine, but programmatically they do not
+   * 
+   * @throws Exception
+   */
+  @Test(groups = {"Functional"}, enabled=false)
+  public void testReloadActuallyReloads() throws Exception
+  {
+    Desktop.instance.closeAll_actionPerformed(null);
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/exampleFile.jvp", DataSourceType.FILE);
+    af.getViewport().getColumnSelection().addElement(3);
+    af.hideSelColumns_actionPerformed(null);
+    af.newView("new", true);
+    af.reload_actionPerformed(null);
+    Thread.sleep(30);
+    // af exists still but isn't shown
+    assertTrue(af.isClosed());
+  }
+
+  @Test(groups = { "Functional" })
+  public void testPAEsaveRestore() throws Exception
+  {
+    Desktop.instance.closeAll_actionPerformed(null);
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            ">seq1\nMATRSQFLVNF\n", DataSourceType.PASTE);
+    AlignmentI al = af.getViewport().getAlignment();
+    SequenceI sq = al.getSequenceAt(0);
+    int i = sq.getLength();
+    float[][] paevals = new float[i][i];
+    for (i = i - 1; i >= 0; i--)
+    {
+      for (int j = 0; j <= i; j++)
+      {
+        paevals[i][j] = ((i - j < 2)
+                || ((i > 1 && i < 5) && (j > 1 && i < 5))) ? 1 : 0f;
+        paevals[j][i] = paevals[i][j];
+      }
+    }
+    PAEContactMatrix dummyMat = new PAEContactMatrix(sq, paevals);
+    AlignmentAnnotation paeCm = sq.addContactList(dummyMat);
+    al.addAnnotation(paeCm);
+    File tfile = File.createTempFile("testStoreAndRecoverPAEmatrix",
+            ".jvp");
+    new Jalview2XML(false).saveState(tfile);
+    Desktop.instance.closeAll_actionPerformed(null);
+
+    af = new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
+            DataSourceType.FILE);
+    AlignmentI newAl = af.getViewport().getAlignment();
+    SequenceI newSeq = newAl.getSequenceAt(0);
+    // check annotation of the expected type exists
+    Assert.assertEquals(newSeq.getAnnotation().length, 1);
+    Assert.assertEquals(newSeq.getAnnotation()[0].graph, paeCm.graph);
+
+    // check a contact matrix was recovered
+    Assert.assertEquals(newSeq.getContactMaps().size(), 1);
+    // and can be found for the annotation
+    ContactMatrixI restoredMat = al
+            .getContactMatrixFor(newSeq.getAnnotation()[0]);
+    Assert.assertNotNull(restoredMat);
+    for (i = sq.getLength() - 1; i >= 0; i--)
+    {
+      ContactListI oldCM = dummyMat.getContactList(i),
+              newCM = restoredMat.getContactList(i);
+      for (int j = oldCM.getContactHeight(); j >= 0; j--)
+      {
+        Assert.assertEquals(oldCM.getContactAt(j), newCM.getContactAt(j));
+      }
+    }
+  }
+
 }