JAL-2295 test that verifies transfer of features to Chimera attributes
[jalview.git] / test / jalview / ext / rbvi / chimera / JalviewChimeraView.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.ext.rbvi.chimera;
22
23 import static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertFalse;
25 import static org.testng.Assert.assertNotNull;
26 import static org.testng.Assert.assertTrue;
27
28 import jalview.api.FeatureRenderer;
29 import jalview.api.structures.JalviewStructureDisplayI;
30 import jalview.bin.Cache;
31 import jalview.bin.Jalview;
32 import jalview.datamodel.DBRefEntry;
33 import jalview.datamodel.PDBEntry;
34 import jalview.datamodel.SequenceFeature;
35 import jalview.datamodel.SequenceI;
36 import jalview.gui.AlignFrame;
37 import jalview.gui.Desktop;
38 import jalview.gui.Preferences;
39 import jalview.gui.StructureViewer;
40 import jalview.gui.StructureViewer.ViewerType;
41 import jalview.io.FileLoader;
42 import jalview.io.FormatAdapter;
43 import jalview.structure.StructureMapping;
44 import jalview.structure.StructureSelectionManager;
45 import jalview.structures.models.AAStructureBindingModel;
46
47 import java.util.List;
48 import java.util.Vector;
49
50 import org.testng.annotations.AfterClass;
51 import org.testng.annotations.AfterMethod;
52 import org.testng.annotations.BeforeClass;
53 import org.testng.annotations.Test;
54
55 @Test(singleThreaded = true)
56 public class JalviewChimeraView
57 {
58
59   private JalviewStructureDisplayI chimeraViewer;
60
61   /**
62    * @throws java.lang.Exception
63    */
64   @BeforeClass(alwaysRun = true)
65   public static void setUpBeforeClass() throws Exception
66   {
67     Jalview.main(new String[] {
68         "-noquestionnaire -nonews -props",
69         "test/jalview/ext/rbvi/chimera/testProps.jvprops" });
70     Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
71             ViewerType.CHIMERA.name());
72     Cache.setProperty("SHOW_ANNOTATIONS", "false");
73     Cache.setProperty(Preferences.STRUCT_FROM_PDB, "false");
74     Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
75             ViewerType.CHIMERA.name());
76     Cache.setProperty("MAP_WITH_SIFTS", "true");
77   }
78
79   /**
80    * @throws java.lang.Exception
81    */
82   @AfterClass(alwaysRun = true)
83   public static void tearDownAfterClass() throws Exception
84   {
85     Desktop.instance.closeAll_actionPerformed(null);
86   }
87
88   @AfterMethod(alwaysRun = true)
89   public void tearDownAfterTest() throws Exception
90   {
91     if (chimeraViewer != null)
92     {
93       chimeraViewer.closeViewer(true);
94     }
95   }
96
97   /**
98    * Load 1GAQ and view the first structure for which a PDB id is found
99    */
100   @Test(groups = { "External", "Network" })
101   public void testSingleSeqViewChimera()
102   {
103     String inFile = "examples/1gaq.txt";
104     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
105             FormatAdapter.FILE);
106     assertNotNull(af, "Failed to create AlignFrame");
107     SequenceI sq = af.getViewport().getAlignment().getSequenceAt(0);
108     assertEquals(sq.getName(), "1GAQ|A");
109     SequenceI dsq = sq.getDatasetSequence();
110     Vector<PDBEntry> pdbIds = dsq.getAllPDBEntries();
111     assertEquals(pdbIds.size(), 1);
112     PDBEntry pdbEntry = pdbIds.get(0);
113     assertEquals(pdbEntry.getId(), "1GAQ");
114     StructureViewer structureViewer = new StructureViewer(af.getViewport()
115             .getStructureSelectionManager());
116     chimeraViewer = structureViewer.viewStructures(pdbEntry,
117             new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
118     /*
119      * Wait for viewer load thread to complete
120      */
121     while (!chimeraViewer.getBinding().isFinishedInit())
122     {
123       try
124       {
125         Thread.sleep(500);
126       } catch (InterruptedException e)
127       {
128       }
129     }
130     assertEquals(chimeraViewer.getBinding().getPdbCount(), 1);
131     chimeraViewer.closeViewer(true);
132     chimeraViewer = null;
133     return;
134   }
135
136   /**
137    * Test for writing Jalview features as attributes on mapped residues in
138    * Chimera
139    */
140   // External as this requires a local install of Chimera
141   // Network as fetch from PDB/SIFTS is involved TODO mock this
142   @Test(groups = { "External", "Network" })
143   public void testTransferFeatures()
144   {
145     String inFile = "examples/uniref50.fa";
146     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(inFile,
147             FormatAdapter.FILE);
148     assertNotNull(af, "Failed to create AlignFrame");
149     SequenceI sq = af.getViewport().getAlignment().findName("FER2_ARATH");
150     assertNotNull(sq, "Didn't find FER2_ARATH");
151
152     /*
153      * need a Uniprot dbref for SIFTS mapping to work!!
154      */
155     sq.addDBRef(new DBRefEntry("UNIPROT", "0", "P16972", null));
156     PDBEntry pdbEntry = new PDBEntry("4ZHO", null, null, null);
157     // addPDBId() doesn't (yet) delegate to dataset sequence if there is one!
158     sq.getDatasetSequence().addPDBId(pdbEntry);
159     StructureViewer structureViewer = new StructureViewer(af.getViewport()
160             .getStructureSelectionManager());
161     chimeraViewer = structureViewer.viewStructures(pdbEntry,
162             new SequenceI[] { sq }, af.getCurrentView().getAlignPanel());
163
164     AAStructureBindingModel binding = chimeraViewer.getBinding();
165     do
166     {
167       try
168       {
169         Thread.sleep(500);
170       } catch (InterruptedException e)
171     {
172     }
173     } while (!binding.isFinishedInit());
174
175     assertEquals(binding.getPdbCount(), 1);
176
177     /*
178      * check mapping is (sequence) 53-145 to (structure) 2-94 A/B
179      * (or possibly 52-145 to 1-94 - see JAL-2319)
180      */
181     StructureSelectionManager ssm = binding.getSsm();
182     String pdbFile = binding.getPdbFile()[0];
183     StructureMapping[] mappings = ssm.getMapping(pdbFile);
184     assertEquals(mappings.length, 2);
185     assertEquals(mappings[0].getChain(), "A");
186     assertEquals(mappings[0].getPDBResNum(53), 2);
187     assertEquals(mappings[0].getPDBResNum(145), 94);
188     assertEquals(mappings[1].getChain(), "B");
189     assertEquals(mappings[1].getPDBResNum(53), 2);
190     assertEquals(mappings[1].getPDBResNum(145), 94);
191
192     /*
193      * now add some features to FER2_ARATH 
194      */
195     // feature on a sequence region not mapped to structure:
196     sq.addSequenceFeature(new SequenceFeature("transit peptide",
197             "chloroplast", 1, 51, Float.NaN, null));
198     // feature on a region mapped to structure:
199     sq.addSequenceFeature(new SequenceFeature("domain",
200             "2Fe-2S ferredoxin-type", 55, 145, Float.NaN, null));
201     // on sparse positions of the sequence
202     sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
203             "Iron-Sulfur (2Fe-2S)", 91, 91, Float.NaN, null));
204     sq.addSequenceFeature(new SequenceFeature("metal ion-binding site",
205             "Iron-Sulfur (2Fe-2S)", 96, 96, Float.NaN, null));
206     // on a sequence region that is partially mapped to structure:
207     sq.addSequenceFeature(new SequenceFeature("helix", null, 50, 60,
208             Float.NaN, null));
209     // and again:
210     sq.addSequenceFeature(new SequenceFeature("chain", null, 50, 70,
211             Float.NaN, null));
212     // add numeric valued features - score is set as attribute value
213     sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 62,
214             62, -2.1f, null));
215     sq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 65,
216             65, 3.6f, null));
217
218     /*
219      * set all features visible except for chain
220      */
221     af.setShowSeqFeatures(true);
222     FeatureRenderer fr = af.getFeatureRenderer();
223     fr.setVisible("transit peptide");
224     fr.setVisible("domain");
225     fr.setVisible("metal ion-binding site");
226     fr.setVisible("helix");
227     fr.setVisible("kd");
228
229     /*
230      * 'perform' menu action to copy visible features to
231      * attributes in Chimera
232      */
233     // TODO rename and pull up method to binding interface
234     // once functionality is added for Jmol as well
235     ((JalviewChimeraBinding) binding).sendFeaturesToChimera(af
236             .getViewport().getAlignPanel());
237
238     /*
239      * give Chimera time to open the commands file and execute it
240      */
241     try
242     {
243       Thread.sleep(1000);
244     } catch (InterruptedException e)
245     {
246     }
247
248     /*
249      * ask Chimera for its residue attribute names
250      */
251     List<String> reply = ((JalviewChimeraBinding) binding)
252             .sendChimeraCommand("list resattr", true);
253     // prefixed and sanitised attribute names for Jalview features:
254     assertTrue(reply.contains("resattr jv_domain"));
255     assertTrue(reply.contains("resattr jv_metal_ion_binding_site"));
256     assertTrue(reply.contains("resattr jv_helix"));
257     assertTrue(reply.contains("resattr jv_kd"));
258     // feature is not on a mapped region - no attribute created
259     assertFalse(reply.contains("resattr jv_transit_peptide"));
260     // feature is not visible - no attribute created
261     assertFalse(reply.contains("resattr jv_chain"));
262
263     /*
264      * ask Chimera for residues with an attribute
265      * 91 and 96 on sequence --> residues 40 and 45 on chains A and B
266      */
267     reply = ((JalviewChimeraBinding) binding).sendChimeraCommand(
268             "list resi att jv_metal_ion_binding_site", true);
269     assertEquals(reply.size(), 4);
270     assertTrue(reply
271             .contains("residue id #0:40.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
272     assertTrue(reply
273             .contains("residue id #0:45.A jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
274     assertTrue(reply
275             .contains("residue id #0:40.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 40"));
276     assertTrue(reply
277             .contains("residue id #0:45.B jv_metal_ion_binding_site \"Iron-Sulfur (2Fe-2S)\" index 45"));
278
279     /*
280      * check attributes with score values
281      * sequence positions 62 and 65 --> residues 11 and 14 on chains A and B
282      */
283     reply = ((JalviewChimeraBinding) binding).sendChimeraCommand(
284             "list resi att jv_kd", true);
285     assertEquals(reply.size(), 4);
286     assertTrue(reply.contains("residue id #0:11.A jv_kd -2.1 index 11"));
287     assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
288     assertTrue(reply.contains("residue id #0:11.B jv_kd -2.1 index 11"));
289     assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
290
291     /*
292      * list residues with positive kd score 
293      */
294     reply = ((JalviewChimeraBinding) binding).sendChimeraCommand(
295             "list resi spec :*/jv_kd>0 attr jv_kd", true);
296     assertEquals(reply.size(), 2);
297     assertTrue(reply.contains("residue id #0:14.A jv_kd 3.6 index 14"));
298     assertTrue(reply.contains("residue id #0:14.B jv_kd 3.6 index 14"));
299
300     chimeraViewer.closeViewer(true);
301     chimeraViewer = null;
302   }
303 }