JAL-2295 test that verifies transfer of features to Chimera attributes
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 11 Nov 2016 08:52:53 +0000 (08:52 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 11 Nov 2016 08:52:53 +0000 (08:52 +0000)
test/jalview/ext/rbvi/chimera/JalviewChimeraView.java

index 93a98b8..870ed99 100644 (file)
  */
 package jalview.ext.rbvi.chimera;
 
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertTrue;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
 
+import jalview.api.FeatureRenderer;
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
+import jalview.bin.Jalview;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
 import jalview.gui.Preferences;
 import jalview.gui.StructureViewer;
 import jalview.gui.StructureViewer.ViewerType;
+import jalview.io.FileLoader;
 import jalview.io.FormatAdapter;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureSelectionManager;
+import jalview.structures.models.AAStructureBindingModel;
+
+import java.util.List;
+import java.util.Vector;
 
 import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -40,15 +56,24 @@ import org.testng.annotations.Test;
 public class JalviewChimeraView
 {
 
+  private JalviewStructureDisplayI chimeraViewer;
+
   /**
    * @throws java.lang.Exception
    */
   @BeforeClass(alwaysRun = true)
   public static void setUpBeforeClass() throws Exception
   {
-    jalview.bin.Jalview.main(new String[] {
+    Jalview.main(new String[] {
         "-noquestionnaire -nonews -props",
         "test/jalview/ext/rbvi/chimera/testProps.jvprops" });
+    Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
+            ViewerType.CHIMERA.name());
+    Cache.setProperty("SHOW_ANNOTATIONS", "false");
+    Cache.setProperty(Preferences.STRUCT_FROM_PDB, "false");
+    Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
+            ViewerType.CHIMERA.name());
+    Cache.setProperty("MAP_WITH_SIFTS", "true");
   }
 
   /**
@@ -57,57 +82,222 @@ public class JalviewChimeraView
   @AfterClass(alwaysRun = true)
   public static void tearDownAfterClass() throws Exception
   {
-    jalview.gui.Desktop.instance.closeAll_actionPerformed(null);
+    Desktop.instance.closeAll_actionPerformed(null);
+  }
+
+  @AfterMethod(alwaysRun = true)
+  public void tearDownAfterTest() throws Exception
+  {
+    if (chimeraViewer != null)
+    {
+      chimeraViewer.closeViewer(true);
+    }
   }
 
-  @Test(groups = { "Functional" })
+  /**
+   * Load 1GAQ and view the first structure for which a PDB id is found
+   */
+  @Test(groups = { "External", "Network" })
   public void testSingleSeqViewChimera()
   {
-    Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
-            ViewerType.CHIMERA.name());
     String inFile = "examples/1gaq.txt";
-    AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded(
-            inFile, FormatAdapter.FILE);
-    assertTrue("Didn't read input file " + inFile, af != null);
-    for (SequenceI sq : af.getViewport().getAlignment().getSequences())
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
+            FormatAdapter.FILE);
+    assertNotNull(af, "Failed to create AlignFrame");
+    SequenceI sq = af.getViewport().getAlignment().getSequenceAt(0);
+    assertEquals(sq.getName(), "1GAQ|A");
+    SequenceI dsq = sq.getDatasetSequence();
+    Vector<PDBEntry> pdbIds = dsq.getAllPDBEntries();
+    assertEquals(pdbIds.size(), 1);
+    PDBEntry pdbEntry = pdbIds.get(0);
+    assertEquals(pdbEntry.getId(), "1GAQ");
+    StructureViewer structureViewer = new StructureViewer(af.getViewport()
+            .getStructureSelectionManager());
+    chimeraViewer = structureViewer.viewStructures(pdbEntry,
+            new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
+    /*
+     * Wait for viewer load thread to complete
+     */
+    while (!chimeraViewer.getBinding().isFinishedInit())
     {
-      System.out.println("** sq=" + sq.getName());
-      SequenceI dsq = sq.getDatasetSequence();
-      while (dsq.getDatasetSequence() != null)
+      try
       {
-        dsq = dsq.getDatasetSequence();
-      }
-      if (dsq.getAllPDBEntries() != null
-              && dsq.getAllPDBEntries().size() > 0)
+        Thread.sleep(500);
+      } catch (InterruptedException e)
       {
-        for (int q = 0; q < dsq.getAllPDBEntries().size(); q++)
-        {
-          final StructureViewer structureViewer = new StructureViewer(af
-                  .getViewport().getStructureSelectionManager());
-          structureViewer.setViewerType(ViewerType.CHIMERA);
-          JalviewStructureDisplayI chimeraViewer = structureViewer
-                  .viewStructures(dsq.getAllPDBEntries().elementAt(q),
-                          new SequenceI[] { sq }, af.getCurrentView()
-                                  .getAlignPanel());
-          /*
-           * Wait for viewer load thread to complete
-           */
-          while (!chimeraViewer.getBinding().isFinishedInit())
-          {
-            try
-            {
-              Thread.sleep(500);
-            } catch (InterruptedException e)
-            {
-            }
-          }
-          assertEquals(1, chimeraViewer.getBinding().getPdbCount());
-          chimeraViewer.closeViewer(true);
-          // todo: break here means only once through this loop?
-          break;
-        }
-        break;
       }
     }
+    assertEquals(chimeraViewer.getBinding().getPdbCount(), 1);
+    chimeraViewer.closeViewer(true);
+    chimeraViewer = null;
+    return;
+  }
+
+  /**
+   * Test for writing Jalview features as attributes on mapped residues in
+   * Chimera
+   */
+  // External as this requires a local install of Chimera
+  // Network as fetch from PDB/SIFTS is involved TODO mock this
+  @Test(groups = { "External", "Network" })
+  public void testTransferFeatures()
+  {
+    String inFile = "examples/uniref50.fa";
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
+            FormatAdapter.FILE);
+    assertNotNull(af, "Failed to create AlignFrame");
+    SequenceI sq = af.getViewport().getAlignment().findName("FER2_ARATH");
+    assertNotNull(sq, "Didn't find FER2_ARATH");
+
+    /*
+     * need a Uniprot dbref for SIFTS mapping to work!!
+     */
+    sq.addDBRef(new DBRefEntry("UNIPROT", "0", "P16972", null));
+    PDBEntry pdbEntry = new PDBEntry("4ZHO", null, null, null);
+    // addPDBId() doesn't (yet) delegate to dataset sequence if there is one!
+    sq.getDatasetSequence().addPDBId(pdbEntry);
+    StructureViewer structureViewer = new StructureViewer(af.getViewport()
+            .getStructureSelectionManager());
+    chimeraViewer = structureViewer.viewStructures(pdbEntry,
+            new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
+
+    AAStructureBindingModel binding = chimeraViewer.getBinding();
+    do
+    {
+      try
+      {
+        Thread.sleep(500);
+      } catch (InterruptedException e)
+    {
+    }
+    } while (!binding.isFinishedInit());
+
+    assertEquals(binding.getPdbCount(), 1);
+
+    /*
+     * check mapping is (sequence) 53-145 to (structure) 2-94 A/B
+     * (or possibly 52-145 to 1-94 - see JAL-2319)
+     */
+    StructureSelectionManager ssm = binding.getSsm();
+    String pdbFile = binding.getPdbFile()[0];
+    StructureMapping[] mappings = ssm.getMapping(pdbFile);
+    assertEquals(mappings.length, 2);
+    assertEquals(mappings[0].getChain(), "A");
+    assertEquals(mappings[0].getPDBResNum(53), 2);
+    assertEquals(mappings[0].getPDBResNum(145), 94);
+    assertEquals(mappings[1].getChain(), "B");
+    assertEquals(mappings[1].getPDBResNum(53), 2);
+    assertEquals(mappings[1].getPDBResNum(145), 94);
+
+    /*
+     * now add some features to FER2_ARATH 
+     */
+    // feature on a sequence region not mapped to structure:
+    sq.addSequenceFeature(new SequenceFeature("transit peptide",
+            "chloroplast", 1, 51, Float.NaN, null));
+    // feature on a region mapped to structure:
+    sq.addSequenceFeature(new SequenceFeature("domain",
+            "2Fe-2S ferredoxin-type", 55, 145, Float.NaN, null));
+    // on sparse positions of the sequence
+    sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
+            "Iron-Sulfur (2Fe-2S)", 91, 91, Float.NaN, null));
+    sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
+            "Iron-Sulfur (2Fe-2S)", 96, 96, Float.NaN, null));
+    // on a sequence region that is partially mapped to structure:
+    sq.addSequenceFeature(new SequenceFeature("helix", null, 50, 60,
+            Float.NaN, null));
+    // and again:
+    sq.addSequenceFeature(new SequenceFeature("chain", null, 50, 70,
+            Float.NaN, null));
+    // add numeric valued features - score is set as attribute value
+    sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 62,
+            62, -2.1f, null));
+    sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 65,
+            65, 3.6f, null));
+
+    /*
+     * set all features visible except for chain
+     */
+    af.setShowSeqFeatures(true);
+    FeatureRenderer fr = af.getFeatureRenderer();
+    fr.setVisible("transit peptide");
+    fr.setVisible("domain");
+    fr.setVisible("metal ion-binding site");
+    fr.setVisible("helix");
+    fr.setVisible("kd");
+
+    /*
+     * 'perform' menu action to copy visible features to
+     * attributes in Chimera
+     */
+    // TODO rename and pull up method to binding interface
+    // once functionality is added for Jmol as well
+    ((JalviewChimeraBinding) binding).sendFeaturesToChimera(af
+            .getViewport().getAlignPanel());
+
+    /*
+     * give Chimera time to open the commands file and execute it
+     */
+    try
+    {
+      Thread.sleep(1000);
+    } catch (InterruptedException e)
+    {
+    }
+
+    /*
+     * ask Chimera for its residue attribute names
+     */
+    List<String> reply = ((JalviewChimeraBinding) binding)
+            .sendChimeraCommand("list resattr", true);
+    // prefixed and sanitised attribute names for Jalview features:
+    assertTrue(reply.contains("resattr jv_domain"));
+    assertTrue(reply.contains("resattr jv_metal_ion_binding_site"));
+    assertTrue(reply.contains("resattr jv_helix"));
+    assertTrue(reply.contains("resattr jv_kd"));
+    // feature is not on a mapped region - no attribute created
+    assertFalse(reply.contains("resattr jv_transit_peptide"));
+    // feature is not visible - no attribute created
+    assertFalse(reply.contains("resattr jv_chain"));
+
+    /*
+     * ask Chimera for residues with an attribute
+     * 91 and 96 on sequence --> residues 40 and 45 on chains A and B
+     */
+    reply = ((JalviewChimeraBinding) binding).sendChimeraCommand(
+            "list resi att jv_metal_ion_binding_site", true);
+    assertEquals(reply.size(), 4);
+    assertTrue(reply
+            .contains("residue id #0:40.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
+    assertTrue(reply
+            .contains("residue id #0:45.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
+    assertTrue(reply
+            .contains("residue id #0:40.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
+    assertTrue(reply
+            .contains("residue id #0:45.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
+
+    /*
+     * check attributes with score values
+     * sequence positions 62 and 65 --> residues 11 and 14 on chains A and B
+     */
+    reply = ((JalviewChimeraBinding) binding).sendChimeraCommand(
+            "list resi att jv_kd", true);
+    assertEquals(reply.size(), 4);
+    assertTrue(reply.contains("residue id #0:11.A jv_kd -2.1 index 11"));
+    assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
+    assertTrue(reply.contains("residue id #0:11.B jv_kd -2.1 index 11"));
+    assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
+
+    /*
+     * list residues with positive kd score 
+     */
+    reply = ((JalviewChimeraBinding) binding).sendChimeraCommand(
+            "list resi spec :*/jv_kd>0 attr jv_kd", true);
+    assertEquals(reply.size(), 2);
+    assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
+    assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
+
+    chimeraViewer.closeViewer(true);
+    chimeraViewer = null;
   }
 }