Merge branch 'features/JAL-1767pcaInProject' into bug/JAL-3171_maintain_datasets_acro...
[jalview.git] / test / jalview / io / Jalview2xmlTests.java
index 6abb7e5..3baacc8 100644 (file)
@@ -23,40 +23,60 @@ package jalview.io;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
+import jalview.analysis.PCA;
 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.HiddenSequences;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.PDBEntry.Type;
+import jalview.datamodel.Point;
 import jalview.datamodel.SequenceCollectionI;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.SequencePoint;
+import jalview.datamodel.features.FeatureMatcher;
+import jalview.datamodel.features.FeatureMatcherSet;
+import jalview.datamodel.features.FeatureMatcherSetI;
 import jalview.gui.AlignFrame;
 import jalview.gui.AlignViewport;
 import jalview.gui.AlignmentPanel;
+import jalview.gui.CalculationChooser;
 import jalview.gui.Desktop;
+import jalview.gui.FeatureRenderer;
 import jalview.gui.Jalview2XML;
 import jalview.gui.JvOptionPane;
+import jalview.gui.PCAPanel;
 import jalview.gui.PopupMenu;
+import jalview.gui.RotatableCanvas;
 import jalview.gui.SliderPanel;
+import jalview.math.MatrixTest;
 import jalview.renderer.ResidueShaderI;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.BuriedColourScheme;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ColourSchemeProperty;
+import jalview.schemes.FeatureColour;
 import jalview.schemes.JalviewColourScheme;
 import jalview.schemes.RNAHelicesColour;
 import jalview.schemes.StrandColourScheme;
 import jalview.schemes.TCoffeeColourScheme;
 import jalview.structure.StructureImportSettings;
+import jalview.util.matcher.Condition;
 import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.PCAModel;
 
+import java.awt.Color;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -64,14 +84,31 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.swing.JInternalFrame;
+import javax.swing.JRadioButton;
+
 import org.testng.Assert;
 import org.testng.AssertJUnit;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import junit.extensions.PA;
+
 @Test(singleThreaded = true)
 public class Jalview2xmlTests extends Jalview2xmlBase
 {
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+  }
+
+  @BeforeMethod(alwaysRun = true)
+  public void setUpMethod()
+  {
+    Desktop.instance.closeAll_actionPerformed(null);
+  }
 
   @Override
   @BeforeClass(alwaysRun = true)
@@ -247,6 +284,31 @@ public class Jalview2xmlTests extends Jalview2xmlBase
 
   }
 
