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