Merge branch 'develop' into trialMerge
[jalview.git] / test / jalview / gui / PopupMenuTest.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.gui;
22
23 import static jalview.util.UrlConstants.DB_ACCESSION;
24 import static jalview.util.UrlConstants.SEQUENCE_ID;
25 import static org.testng.AssertJUnit.assertEquals;
26 import static org.testng.AssertJUnit.assertFalse;
27 import static org.testng.AssertJUnit.assertTrue;
28
29 import jalview.datamodel.AlignmentAnnotation;
30 import jalview.datamodel.AlignmentI;
31 import jalview.datamodel.Annotation;
32 import jalview.datamodel.DBRefEntry;
33 import jalview.datamodel.DBRefSource;
34 import jalview.datamodel.Sequence;
35 import jalview.datamodel.SequenceI;
36 import jalview.io.DataSourceType;
37 import jalview.io.FileFormat;
38 import jalview.io.FormatAdapter;
39 import jalview.util.MessageManager;
40
41 import java.awt.Component;
42 import java.io.IOException;
43 import java.util.ArrayList;
44 import java.util.List;
45
46 import javax.swing.JMenu;
47 import javax.swing.JMenuItem;
48 import javax.swing.JPopupMenu;
49 import javax.swing.JSeparator;
50
51 import org.testng.annotations.BeforeMethod;
52 import org.testng.annotations.Test;
53
54 public class PopupMenuTest
55 {
56   // 4 sequences x 13 positions
57   final static String TEST_DATA = ">FER_CAPAA Ferredoxin\n"
58           + "TIETHKEAELVG-\n"
59           + ">FER_CAPAN Ferredoxin, chloroplast precursor\n"
60           + "TIETHKEAELVG-\n"
61           + ">FER1_SOLLC Ferredoxin-1, chloroplast precursor\n"
62           + "TIETHKEEELTA-\n" + ">Q93XJ9_SOLTU Ferredoxin I precursor\n"
63           + "TIETHKEEELTA-\n";
64
65   AlignmentI alignment;
66
67   AlignmentPanel parentPanel;
68
69   PopupMenu testee = null;
70
71   @BeforeMethod(alwaysRun = true)
72   public void setUp() throws IOException
73   {
74     alignment = new FormatAdapter().readFile(TEST_DATA,
75             DataSourceType.PASTE, FileFormat.Fasta);
76     AlignFrame af = new AlignFrame(alignment, 700, 500);
77     parentPanel = new AlignmentPanel(af, af.getViewport());
78     testee = new PopupMenu(parentPanel, null, null);
79     int i = 0;
80     for (SequenceI seq : alignment.getSequences())
81     {
82       final AlignmentAnnotation annotation = new AlignmentAnnotation(
83               "label" + i, "desc" + i, i);
84       annotation.setCalcId("calcId" + i);
85       seq.addAlignmentAnnotation(annotation);
86       annotation.setSequenceRef(seq);
87     }
88   }
89
90   @Test(groups = { "Functional" })
91   public void testConfigureReferenceAnnotationsMenu_noSequenceSelected()
92   {
93     JMenuItem menu = new JMenuItem();
94     List<SequenceI> seqs = new ArrayList<SequenceI>();
95     testee.configureReferenceAnnotationsMenu(menu, seqs);
96     assertFalse(menu.isEnabled());
97     // now try null list
98     menu.setEnabled(true);
99     testee.configureReferenceAnnotationsMenu(menu, null);
100     assertFalse(menu.isEnabled());
101   }
102
103   /**
104    * Test building the 'add reference annotations' menu for the case where there
105    * are no reference annotations to add to the alignment. The menu item should
106    * be disabled.
107    */
108   @Test(groups = { "Functional" })
109   public void testConfigureReferenceAnnotationsMenu_noReferenceAnnotations()
110   {
111     JMenuItem menu = new JMenuItem();
112
113     /*
114      * Initial state is that sequences have annotations, and have dataset
115      * sequences, but the dataset sequences have no annotations. Hence nothing
116      * to add.
117      */
118     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
119
120     testee.configureReferenceAnnotationsMenu(menu, seqs);
121     assertFalse(menu.isEnabled());
122   }
123
124   /**
125    * Test building the 'add reference annotations' menu for the case where all
126    * reference annotations are already on the alignment. The menu item should be
127    * disabled.
128    */
129   @Test(groups = { "Functional" })
130   public void testConfigureReferenceAnnotationsMenu_alreadyAdded()
131   {
132     JMenuItem menu = new JMenuItem();
133     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
134
135     // make up new annotations and add to dataset sequences, sequences and
136     // alignment
137     attachReferenceAnnotations(seqs, true, true);
138
139     testee.configureReferenceAnnotationsMenu(menu, seqs);
140     assertFalse(menu.isEnabled());
141   }
142
143   /**
144    * Test building the 'add reference annotations' menu for the case where
145    * several reference annotations are on the dataset but not on the sequences.
146    * The menu item should be enabled, and acquire a tooltip which lists the
147    * annotation sources (calcIds) and type (labels).
148    */
149   @Test(groups = { "Functional" })
150   public void testConfigureReferenceAnnotationsMenu()
151   {
152     JMenuItem menu = new JMenuItem();
153     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
154
155     // make up new annotations and add to dataset sequences
156     attachReferenceAnnotations(seqs, false, false);
157
158     testee.configureReferenceAnnotationsMenu(menu, seqs);
159     assertTrue(menu.isEnabled());
160     String s = MessageManager.getString("label.add_annotations_for");
161     String expected = "<html><style> p.ttip {width: 350; text-align: justify; word-wrap: break-word;}</style><p class=\"ttip\">"
162             + s + "<br/>Jmol/secondary structure<br/>PDB/Temp</p></html>";
163     assertEquals(expected, menu.getToolTipText());
164   }
165
166   /**
167    * Test building the 'add reference annotations' menu for the case where
168    * several reference annotations are on the dataset and the sequences but not
169    * on the alignment. The menu item should be enabled, and acquire a tooltip
170    * which lists the annotation sources (calcIds) and type (labels).
171    */
172   @Test(groups = { "Functional" })
173   public void testConfigureReferenceAnnotationsMenu_notOnAlignment()
174   {
175     JMenuItem menu = new JMenuItem();
176     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
177
178     // make up new annotations and add to dataset sequences and sequences
179     attachReferenceAnnotations(seqs, true, false);
180
181     testee.configureReferenceAnnotationsMenu(menu, seqs);
182     assertTrue(menu.isEnabled());
183     String s = MessageManager.getString("label.add_annotations_for");
184     String expected = "<html><style> p.ttip {width: 350; text-align: justify; word-wrap: break-word;}</style><p class=\"ttip\">"
185             + s + "<br/>Jmol/secondary structure<br/>PDB/Temp</p></html>";
186     assertEquals(expected, menu.getToolTipText());
187   }
188
189   /**
190    * Generate annotations and add to dataset sequences and (optionally)
191    * sequences and/or alignment
192    * 
193    * @param seqs
194    * @param addToSequence
195    * @param addToAlignment
196    */
197   private void attachReferenceAnnotations(List<SequenceI> seqs,
198           boolean addToSequence, boolean addToAlignment)
199   {
200     // PDB.secondary structure on Sequence0
201     AlignmentAnnotation annotation = new AlignmentAnnotation(
202             "secondary structure", "", 0);
203     annotation.setCalcId("PDB");
204     seqs.get(0).getDatasetSequence().addAlignmentAnnotation(annotation);
205     if (addToSequence)
206     {
207       seqs.get(0).addAlignmentAnnotation(annotation);
208     }
209     if (addToAlignment)
210     {
211       this.alignment.addAnnotation(annotation);
212     }
213
214     // PDB.Temp on Sequence1
215     annotation = new AlignmentAnnotation("Temp", "", 0);
216     annotation.setCalcId("PDB");
217     seqs.get(1).getDatasetSequence().addAlignmentAnnotation(annotation);
218     if (addToSequence)
219     {
220       seqs.get(1).addAlignmentAnnotation(annotation);
221     }
222     if (addToAlignment)
223     {
224       this.alignment.addAnnotation(annotation);
225     }
226
227     // JMOL.secondary structure on Sequence0
228     annotation = new AlignmentAnnotation("secondary structure", "", 0);
229     annotation.setCalcId("Jmol");
230     seqs.get(0).getDatasetSequence().addAlignmentAnnotation(annotation);
231     if (addToSequence)
232     {
233       seqs.get(0).addAlignmentAnnotation(annotation);
234     }
235     if (addToAlignment)
236     {
237       this.alignment.addAnnotation(annotation);
238     }
239   }
240
241   /**
242    * Test building the 'add reference annotations' menu for the case where there
243    * are two alignment views:
244    * <ul>
245    * <li>in one view, reference annotations have been added (are on the
246    * datasets, sequences and alignment)</li>
247    * <li>in the current view, reference annotations are on the dataset and
248    * sequence, but not the alignment</li>
249    * </ul>
250    * The menu item should be enabled, and acquire a tooltip which lists the
251    * annotation sources (calcIds) and type (labels).
252    */
253   @Test(groups = { "Functional" })
254   public void testConfigureReferenceAnnotationsMenu_twoViews()
255   {
256   }
257
258   /**
259    * Test for building menu options including 'show' and 'hide' annotation
260    * types.
261    */
262   @Test(groups = { "Functional" })
263   public void testBuildAnnotationTypesMenus()
264   {
265     JMenu showMenu = new JMenu();
266     JMenu hideMenu = new JMenu();
267     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
268
269     // make up new annotations and add to sequences and to the alignment
270
271     // PDB.secondary structure on Sequence0
272     AlignmentAnnotation annotation = new AlignmentAnnotation(
273             "secondary structure", "", new Annotation[] {});
274     annotation.setCalcId("PDB");
275     annotation.visible = true;
276     seqs.get(0).addAlignmentAnnotation(annotation);
277     parentPanel.getAlignment().addAnnotation(annotation);
278
279     // JMOL.secondary structure on Sequence0 - hidden
280     annotation = new AlignmentAnnotation("secondary structure", "",
281             new Annotation[] {});
282     annotation.setCalcId("JMOL");
283     annotation.visible = false;
284     seqs.get(0).addAlignmentAnnotation(annotation);
285     parentPanel.getAlignment().addAnnotation(annotation);
286
287     // Jpred.SSP on Sequence0 - hidden
288     annotation = new AlignmentAnnotation("SSP", "", new Annotation[] {});
289     annotation.setCalcId("JPred");
290     annotation.visible = false;
291     seqs.get(0).addAlignmentAnnotation(annotation);
292     parentPanel.getAlignment().addAnnotation(annotation);
293
294     // PDB.Temp on Sequence1
295     annotation = new AlignmentAnnotation("Temp", "", new Annotation[] {});
296     annotation.setCalcId("PDB");
297     annotation.visible = true;
298     seqs.get(1).addAlignmentAnnotation(annotation);
299     parentPanel.getAlignment().addAnnotation(annotation);
300
301     /*
302      * Expect menu options to show "secondary structure" and "SSP", and to hide
303      * "secondary structure" and "Temp". Tooltip should be calcId.
304      */
305     testee.buildAnnotationTypesMenus(showMenu, hideMenu, seqs);
306
307     assertTrue(showMenu.isEnabled());
308     assertTrue(hideMenu.isEnabled());
309
310     Component[] showOptions = showMenu.getMenuComponents();
311     Component[] hideOptions = hideMenu.getMenuComponents();
312
313     assertEquals(4, showOptions.length); // includes 'All' and separator
314     assertEquals(4, hideOptions.length);
315     assertEquals("All", ((JMenuItem) showOptions[0]).getText());
316     assertTrue(showOptions[1] instanceof JPopupMenu.Separator);
317     assertEquals(JSeparator.HORIZONTAL,
318             ((JSeparator) showOptions[1]).getOrientation());
319     assertEquals("secondary structure",
320             ((JMenuItem) showOptions[2]).getText());
321     assertEquals("JMOL", ((JMenuItem) showOptions[2]).getToolTipText());
322     assertEquals("SSP", ((JMenuItem) showOptions[3]).getText());
323     assertEquals("JPred", ((JMenuItem) showOptions[3]).getToolTipText());
324
325     assertEquals("All", ((JMenuItem) hideOptions[0]).getText());
326     assertTrue(hideOptions[1] instanceof JPopupMenu.Separator);
327     assertEquals(JSeparator.HORIZONTAL,
328             ((JSeparator) hideOptions[1]).getOrientation());
329     assertEquals("secondary structure",
330             ((JMenuItem) hideOptions[2]).getText());
331     assertEquals("PDB", ((JMenuItem) hideOptions[2]).getToolTipText());
332     assertEquals("Temp", ((JMenuItem) hideOptions[3]).getText());
333     assertEquals("PDB", ((JMenuItem) hideOptions[3]).getToolTipText());
334   }
335
336   /**
337    * Test for building menu options with only 'hide' annotation types enabled.
338    */
339   @Test(groups = { "Functional" })
340   public void testBuildAnnotationTypesMenus_showDisabled()
341   {
342     JMenu showMenu = new JMenu();
343     JMenu hideMenu = new JMenu();
344     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
345
346     // make up new annotations and add to sequences and to the alignment
347
348     // PDB.secondary structure on Sequence0
349     AlignmentAnnotation annotation = new AlignmentAnnotation(
350             "secondary structure", "", new Annotation[] {});
351     annotation.setCalcId("PDB");
352     annotation.visible = true;
353     seqs.get(0).addAlignmentAnnotation(annotation);
354     parentPanel.getAlignment().addAnnotation(annotation);
355
356     // PDB.Temp on Sequence1
357     annotation = new AlignmentAnnotation("Temp", "", new Annotation[] {});
358     annotation.setCalcId("PDB");
359     annotation.visible = true;
360     seqs.get(1).addAlignmentAnnotation(annotation);
361     parentPanel.getAlignment().addAnnotation(annotation);
362
363     /*
364      * Expect menu options to hide "secondary structure" and "Temp". Tooltip
365      * should be calcId. 'Show' menu should be disabled.
366      */
367     testee.buildAnnotationTypesMenus(showMenu, hideMenu, seqs);
368
369     assertFalse(showMenu.isEnabled());
370     assertTrue(hideMenu.isEnabled());
371
372     Component[] showOptions = showMenu.getMenuComponents();
373     Component[] hideOptions = hideMenu.getMenuComponents();
374
375     assertEquals(2, showOptions.length); // includes 'All' and separator
376     assertEquals(4, hideOptions.length);
377     assertEquals("All", ((JMenuItem) showOptions[0]).getText());
378     assertTrue(showOptions[1] instanceof JPopupMenu.Separator);
379     assertEquals(JSeparator.HORIZONTAL,
380             ((JSeparator) showOptions[1]).getOrientation());
381
382     assertEquals("All", ((JMenuItem) hideOptions[0]).getText());
383     assertTrue(hideOptions[1] instanceof JPopupMenu.Separator);
384     assertEquals(JSeparator.HORIZONTAL,
385             ((JSeparator) hideOptions[1]).getOrientation());
386     assertEquals("secondary structure",
387             ((JMenuItem) hideOptions[2]).getText());
388     assertEquals("PDB", ((JMenuItem) hideOptions[2]).getToolTipText());
389     assertEquals("Temp", ((JMenuItem) hideOptions[3]).getText());
390     assertEquals("PDB", ((JMenuItem) hideOptions[3]).getToolTipText());
391   }
392
393   /**
394    * Test for building menu options with only 'show' annotation types enabled.
395    */
396   @Test(groups = { "Functional" })
397   public void testBuildAnnotationTypesMenus_hideDisabled()
398   {
399     JMenu showMenu = new JMenu();
400     JMenu hideMenu = new JMenu();
401     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
402
403     // make up new annotations and add to sequences and to the alignment
404
405     // PDB.secondary structure on Sequence0
406     AlignmentAnnotation annotation = new AlignmentAnnotation(
407             "secondary structure", "", new Annotation[] {});
408     annotation.setCalcId("PDB");
409     annotation.visible = false;
410     seqs.get(0).addAlignmentAnnotation(annotation);
411     parentPanel.getAlignment().addAnnotation(annotation);
412
413     // PDB.Temp on Sequence1
414     annotation = new AlignmentAnnotation("Temp", "", new Annotation[] {});
415     annotation.setCalcId("PDB2");
416     annotation.visible = false;
417     seqs.get(1).addAlignmentAnnotation(annotation);
418     parentPanel.getAlignment().addAnnotation(annotation);
419
420     /*
421      * Expect menu options to show "secondary structure" and "Temp". Tooltip
422      * should be calcId. 'hide' menu should be disabled.
423      */
424     testee.buildAnnotationTypesMenus(showMenu, hideMenu, seqs);
425
426     assertTrue(showMenu.isEnabled());
427     assertFalse(hideMenu.isEnabled());
428
429     Component[] showOptions = showMenu.getMenuComponents();
430     Component[] hideOptions = hideMenu.getMenuComponents();
431
432     assertEquals(4, showOptions.length); // includes 'All' and separator
433     assertEquals(2, hideOptions.length);
434     assertEquals("All", ((JMenuItem) showOptions[0]).getText());
435     assertTrue(showOptions[1] instanceof JPopupMenu.Separator);
436     assertEquals(JSeparator.HORIZONTAL,
437             ((JSeparator) showOptions[1]).getOrientation());
438     assertEquals("secondary structure",
439             ((JMenuItem) showOptions[2]).getText());
440     assertEquals("PDB", ((JMenuItem) showOptions[2]).getToolTipText());
441     assertEquals("Temp", ((JMenuItem) showOptions[3]).getText());
442     assertEquals("PDB2", ((JMenuItem) showOptions[3]).getToolTipText());
443
444     assertEquals("All", ((JMenuItem) hideOptions[0]).getText());
445     assertTrue(hideOptions[1] instanceof JPopupMenu.Separator);
446     assertEquals(JSeparator.HORIZONTAL,
447             ((JSeparator) hideOptions[1]).getOrientation());
448   }
449
450   /**
451    * Test for adding feature links
452    */
453   @Test(groups = { "Functional" })
454   public void testAddFeatureLinks()
455   {
456     // sequences from the alignment
457     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
458
459     // create list of links and list of DBRefs
460     List<String> links = new ArrayList<String>();
461     List<DBRefEntry> refs = new ArrayList<DBRefEntry>();
462
463     // links as might be added into Preferences | Connections dialog
464     links.add("EMBL-EBI Search | http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$"
465             + SEQUENCE_ID + "$");
466     links.add("UNIPROT | http://www.uniprot.org/uniprot/$" + DB_ACCESSION
467             + "$");
468     links.add("INTERPRO | http://www.ebi.ac.uk/interpro/entry/$"
469             + DB_ACCESSION + "$");
470     // Gene3D entry tests for case (in)sensitivity
471     links.add("Gene3D | http://gene3d.biochem.ucl.ac.uk/Gene3D/search?sterm=$"
472             + DB_ACCESSION + "$&mode=protein");
473
474     // make seq0 dbrefs
475     refs.add(new DBRefEntry(DBRefSource.UNIPROT, "1", "P83527"));
476     refs.add(new DBRefEntry("INTERPRO", "1", "IPR001041"));
477     refs.add(new DBRefEntry("INTERPRO", "1", "IPR006058"));
478     refs.add(new DBRefEntry("INTERPRO", "1", "IPR012675"));
479     
480     // make seq1 dbrefs
481     refs.add(new DBRefEntry(DBRefSource.UNIPROT, "1", "Q9ZTS2"));
482     refs.add(new DBRefEntry("GENE3D", "1", "3.10.20.30"));
483
484     // add all the dbrefs to the sequences: Uniprot 1 each, Interpro all 3 to
485     // seq0, Gene3D to seq1
486     seqs.get(0).addDBRef(refs.get(0));
487
488     seqs.get(0).addDBRef(refs.get(1));
489     seqs.get(0).addDBRef(refs.get(2));
490     seqs.get(0).addDBRef(refs.get(3));
491     
492     seqs.get(1).addDBRef(refs.get(4));
493     seqs.get(1).addDBRef(refs.get(5));
494     
495     // get the Popup Menu for first sequence
496     testee = new PopupMenu(parentPanel, (Sequence) seqs.get(0), links);
497     Component[] seqItems = testee.sequenceMenu.getMenuComponents();
498     JMenu linkMenu = (JMenu) seqItems[6];
499     Component[] linkItems = linkMenu.getMenuComponents();
500     
501     // check the number of links are the expected number
502     assertEquals(5, linkItems.length);
503
504     // first entry is EMBL-EBI which just uses sequence id not accession id?
505     assertEquals("EMBL-EBI Search", ((JMenuItem) linkItems[0]).getText());
506
507     // sequence id for each link should match corresponding DB accession id
508     for (int i = 1; i < 4; i++)
509     {
510       assertEquals(refs.get(i - 1).getSource(), ((JMenuItem) linkItems[i])
511               .getText().split("\\|")[0]);
512       assertEquals(refs.get(i - 1).getAccessionId(),
513               ((JMenuItem) linkItems[i])
514               .getText().split("\\|")[1]);
515     }
516
517     // get the Popup Menu for second sequence
518     testee = new PopupMenu(parentPanel, (Sequence) seqs.get(1), links);
519     seqItems = testee.sequenceMenu.getMenuComponents();
520     linkMenu = (JMenu) seqItems[6];
521     linkItems = linkMenu.getMenuComponents();
522     
523     // check the number of links are the expected number
524     assertEquals(3, linkItems.length);
525
526     // first entry is EMBL-EBI which just uses sequence id not accession id?
527     assertEquals("EMBL-EBI Search", ((JMenuItem) linkItems[0]).getText());
528
529     // sequence id for each link should match corresponding DB accession id
530     for (int i = 1; i < 3; i++)
531     {
532       assertEquals(refs.get(i + 3).getSource(), ((JMenuItem) linkItems[i])
533               .getText().split("\\|")[0].toUpperCase());
534       assertEquals(refs.get(i + 3).getAccessionId(),
535               ((JMenuItem) linkItems[i]).getText().split("\\|")[1]);
536     }
537
538     // if there are no valid links the Links submenu is disabled
539     List<String> nomatchlinks = new ArrayList<String>();
540     nomatchlinks.add("NOMATCH | http://www.uniprot.org/uniprot/$"
541             + DB_ACCESSION + "$");
542
543     testee = new PopupMenu(parentPanel, (Sequence) seqs.get(0),
544             nomatchlinks);
545     seqItems = testee.sequenceMenu.getMenuComponents();
546     linkMenu = (JMenu) seqItems[6];
547     assertFalse(linkMenu.isEnabled());
548
549   }
550 }