+  /**
+   * Test for JAL-2223 - multiple mappings in View Mapping report
+   * 
+   * @throws Exception
+   */
+  @Test(groups = { "Functional" })
+  public void noDuplicatePdbMappingsMade() throws Exception
+  {
+    StructureImportSettings.setProcessSecondaryStructure(true);
+    StructureImportSettings.setVisibleChainAnnotation(true);
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/exampleFile_2_7.jar", DataSourceType.FILE);
+    assertNotNull(af, "Didn't read in the example file correctly.");
+
+    // locate Jmol viewer
+    // count number of PDB mappings the structure selection manager holds -
+    String pdbFile = af.getCurrentView().getStructureSelectionManager()
+            .findFileForPDBId("1A70");
+    assertEquals(
+            af.getCurrentView().getStructureSelectionManager()
+                    .getMapping(pdbFile).length,
+            2, "Expected only two mappings for 1A70");
+
+  }
+
   @Test(groups = { "Functional" })
   public void viewRefPdbAnnotation() throws Exception
   {
@@ -413,7 +475,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     String afid = af.getViewport().getSequenceSetId();
 
     // remember reference sequence for each panel
-    Map<String, SequenceI> refseqs = new HashMap<String, SequenceI>();
+    Map<String, SequenceI> refseqs = new HashMap<>();
 
     /*
      * mark sequence 2, 3, 4.. in panels 1, 2, 3...
@@ -551,8 +613,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
      * remember representative and hidden sequences marked 
      * on each panel
      */
-    Map<String, SequenceI> repSeqs = new HashMap<String, SequenceI>();
-    Map<String, List<String>> hiddenSeqNames = new HashMap<String, List<String>>();
+    Map<String, SequenceI> repSeqs = new HashMap<>();
+    Map<String, List<String>> hiddenSeqNames = new HashMap<>();
 
     /*
      * mark sequence 2, 3, 4.. in panels 1, 2, 3...
@@ -568,7 +630,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
       repIndex = Math.max(repIndex, 1);
       SequenceI repSeq = alignment.getSequenceAt(repIndex);
       repSeqs.put(ap.getViewName(), repSeq);
-      List<String> hiddenNames = new ArrayList<String>();
+      List<String> hiddenNames = new ArrayList<>();
       hiddenSeqNames.put(ap.getViewName(), hiddenNames);
 
       /*
@@ -841,4 +903,293 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     assertTrue(rs.conservationApplied());
     assertEquals(rs.getConservationInc(), 30);
   }
+
+  /**
+   * Test save and reload of feature colour schemes and filter settings
+   * 
+   * @throws IOException
+   */
+  @Test(groups = { "Functional" })
+  public void testSaveLoadFeatureColoursAndFilters() throws IOException
+  {
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            ">Seq1\nACDEFGHIKLM", DataSourceType.PASTE);
+    SequenceI seq1 = af.getViewport().getAlignment().getSequenceAt(0);
+
+    /*
+     * add some features to the sequence
+     */
+    int score = 1;
+    addFeatures(seq1, "type1", score++);
+    addFeatures(seq1, "type2", score++);
+    addFeatures(seq1, "type3", score++);
+    addFeatures(seq1, "type4", score++);
+    addFeatures(seq1, "type5", score++);
+
+    /*
+     * set colour schemes for features
+     */
+    FeatureRenderer fr = af.getFeatureRenderer();
+    fr.findAllFeatures(true);
+
+    // type1: red
+    fr.setColour("type1", new FeatureColour(Color.red));
+
+    // type2: by label
+    FeatureColourI byLabel = new FeatureColour();
+    byLabel.setColourByLabel(true);
+    fr.setColour("type2", byLabel);
+
+    // type3: by score above threshold
+    FeatureColourI byScore = new FeatureColour(Color.BLACK, Color.BLUE, 1,
+            10);
+    byScore.setAboveThreshold(true);
+    byScore.setThreshold(2f);
+    fr.setColour("type3", byScore);
+
+    // type4: by attribute AF
+    FeatureColourI byAF = new FeatureColour();
+    byAF.setColourByLabel(true);
+    byAF.setAttributeName("AF");
+    fr.setColour("type4", byAF);
+
+    // type5: by attribute CSQ:PolyPhen below threshold
+    FeatureColourI byPolyPhen = new FeatureColour(Color.BLACK, Color.BLUE,
+            1, 10);
+    byPolyPhen.setBelowThreshold(true);
+    byPolyPhen.setThreshold(3f);
+    byPolyPhen.setAttributeName("CSQ", "PolyPhen");
+    fr.setColour("type5", byPolyPhen);
+
+    /*
+     * set filters for feature types
+     */
+
+    // filter type1 features by (label contains "x")
+    FeatureMatcherSetI filterByX = new FeatureMatcherSet();
+    filterByX.and(FeatureMatcher.byLabel(Condition.Contains, "x"));
+    fr.setFeatureFilter("type1", filterByX);
+
+    // filter type2 features by (score <= 2.4 and score > 1.1)
+    FeatureMatcherSetI filterByScore = new FeatureMatcherSet();
+    filterByScore.and(FeatureMatcher.byScore(Condition.LE, "2.4"));
+    filterByScore.and(FeatureMatcher.byScore(Condition.GT, "1.1"));
+    fr.setFeatureFilter("type2", filterByScore);
+
+    // filter type3 features by (AF contains X OR CSQ:PolyPhen != 0)
+    FeatureMatcherSetI filterByXY = new FeatureMatcherSet();
+    filterByXY
+            .and(FeatureMatcher.byAttribute(Condition.Contains, "X", "AF"));
+    filterByXY.or(FeatureMatcher.byAttribute(Condition.NE, "0", "CSQ",
+            "PolyPhen"));
+    fr.setFeatureFilter("type3", filterByXY);
+
+    /*
+     * save as Jalview project
+     */
+    File tfile = File.createTempFile("JalviewTest", ".jvp");
+    tfile.deleteOnExit();
+    String filePath = tfile.getAbsolutePath();
+    assertTrue(af.saveAlignment(filePath, FileFormat.Jalview),
+            "Failed to store as a project.");
+
+    /*
+     * close current alignment and load the saved project
+     */
+    af.closeMenuItem_actionPerformed(true);
+    af = null;
+    af = new FileLoader()
+            .LoadFileWaitTillLoaded(filePath, DataSourceType.FILE);
+    assertNotNull(af, "Failed to import new project");
+
+    /*
+     * verify restored feature colour schemes and filters
+     */
+    fr = af.getFeatureRenderer();
+    FeatureColourI fc = fr.getFeatureStyle("type1");
+    assertTrue(fc.isSimpleColour());
+    assertEquals(fc.getColour(), Color.red);
+    fc = fr.getFeatureStyle("type2");
+    assertTrue(fc.isColourByLabel());
+    fc = fr.getFeatureStyle("type3");
+    assertTrue(fc.isGraduatedColour());
+    assertNull(fc.getAttributeName());
+    assertTrue(fc.isAboveThreshold());
+    assertEquals(fc.getThreshold(), 2f);
+    fc = fr.getFeatureStyle("type4");
+    assertTrue(fc.isColourByLabel());
+    assertTrue(fc.isColourByAttribute());
+    assertEquals(fc.getAttributeName(), new String[] { "AF" });
+    fc = fr.getFeatureStyle("type5");
+    assertTrue(fc.isGraduatedColour());
+    assertTrue(fc.isColourByAttribute());
+    assertEquals(fc.getAttributeName(), new String[] { "CSQ", "PolyPhen" });
+    assertTrue(fc.isBelowThreshold());
+    assertEquals(fc.getThreshold(), 3f);
+
+    assertEquals(fr.getFeatureFilter("type1").toStableString(),
+            "Label Contains x");
+    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)");
+  }
+
+  private void addFeature(SequenceI seq, String featureType, int score)
+  {
+    SequenceFeature sf = new SequenceFeature(featureType, "desc", 1, 2,
+            score, "grp");
+    sf.setValue("AF", score);
+    sf.setValue("CSQ", new HashMap<String, String>()
+    {
+      {
+        put("PolyPhen", Integer.toString(score));
+      }
+    });
+    seq.addSequenceFeature(sf);
+  }
+
+  /**
+   * Adds two features of the given type to the given sequence, also setting the
+   * score as the value of attribute "AF" and sub-attribute "CSQ:PolyPhen"
+   * 
+   * @param seq
+   * @param featureType
+   * @param score
+   */
+  private void addFeatures(SequenceI seq, String featureType, int score)
+  {
+    addFeature(seq, featureType, score++);
+    addFeature(seq, featureType, score);
+  }
+
+  /**
+   * Test save and reload of a PCA viewer
+   * 
+   * @throws IOException
+   */
+  @Test(groups = { "Functional" })
+  public void testSaveLoadPCA() throws IOException
+  {
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            "examples/uniref50.fa", DataSourceType.FILE);
+    assertEquals(af.getViewport().getAlignment().getHeight(), 15);
+
+    /*
+     * calculate and open PCA (calculates in a separate thread)
+     */
+    CalculationChooser chooser = new CalculationChooser(af);
+    ((JRadioButton) PA.getValue(chooser, "pca")).setSelected(true);
+    PA.invokeMethod(chooser, "calculate_actionPerformed()");
+    PCAPanel pcaPanel = (PCAPanel) PA.getValue(chooser, "pcaPanel");
+    assertNotNull(pcaPanel);
+    waitFor(50); // let it get started!!
+    while (pcaPanel.isWorking())
+    {
+      waitFor(50);
+    }
+    PA.invokeMethod(chooser, "close_actionPerformed()");
+
+    /*
+     * rotate, zoom in, change background colour
+     */
+    RotatableCanvas rc = (RotatableCanvas) PA.getValue(pcaPanel, "rc");
+    PA.setValue(rc, "bgColour", Color.PINK);
+    rc.zoom(1.9f);
+    rc.rotate(20, 40f);
+
+    /*
+     * save as Jalview project
+     */
+    File tfile = File.createTempFile("JalviewTest", ".jvp");
+    tfile.deleteOnExit();
+    String filePath = tfile.getAbsolutePath();
+    assertTrue(af.saveAlignment(filePath, FileFormat.Jalview),
+            "Failed to store as a project.");
+  
+    /*
+     * load the saved project and locate the restored PCA panel
+     */
+    new FileLoader().LoadFileWaitTillLoaded(filePath, DataSourceType.FILE);
+    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    PCAPanel pcaPanel2 = null;
+    for (JInternalFrame frame : frames)
+    {
+      if (frame instanceof PCAPanel && frame != pcaPanel)
+      {
+        pcaPanel2 = (PCAPanel) frame;
+      }
+    }
+    assertNotNull(pcaPanel2);
+
+    /*
+     * compare restored and original PCA
+     */
+    PCAModel pcaModel = (PCAModel) PA.getValue(pcaPanel, "pcaModel");
+    PCAModel pcaModel2 = (PCAModel) PA.getValue(pcaPanel2, "pcaModel");
+    RotatableCanvas rc2 = (RotatableCanvas) PA.getValue(pcaPanel2, "rc");
+    assertNotNull(pcaModel);
+    assertNotNull(pcaModel2);
+    assertNotNull(rc2);
+    assertEquals(rc2.getBackgroundColour(), Color.PINK);
+    assertEquals(PA.getValue(rc2, "scaleFactor"), 1.9f);
+
+    // original has input data
+    assertNotNull(pcaModel.getInputData());
+    // restored has no input data (JAL-2647 to do)
+    assertNull(pcaModel2.getInputData());
+
+    // verify sequence points are at the same positions
+    List<SequencePoint> seqPts = pcaModel.getSequencePoints();
+    List<SequencePoint> seqPts2 = pcaModel2.getSequencePoints();
+    assertEquals(seqPts.size(), seqPts2.size());
+    for (int i = 0; i < seqPts.size(); i++)
+    {
+      SequencePoint sp = seqPts.get(i);
+      SequencePoint sp2 = seqPts2.get(i);
+      assertEquals(sp.getSequence().getName(), sp2.getSequence().getName());
+      assertEquals(sp.coord, sp2.coord);
+    }
+
+    // verify axis end points are at the same positions
+    Point[] axes = (Point[]) PA.getValue(rc, "axisEndPoints");
+    Point[] axes2 = (Point[]) PA.getValue(rc2, "axisEndPoints");
+    assertEquals(axes.length, 3);
+    assertEquals(axes2.length, 3);
+    for (int i = 0; i < 3; i++)
+    {
+      assertEquals(axes[i], axes2[i]);
+    }
+
+    // compare PCA data
+    PCA pca = (PCA) PA.getValue(pcaModel, "pca");
+    PCA pca2 = (PCA) PA.getValue(pcaModel2, "pca");
+    assertNotNull(pca);
+    assertNotNull(pca2);
+    // same (BLOSUM62) score model (a singleton object)
+    assertSame(PA.getValue(pca, "scoreModel"),
+            PA.getValue(pca2, "scoreModel"));
+    assertEquals(PA.getValue(pca, "similarityParams"),
+            PA.getValue(pca2, "similarityParams"));
+    MatrixTest.assertMatricesMatch(pca.getPairwiseScores(),
+            pca2.getPairwiseScores());
+    MatrixTest.assertMatricesMatch(pca.getTridiagonal(),
+            pca2.getTridiagonal());
+    MatrixTest.assertMatricesMatch(pca.getEigenmatrix(),
+            pca2.getEigenmatrix());
+  }
+
+  protected void waitFor(long t)
+  {
+    synchronized (this)
+    {
+      try
+      {
+        wait(t);
+      } catch (InterruptedException e)
+      {
+        fail(e.getMessage());
+      }
+    }
+  }
 }