JAL-3390 unit tests for buildShowStructuresCommand() (Jmol and Chimera)
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 21 Aug 2019 10:42:11 +0000 (11:42 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Wed, 21 Aug 2019 10:42:11 +0000 (11:42 +0100)
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/structures/models/AAStructureBindingModel.java
src/jalview/util/StructureCommands.java
test/jalview/ext/jmol/JalviewJmolBindingTest.java [new file with mode: 0644]
test/jalview/ext/rbvi/chimera/JalviewChimeraBindingTest.java [new file with mode: 0644]

index 6da0eb5..3af7279 100644 (file)
@@ -77,7 +77,10 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
   Vector<String> atomsPicked = new Vector<>();
 
-  Hashtable<String, String> chainFile;
+  /*
+   * lookup map of { pdbId:chainId, pdbFilepath }
+   */
+  Map<String, String> chainFile;
 
   /*
    * the default or current model displayed if the model cannot be identified
@@ -599,26 +602,27 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   @Override
   public synchronized String[] getStructureFiles()
   {
+    if (modelFileNames != null)
+    {
+      return modelFileNames;
+    }
     if (viewer == null)
     {
       return new String[0];
     }
 
-    if (modelFileNames == null)
+    List<String> mset = new ArrayList<>();
+    int modelCount = viewer.ms.mc;
+    String filePath = null;
+    for (int i = 0; i < modelCount; ++i)
     {
-      List<String> mset = new ArrayList<>();
-      int modelCount = viewer.ms.mc;
-      String filePath = null;
-      for (int i = 0; i < modelCount; ++i)
+      filePath = viewer.ms.getModelFileName(i);
+      if (!mset.contains(filePath))
       {
-        filePath = viewer.ms.getModelFileName(i);
-        if (!mset.contains(filePath))
-        {
-          mset.add(filePath);
-        }
+        mset.add(filePath);
       }
-      modelFileNames = mset.toArray(new String[mset.size()]);
     }
+    modelFileNames = mset.toArray(new String[mset.size()]);
 
     return modelFileNames;
   }
index fdd07f6..8a19d53 100644 (file)
@@ -545,18 +545,19 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   @Override
   public String getModelSpec(int pdbfnum)
   {
-    if (pdbfnum < 0 || pdbfnum >= getPdbCount())
-    {
-      return "";
-    }
-
     /*
      * For now, the test for having sub-models is whether multiple Chimera
      * models are mapped for the PDB file; the models are returned as a response
      * to the Chimera command 'list models type molecule', see
      * ChimeraManager.getModelList().
      */
-    List<ChimeraModel> maps = chimeraMaps.get(getStructureFiles()[pdbfnum]);
+    String[] structureFiles = getStructureFiles();
+    if (pdbfnum < 0 || pdbfnum >= structureFiles.length)
+    {
+      return "";
+    }
+
+    List<ChimeraModel> maps = chimeraMaps.get(structureFiles[pdbfnum]);
     boolean hasSubModels = maps != null && maps.size() > 1;
     String spec = "#" + String.valueOf(pdbfnum);
     return hasSubModels ? spec + ".1" : spec;
index be4333e..19b72b9 100644 (file)
@@ -90,8 +90,9 @@ public abstract class AAStructureBindingModel
 
   private boolean finishedInit = false;
 
-  /**
-   * current set of model filenames loaded in the Jmol instance
+  /*
+   * current set of model filenames loaded in the Jmol instance 
+   * array index 0, 1, 2... corresponds to Jmol model numbers 1, 2, 3...
    */
   protected String[] modelFileNames = null;
 
index fb52340..b45ca5d 100644 (file)
@@ -61,8 +61,8 @@ public abstract class StructureCommands
   }
 
   /**
-   * Build a data structure which records contiguous subsequences by model and
-   * chain. From this we can easily generate the Chimera or Jmol specific
+   * Build a data structure which records contiguous subsequences by colour, model
+   * and chain. From this we can easily generate the Chimera or Jmol specific
    * selection expression.
    * 
    * <pre>
diff --git a/test/jalview/ext/jmol/JalviewJmolBindingTest.java b/test/jalview/ext/jmol/JalviewJmolBindingTest.java
new file mode 100644 (file)
index 0000000..0a0b634
--- /dev/null
@@ -0,0 +1,160 @@
+package jalview.ext.jmol;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
+import jalview.gui.AppJmolBinding;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureSelectionManager;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import junit.extensions.PA;
+
+public class JalviewJmolBindingTest
+{
+  private AlignFrame af;
+
+  @BeforeTest(alwaysRun = true)
+  public void setup()
+  {
+    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
+            DataSourceType.FILE);
+  }
+
+  @Test(groups = "Functional")
+  public void testBuildShowStructuresCommand()
+  {
+    AlignViewport av = af.getViewport();
+    PDBEntry[] pdbs = new PDBEntry[] {};
+    SequenceI seq1 = av.getAlignment().findSequenceMatch("FER1_SPIOL")[0];
+    assertNotNull(seq1);
+    SequenceI seq2 = av.getAlignment().findSequenceMatch("FER2_ARATH")[0];
+    assertNotNull(seq2);
+    StructureSelectionManager ssm = new StructureSelectionManager();
+    SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
+    AppJmolBinding testee = new AppJmolBinding(null, ssm, pdbs, seqs,
+            null);
+
+    /*
+     * map FER1_SPIOL residues 51-100 to residues 1-50 (atoms 1-250) in 1A70
+     * and residues 110-147 to structure residues 60-97
+     * (in fact there is no gap, added here for test purposes)
+     */
+    HashMap<Integer, int[]> map = new HashMap<>();
+    for (int pos = 51; pos <= 100; pos++)
+    {
+      map.put(pos, new int[] { pos - 50, 5 * (pos - 50) });
+    }
+    for (int pos = 110; pos <= 147; pos++)
+    {
+      map.put(pos, new int[] { pos - 50, 5 * (pos - 50) });
+    }
+    StructureMapping sm1 = new StructureMapping(seq1, "1a70.pdb", "1A70",
+            "A", map, null);
+    ssm.addStructureMapping(sm1);
+
+    /*
+     * map FER2_ARATH residues 53-148 to residues 2-97 in 4ZHO
+     */
+    map = new HashMap<>();
+    for (int pos = 53; pos <= 148; pos++)
+    {
+      map.put(pos, new int[] { pos - 51, 5 * (pos - 51) });
+    }
+    StructureMapping sm2 = new StructureMapping(seq2, "4zho.pdb", "4ZHO",
+            "B", map, null);
+    ssm.addStructureMapping(sm2);
+
+    /*
+     * show everything
+     */
+    String cmd = testee.buildShowStructuresCommand(av, true);
+    assertEquals(cmd, "display *; cartoon only; zoom 0");
+    cmd = testee.buildShowStructuresCommand(av, false);
+    assertEquals(cmd, "display *; cartoon only");
+
+    /*
+     * stub out modelFileNames - array index is Jmol
+     * model number - 1
+     */
+    PA.setValue(testee, "modelFileNames",
+            new String[]
+            { "1a70.pdb", "4zho.pdb" });
+
+    /*
+     * stub out lookup map from pdb:chain to filename
+     */
+    Map<String, String> chainFiles = new HashMap<>();
+    PA.setValue(testee, "chainFile", chainFiles);
+    chainFiles.put("1A70:A", "1a70.pdb");
+    chainFiles.put("1A70:B", "1a70.pdb");
+    chainFiles.put("4ZHO:B", "4zho.pdb");
+    chainFiles.put("4ZHO:C", "4zho.pdb");
+
+    /*
+     * show all except for selected chains to hide
+     */
+    List<String> chainsToHide = (List<String>) PA.getValue(testee,
+            "chainsToHide");
+    chainsToHide.add("1A70:B");
+    chainsToHide.add("4ZHO:C");
+    cmd = testee.buildShowStructuresCommand(av, true);
+    assertEquals(cmd,
+            "display *; hide add :B/1.1,:C/2.1; cartoon only; zoom 0");
+
+    /*
+     * show alignment only, no chains hidden
+     */
+    chainsToHide.clear();
+    testee.setShowAlignmentOnly(true);
+    cmd = testee.buildShowStructuresCommand(av, false);
+    assertEquals(cmd,
+            "hide *;display (1-50,60-97)&:A/1.1,2-97:B/2.1; select displayed; cartoon only");
+
+    /*
+     * now with a chain hidden
+     */
+    chainsToHide.add("4ZHO:C");
+    cmd = testee.buildShowStructuresCommand(av, false);
+    String expected = "hide *;display (1-50,60-97)&:A/1.1,2-97:B/2.1; select displayed; hide add :C/2.1; cartoon only";
+    assertEquals(cmd, expected);
+
+    /*
+     * hide columns in the mapped region - should not change the command (yet)
+     */
+    int fromCol = seq1.findIndex(60); // structure residue 10
+    int toCol = seq1.findIndex(70); // structure residue 20
+    av.hideColumns(fromCol - 1, toCol - 1);
+    cmd = testee.buildShowStructuresCommand(av, false);
+    assertEquals(cmd, expected);
+
+    /*
+     * select 'hide hidden columns'
+     * command should now exclude these in both mapped sequences
+     */
+    testee.setHideHiddenRegions(true);
+    cmd = testee.buildShowStructuresCommand(av, false);
+    expected = "hide *;display (1-9,21-50,60-97)&:A/1.1,(2-10,22-97)&:B/2.1; select displayed; hide add :C/2.1; cartoon only";
+    assertEquals(cmd, expected);
+
+    /*
+     * deselect 'show alignment only'
+     * hide hidden columns is now ignored
+     */
+    testee.setShowAlignmentOnly(false);
+    cmd = testee.buildShowStructuresCommand(av, false);
+    assertEquals(cmd, "display *; hide add :C/2.1; cartoon only");
+  }
+}
diff --git a/test/jalview/ext/rbvi/chimera/JalviewChimeraBindingTest.java b/test/jalview/ext/rbvi/chimera/JalviewChimeraBindingTest.java
new file mode 100644 (file)
index 0000000..2604243
--- /dev/null
@@ -0,0 +1,164 @@
+package jalview.ext.rbvi.chimera;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignViewport;
+import jalview.gui.ChimeraViewFrame;
+import jalview.gui.JalviewChimeraBindingModel;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureSelectionManager;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel;
+import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType;
+import junit.extensions.PA;
+
+public class JalviewChimeraBindingTest
+{
+  private AlignFrame af;
+
+  @BeforeTest(alwaysRun = true)
+  public void setup()
+  {
+    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
+            DataSourceType.FILE);
+  }
+
+  @Test(groups = "Functional")
+  public void testBuildShowStructuresCommand()
+  {
+    AlignViewport av = af.getViewport();
+    PDBEntry[] pdbs = new PDBEntry[] {};
+    StructureSelectionManager ssm = new StructureSelectionManager();
+    SequenceI seq1 = av.getAlignment().findSequenceMatch("FER1_SPIOL")[0];
+    assertNotNull(seq1);
+    SequenceI seq2 = av.getAlignment().findSequenceMatch("FER2_ARATH")[0];
+    assertNotNull(seq2);
+    SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
+    JalviewChimeraBindingModel testee = new JalviewChimeraBindingModel(
+            new ChimeraViewFrame(),
+            ssm, pdbs, seqs, null);
+
+    /*
+     * with no structures mapped
+     */
+    String cmd = testee.buildShowStructuresCommand(av, true);
+    assertEquals(cmd, "~display; ribbon; focus");
+    cmd = testee.buildShowStructuresCommand(av, false);
+    assertEquals(cmd, "~display; ribbon");
+
+    /*
+     * stub out a structure with chains A and B
+     */
+    Map<String, List<ChimeraModel>> chimeraMaps = (Map<String, List<ChimeraModel>>) PA
+            .getValue(testee, "chimeraMaps");
+    ChimeraModel model0 = new ChimeraModel("1A70", ModelType.PDB_MODEL, 0,
+            0);
+    chimeraMaps.put("1a70.pdb", Arrays.asList(model0));
+    ChimeraModel model1 = new ChimeraModel("4ZHO", ModelType.PDB_MODEL, 1,
+            0);
+    chimeraMaps.put("4zho.pdb", Arrays.asList(model1));
+
+    Map<String, String> chainFiles = (Map<String, String>) PA
+            .getValue(testee, "chainFile");
+    chainFiles.put("1A70:A", "1a70.pdb");
+    chainFiles.put("1A70:B", "1a70.pdb");
+    chainFiles.put("4ZHO:B", "4zho.pdb");
+    chainFiles.put("4ZHO:C", "4zho.pdb");
+
+    /*
+     * map FER1_SPIOL residues 51-100 to residues 1-50 (atoms 1-250) in 1A70
+     * and residues 110-147 to structure residues 60-97
+     * (in fact there is no gap, added here for test purposes)
+     */
+    HashMap<Integer, int[]> map = new HashMap<>();
+    for (int pos = 51; pos <= 100; pos++)
+    {
+      map.put(pos, new int[] { pos - 50, 5 * (pos - 50) });
+    }
+    for (int pos = 110; pos <= 147; pos++)
+    {
+      map.put(pos, new int[] { pos - 50, 5 * (pos - 50) });
+    }
+    StructureMapping sm1 = new StructureMapping(seq1, "1a70.pdb", "1A70",
+            "A", map, null);
+    ssm.addStructureMapping(sm1);
+
+    /*
+     * map FER2_ARATH residues 53-148 to residues 2-97 in 4ZHO
+     */
+    map = new HashMap<>();
+    for (int pos = 53; pos <= 148; pos++)
+    {
+      map.put(pos, new int[] { pos - 51, 5 * (pos - 51) });
+    }
+    StructureMapping sm2 = new StructureMapping(seq2, "4zho.pdb", "4ZHO",
+            "B", map, null);
+    ssm.addStructureMapping(sm2);
+
+    /*
+     * select chain A only (hide chain B)
+     */
+    List<String> chainsToHide = (List<String>) PA.getValue(testee, "chainsToHide");
+    chainsToHide.add("1A70:B");
+    chainsToHide.add("4ZHO:C");
+    cmd = testee.buildShowStructuresCommand(av, false);
+    assertEquals(cmd, "~display; ribbon; ~ribbon #0:.B; ~ribbon #1:.C");
+
+    /*
+     * show alignment only, no chains hidden
+     */
+    chainsToHide.clear();
+    testee.setShowAlignmentOnly(true);
+    cmd = testee.buildShowStructuresCommand(av, false);
+    assertEquals(cmd,
+            "~display; ~ribbon; ribbon #0:1-50.A,60-97.A|#1:2-97.B");
+
+    /*
+     * now with a chain hidden
+     */
+    chainsToHide.add("4ZHO:C");
+    cmd = testee.buildShowStructuresCommand(av, false);
+    String expected = "~display; ~ribbon; ribbon #0:1-50.A,60-97.A|#1:2-97.B; ~ribbon #1:.C";
+    assertEquals(cmd, expected);
+
+    /*
+     * hide columns in the mapped region - should not change the command (yet)
+     */
+    int fromCol = seq1.findIndex(60); // structure residue 10
+    int toCol = seq1.findIndex(70); // structure residue 20
+    av.hideColumns(fromCol - 1, toCol - 1);
+    cmd = testee.buildShowStructuresCommand(av, false);
+    assertEquals(cmd, expected);
+
+    /*
+     * select 'hide hidden columns'
+     * command should now exclude these in both mapped sequences
+     */
+    testee.setHideHiddenRegions(true);
+    cmd = testee.buildShowStructuresCommand(av, false);
+    expected = "~display; ~ribbon; ribbon #0:1-9.A,21-50.A,60-97.A|#1:2-10.B,22-97.B; ~ribbon #1:.C";
+    assertEquals(cmd, expected);
+
+    /*
+     * deselect 'show alignment only'
+     * hide hidden columns is now ignored
+     */
+    testee.setShowAlignmentOnly(false);
+    cmd = testee.buildShowStructuresCommand(av, false);
+    assertEquals(cmd, "~display; ribbon; ~ribbon #1:.C");
+  }
+}