From 55bfdb07355198ae1a1a8b5e14933b4669113ca6 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Wed, 21 Aug 2019 11:42:11 +0100 Subject: [PATCH] JAL-3390 unit tests for buildShowStructuresCommand() (Jmol and Chimera) --- src/jalview/ext/jmol/JalviewJmolBinding.java | 28 ++-- .../ext/rbvi/chimera/JalviewChimeraBinding.java | 13 +- .../structures/models/AAStructureBindingModel.java | 5 +- src/jalview/util/StructureCommands.java | 4 +- test/jalview/ext/jmol/JalviewJmolBindingTest.java | 160 +++++++++++++++++++ .../rbvi/chimera/JalviewChimeraBindingTest.java | 164 ++++++++++++++++++++ 6 files changed, 352 insertions(+), 22 deletions(-) create mode 100644 test/jalview/ext/jmol/JalviewJmolBindingTest.java create mode 100644 test/jalview/ext/rbvi/chimera/JalviewChimeraBindingTest.java diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 6da0eb5..3af7279 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -77,7 +77,10 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel Vector atomsPicked = new Vector<>(); - Hashtable chainFile; + /* + * lookup map of { pdbId:chainId, pdbFilepath } + */ + Map 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 mset = new ArrayList<>(); + int modelCount = viewer.ms.mc; + String filePath = null; + for (int i = 0; i < modelCount; ++i) { - List 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; } diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index fdd07f6..8a19d53 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -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 maps = chimeraMaps.get(getStructureFiles()[pdbfnum]); + String[] structureFiles = getStructureFiles(); + if (pdbfnum < 0 || pdbfnum >= structureFiles.length) + { + return ""; + } + + List maps = chimeraMaps.get(structureFiles[pdbfnum]); boolean hasSubModels = maps != null && maps.size() > 1; String spec = "#" + String.valueOf(pdbfnum); return hasSubModels ? spec + ".1" : spec; diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java index be4333e..19b72b9 100644 --- a/src/jalview/structures/models/AAStructureBindingModel.java +++ b/src/jalview/structures/models/AAStructureBindingModel.java @@ -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; diff --git a/src/jalview/util/StructureCommands.java b/src/jalview/util/StructureCommands.java index fb52340..b45ca5d 100644 --- a/src/jalview/util/StructureCommands.java +++ b/src/jalview/util/StructureCommands.java @@ -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. * *
diff --git a/test/jalview/ext/jmol/JalviewJmolBindingTest.java b/test/jalview/ext/jmol/JalviewJmolBindingTest.java
new file mode 100644
index 0000000..0a0b634
--- /dev/null
+++ b/test/jalview/ext/jmol/JalviewJmolBindingTest.java
@@ -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 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 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 chainsToHide = (List) 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
index 0000000..2604243
--- /dev/null
+++ b/test/jalview/ext/rbvi/chimera/JalviewChimeraBindingTest.java
@@ -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> chimeraMaps = (Map>) 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 chainFiles = (Map) 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 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 chainsToHide = (List) 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");
+  }
+}
-- 
1.7.10.2