JAL-2650 configure new viewport settings before selecting view tab
[jalview.git] / test / jalview / gui / AlignFrameTest.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 org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertFalse;
25 import static org.testng.Assert.assertNotSame;
26 import static org.testng.Assert.assertSame;
27 import static org.testng.Assert.assertTrue;
28
29 import jalview.api.AlignCalcManagerI;
30 import jalview.bin.Cache;
31 import jalview.bin.Jalview;
32 import jalview.datamodel.Alignment;
33 import jalview.datamodel.AlignmentI;
34 import jalview.datamodel.Sequence;
35 import jalview.datamodel.SequenceFeature;
36 import jalview.datamodel.SequenceGroup;
37 import jalview.datamodel.SequenceI;
38 import jalview.io.DataSourceType;
39 import jalview.io.FileLoader;
40 import jalview.io.Jalview2xmlTests;
41 import jalview.renderer.ResidueShaderI;
42 import jalview.schemes.BuriedColourScheme;
43 import jalview.schemes.HelixColourScheme;
44 import jalview.schemes.JalviewColourScheme;
45 import jalview.schemes.StrandColourScheme;
46 import jalview.schemes.TurnColourScheme;
47 import jalview.util.MessageManager;
48
49 import java.awt.Color;
50 import java.util.List;
51
52 import org.testng.annotations.AfterMethod;
53 import org.testng.annotations.BeforeClass;
54 import org.testng.annotations.BeforeMethod;
55 import org.testng.annotations.Test;
56
57 public class AlignFrameTest
58 {
59   AlignFrame af;
60
61   @BeforeClass(alwaysRun = true)
62   public void setUpJvOptionPane()
63   {
64     JvOptionPane.setInteractiveMode(false);
65     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
66   }
67
68   @Test(groups = "Functional")
69   public void testHideFeatureColumns()
70   {
71     SequenceI seq1 = new Sequence("Seq1", "ABCDEFGHIJ");
72     SequenceI seq2 = new Sequence("Seq2", "ABCDEFGHIJ");
73     seq1.addSequenceFeature(new SequenceFeature("Metal", "", 1, 5,
74             Float.NaN, null));
75     seq2.addSequenceFeature(new SequenceFeature("Metal", "", 6, 10,
76             Float.NaN, null));
77     seq1.addSequenceFeature(new SequenceFeature("Turn", "", 2, 4,
78             Float.NaN, null));
79     seq2.addSequenceFeature(new SequenceFeature("Turn", "", 7, 9,
80             Float.NaN, null));
81     AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 });
82     AlignFrame alignFrame = new AlignFrame(al, al.getWidth(), al.getHeight());
83
84     /*
85      * hiding a feature not present does nothing
86      */
87     assertFalse(alignFrame.hideFeatureColumns("exon", true));
88     assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty());
89     assertTrue(alignFrame.getViewport().getAlignment().getHiddenColumns()
90             .getHiddenColumnsCopy()
91             .isEmpty());
92     assertFalse(alignFrame.hideFeatureColumns("exon", false));
93     assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty());
94     assertTrue(alignFrame.getViewport().getAlignment().getHiddenColumns()
95             .getHiddenColumnsCopy()
96             .isEmpty());
97
98     /*
99      * hiding a feature in all columns does nothing
100      */
101     assertFalse(alignFrame.hideFeatureColumns("Metal", true));
102     assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty());
103     List<int[]> hidden = alignFrame.getViewport().getAlignment()
104             .getHiddenColumns()
105             .getHiddenColumnsCopy();
106     assertTrue(hidden.isEmpty());
107
108     /*
109      * hide a feature present in some columns
110      * sequence positions [2-4], [7-9] are column positions
111      * [1-3], [6-8] base zero
112      */
113     assertTrue(alignFrame.hideFeatureColumns("Turn", true));
114     hidden = alignFrame.getViewport().getAlignment().getHiddenColumns()
115             .getHiddenColumnsCopy();
116     assertEquals(hidden.size(), 2);
117     assertEquals(hidden.get(0)[0], 1);
118     assertEquals(hidden.get(0)[1], 3);
119     assertEquals(hidden.get(1)[0], 6);
120     assertEquals(hidden.get(1)[1], 8);
121   }
122
123   @BeforeClass(alwaysRun = true)
124   public static void setUpBeforeClass() throws Exception
125   {
126     /*
127      * use read-only test properties file
128      */
129     Cache.loadProperties("test/jalview/io/testProps.jvprops");
130     Jalview.main(new String[] { "-nonews" });
131   }
132
133   @AfterMethod(alwaysRun = true)
134   public void tearDown()
135   {
136     Desktop.instance.closeAll_actionPerformed(null);
137   }
138
139   /**
140    * configure (read-only) properties for test to ensure Consensus is computed
141    * for colour Above PID testing
142    */
143   @BeforeMethod(alwaysRun = true)
144   public void setUp()
145   {
146     Cache.loadProperties("test/jalview/io/testProps.jvprops");
147     Cache.applicationProperties.setProperty("SHOW_IDENTITY",
148             Boolean.TRUE.toString());
149     af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
150             DataSourceType.FILE);
151
152     /*
153      * wait for Consensus thread to complete
154      */
155     synchronized (this)
156     {
157       while (af.getViewport().getConsensusSeq() == null)
158       {
159         try
160         {
161           wait(50);
162         } catch (InterruptedException e)
163         {
164         }
165       }
166     }
167   }
168
169   /**
170    * Test that changing background (alignment) colour scheme
171    * <ul>
172    * <li>with Apply Colour to All Groups not selected, does not change group
173    * colours</li>
174    * <li>with Apply Colour to All Groups selected, does change group colours</li>
175    * <li>in neither case, changes alignment or group colour thresholds (PID or
176    * Conservation)</li>
177    * </ul>
178    */
179   @Test(groups = "Functional")
180   public void testChangeColour_background_groupsAndThresholds()
181   {
182     AlignViewport av = af.getViewport();
183     AlignmentI al = av.getAlignment();
184
185     /*
186      * Colour alignment by Buried Index
187      */
188     af.applyToAllGroups_actionPerformed(false);
189     af.changeColour_actionPerformed(JalviewColourScheme.Buried.toString());
190     assertTrue(av.getGlobalColourScheme() instanceof BuriedColourScheme);
191     assertFalse(av.getResidueShading().conservationApplied());
192     assertEquals(av.getResidueShading().getThreshold(), 0);
193
194     /*
195      * Apply Conservation 20%
196      */
197     af.conservationMenuItem_actionPerformed(true);
198     SliderPanel sp = SliderPanel.getSliderPanel();
199     assertEquals(sp.getTitle(), MessageManager.formatMessage(
200             "label.conservation_colour_increment",
201             new String[] { "Background" }));
202     assertTrue(sp.isForConservation());
203     sp.valueChanged(20);
204     assertTrue(av.getResidueShading().conservationApplied());
205     assertEquals(av.getResidueShading().getConservationInc(), 20);
206
207     /*
208      * Apply PID threshold 10% (conservation still applies as well)
209      */
210     af.abovePIDThreshold_actionPerformed(true);
211     sp = SliderPanel.getSliderPanel();
212     assertFalse(sp.isForConservation());
213     assertEquals(sp.getTitle(), MessageManager.formatMessage(
214             "label.percentage_identity_threshold",
215             new String[] { "Background" }));
216     sp.valueChanged(10);
217     assertEquals(av.getResidueShading().getThreshold(), 10);
218     assertTrue(av.getResidueShading().conservationApplied());
219     assertEquals(av.getResidueShading().getConservationInc(), 20);
220
221     /*
222      * create a group with Strand colouring, 30% Conservation
223      * and 40% PID threshold
224      */
225     SequenceGroup sg = new SequenceGroup();
226     sg.addSequence(al.getSequenceAt(0), false);
227     sg.setStartRes(15);
228     sg.setEndRes(25);
229     av.setSelectionGroup(sg);
230
231     /*
232      * apply 30% Conservation to group
233      */
234     PopupMenu popupMenu = new PopupMenu(af.alignPanel, null, null);
235     popupMenu.changeColour_actionPerformed(JalviewColourScheme.Strand
236             .toString());
237     assertTrue(sg.getColourScheme() instanceof StrandColourScheme);
238     assertEquals(al.getGroups().size(), 1);
239     assertSame(al.getGroups().get(0), sg);
240     popupMenu.conservationMenuItem_actionPerformed(true);
241     sp = SliderPanel.getSliderPanel();
242     assertTrue(sp.isForConservation());
243     assertEquals(sp.getTitle(), MessageManager.formatMessage(
244             "label.conservation_colour_increment",
245             new String[] { sg.getName() }));
246     sp.valueChanged(30);
247     assertTrue(sg.getGroupColourScheme().conservationApplied());
248     assertEquals(sg.getGroupColourScheme().getConservationInc(), 30);
249
250     /*
251      * apply 40% PID threshold to group
252      */
253     popupMenu.abovePIDColour_actionPerformed(true);
254     sp = SliderPanel.getSliderPanel();
255     assertFalse(sp.isForConservation());
256     assertEquals(sp.getTitle(), MessageManager.formatMessage(
257             "label.percentage_identity_threshold",
258             new String[] { sg.getName() }));
259     sp.valueChanged(40);
260     assertEquals(sg.getGroupColourScheme().getThreshold(), 40);
261     // conservation threshold is unchanged:
262     assertTrue(sg.getGroupColourScheme().conservationApplied());
263     assertEquals(sg.getGroupColourScheme().getConservationInc(), 30);
264
265     /*
266      * change alignment colour - group colour, and all thresholds,
267      * should be unaffected
268      */
269     af.changeColour_actionPerformed(JalviewColourScheme.Turn.toString());
270     assertTrue(av.getGlobalColourScheme() instanceof TurnColourScheme);
271     assertTrue(av.getResidueShading().conservationApplied());
272     assertEquals(av.getResidueShading().getConservationInc(), 20);
273     assertEquals(av.getResidueShading().getThreshold(), 10);
274     assertTrue(sg.getColourScheme() instanceof StrandColourScheme);
275     assertTrue(sg.getGroupColourScheme().conservationApplied());
276     assertEquals(sg.getGroupColourScheme().getConservationInc(), 30);
277     assertEquals(sg.getGroupColourScheme().getThreshold(), 40);
278
279     /*
280      * Now change alignment colour with Apply Colour To All Groups
281      * - group colour should change, but not colour thresholds
282      */
283     af.applyToAllGroups_actionPerformed(true);
284     af.changeColour_actionPerformed(JalviewColourScheme.Helix.toString());
285     assertTrue(av.getGlobalColourScheme() instanceof HelixColourScheme);
286     assertTrue(av.getResidueShading().conservationApplied());
287     assertEquals(av.getResidueShading().getConservationInc(), 20);
288     assertEquals(av.getResidueShading().getThreshold(), 10);
289     assertTrue(sg.getColourScheme() instanceof HelixColourScheme);
290     assertTrue(sg.getGroupColourScheme().conservationApplied());
291     assertEquals(sg.getGroupColourScheme().getConservationInc(), 30);
292     assertEquals(sg.getGroupColourScheme().getThreshold(), 40);
293   }
294
295   /**
296    * Test residue colouring with various options
297    * <ol>
298    * <li>no PID or Conservation threshold</li>
299    * <li>colour by Conservation applied</li>
300    * <li>colour by Conservation removed</li>
301    * <li>colour above PID - various values</li>
302    * <li>colour above PID removed</li>
303    * <li>Above PID plus By Conservation combined</li>
304    * <li>remove Above PID to leave just By Conservation</li>
305    * <li>re-add Above PID</li>
306    * <li>remove By Conservation to leave just Above PID</li>
307    * <li>remove Above PID to leave original colours</li>
308    * </ol>
309    */
310   @Test(groups = "Functional")
311   public void testColourThresholdActions()
312   {
313     AlignViewport av = af.getViewport();
314     AlignmentI al = av.getAlignment();
315
316     /*
317      * Colour alignment by Helix Propensity, no thresholds
318      */
319     af.applyToAllGroups_actionPerformed(false);
320     af.changeColour_actionPerformed(JalviewColourScheme.Helix.toString());
321     assertTrue(av.getGlobalColourScheme() instanceof HelixColourScheme);
322     assertFalse(av.getResidueShading().conservationApplied());
323     assertEquals(av.getResidueShading().getThreshold(), 0);
324
325     /*
326      * inspect the colour of 
327      * FER_CAPAN.9(I), column 14 (14 base 0)
328      * FER_CAPAN.10(SER), column 16 (15 base 0)
329      */
330     SequenceI ferCapan = al.findName("FER_CAPAN");
331     ResidueShaderI rs = av.getResidueShading();
332     Color c = rs.findColour('I', 14, ferCapan);
333     Color i_original = new Color(138, 117, 138);
334     assertEquals(c, i_original);
335     c = rs.findColour('S', 15, ferCapan);
336     Color s_original = new Color(54, 201, 54);
337     assertEquals(c, s_original);
338
339     /*
340      * colour by conservation with increment 10
341      */
342     af.conservationMenuItem_actionPerformed(true);
343     SliderPanel sp = SliderPanel.getSliderPanel();
344     assertTrue(sp.isForConservation());
345     assertEquals(sp.getValue(), 30); // initial slider setting
346     sp.valueChanged(10);
347     assertSame(rs, av.getResidueShading());
348     c = rs.findColour('I', 14, ferCapan);
349     Color i_faded = new Color(196, 186, 196);
350     assertEquals(c, i_faded);
351     c = rs.findColour('S', 15, ferCapan);
352     Color s_faded = new Color(144, 225, 144);
353     assertEquals(c, s_faded);
354
355     /*
356      * deselect By Conservation - colour should revert
357      */
358     af.conservationMenuItem_actionPerformed(false);
359     c = rs.findColour('S', 15, ferCapan);
360     assertEquals(c, s_original);
361
362     /*
363      * now Above PID, threshold = 0%
364      * should be no change
365      */
366     af.abovePIDThreshold_actionPerformed(true);
367     sp = SliderPanel.getSliderPanel();
368     assertFalse(sp.isForConservation());
369     assertEquals(sp.getValue(), 0); // initial slider setting
370     c = rs.findColour('I', 14, ferCapan);
371     assertEquals(c, i_original);
372     c = rs.findColour('S', 15, ferCapan);
373     assertEquals(c, s_original);
374
375     /*
376      * Above PID, threshold = 1%
377      * 15.I becomes White because no match to consensus (V)
378      * 16.S remains coloured as matches 66.66% consensus
379      */
380     sp.valueChanged(1);
381     c = rs.findColour('I', 14, ferCapan);
382     assertEquals(c, Color.white);
383     c = rs.findColour('S', 15, ferCapan);
384     assertEquals(c, s_original);
385
386     /*
387      * threshold 66% - no further change yet...
388      */
389     sp.valueChanged(66);
390     c = rs.findColour('I', 14, ferCapan);
391     assertEquals(c, Color.white);
392     c = rs.findColour('S', 15, ferCapan);
393     assertEquals(c, s_original);
394
395     /*
396      * threshold 67% - now both residues are white
397      */
398     sp.valueChanged(67);
399     c = rs.findColour('I', 14, ferCapan);
400     assertEquals(c, Color.white);
401     c = rs.findColour('S', 15, ferCapan);
402     assertEquals(c, Color.white);
403
404     /*
405      * deselect Above PID - colours should revert
406      */
407     af.abovePIDThreshold_actionPerformed(false);
408     c = rs.findColour('I', 14, ferCapan);
409     assertEquals(c, i_original);
410     c = rs.findColour('S', 15, ferCapan);
411     assertEquals(c, s_original);
412
413     /*
414      * Now combine Above 50% PID and By Conservation 10%
415      * 15.I is White because no match to consensus (V)
416      * 16.S is coloured but faded
417      */
418     af.abovePIDThreshold_actionPerformed(true);
419     sp = SliderPanel.getSliderPanel();
420     assertFalse(sp.isForConservation());
421     sp.valueChanged(50);
422     af.conservationMenuItem_actionPerformed(true);
423     sp = SliderPanel.getSliderPanel();
424     assertTrue(sp.isForConservation());
425     sp.valueChanged(10);
426     c = rs.findColour('I', 14, ferCapan);
427     assertEquals(c, Color.white);
428     c = rs.findColour('S', 15, ferCapan);
429     assertEquals(c, s_faded);
430
431     /*
432      * turn off Above PID - should just leave Conservation fading as before 
433      */
434     af.abovePIDThreshold_actionPerformed(false);
435     c = rs.findColour('I', 14, ferCapan);
436     assertEquals(c, i_faded);
437     c = rs.findColour('S', 15, ferCapan);
438     assertEquals(c, s_faded);
439
440     /*
441      * Now add Above 50% PID to conservation colouring
442      * - should give the same as PID followed by conservation (above)
443      */
444     af.abovePIDThreshold_actionPerformed(true);
445     SliderPanel.getSliderPanel().valueChanged(50);
446     c = rs.findColour('I', 14, ferCapan);
447     assertEquals(c, Color.white);
448     c = rs.findColour('S', 15, ferCapan);
449     assertEquals(c, s_faded);
450
451     /*
452      * turn off By Conservation
453      * should leave I white, S original (unfaded) colour
454      */
455     af.conservationMenuItem_actionPerformed(false);
456     c = rs.findColour('I', 14, ferCapan);
457     assertEquals(c, Color.white);
458     c = rs.findColour('S', 15, ferCapan);
459     assertEquals(c, s_original);
460
461     /*
462      * finally turn off Above PID to leave original colours
463      */
464     af.abovePIDThreshold_actionPerformed(false);
465     c = rs.findColour('I', 14, ferCapan);
466     assertEquals(c, i_original);
467     c = rs.findColour('S', 15, ferCapan);
468     assertEquals(c, s_original);
469   }
470
471   /**
472    * Verify that making a New View transfers alignment and group colour schemes,
473    * including any thresholds, to the new view. Because New View is performed by
474    * saving and reloading a 'project' file, this is similar to verifying a
475    * project save and reload.
476    * 
477    * @see Jalview2xmlTests#testStoreAndRecoverColourThresholds()
478    */
479   @Test(groups = "Functional")
480   public void testNewView_colourThresholds()
481   {
482     AlignViewport av = af.getViewport();
483     AlignmentI al = av.getAlignment();
484
485     /*
486      * Colour alignment by Buried Index, Above 10% PID, By Conservation 20%
487      */
488     af.changeColour_actionPerformed(JalviewColourScheme.Buried.toString());
489     assertTrue(av.getGlobalColourScheme() instanceof BuriedColourScheme);
490     af.abovePIDThreshold_actionPerformed(true);
491     SliderPanel sp = SliderPanel.getSliderPanel();
492     assertFalse(sp.isForConservation());
493     sp.valueChanged(10);
494     af.conservationMenuItem_actionPerformed(true);
495     sp = SliderPanel.getSliderPanel();
496     assertTrue(sp.isForConservation());
497     sp.valueChanged(20);
498     ResidueShaderI rs = av.getResidueShading();
499     assertEquals(rs.getThreshold(), 10);
500     assertTrue(rs.conservationApplied());
501     assertEquals(rs.getConservationInc(), 20);
502
503     /*
504      * create a group with Strand colouring, 30% Conservation
505      * and 40% PID threshold
506      */
507     SequenceGroup sg = new SequenceGroup();
508     sg.addSequence(al.getSequenceAt(0), false);
509     sg.setStartRes(15);
510     sg.setEndRes(25);
511     av.setSelectionGroup(sg);
512     PopupMenu popupMenu = new PopupMenu(af.alignPanel, null, null);
513     popupMenu.changeColour_actionPerformed(JalviewColourScheme.Strand
514             .toString());
515     assertTrue(sg.getColourScheme() instanceof StrandColourScheme);
516     assertEquals(al.getGroups().size(), 1);
517     assertSame(al.getGroups().get(0), sg);
518     popupMenu.conservationMenuItem_actionPerformed(true);
519     sp = SliderPanel.getSliderPanel();
520     assertTrue(sp.isForConservation());
521     sp.valueChanged(30);
522     popupMenu.abovePIDColour_actionPerformed(true);
523     sp = SliderPanel.getSliderPanel();
524     assertFalse(sp.isForConservation());
525     sp.valueChanged(40);
526     rs = sg.getGroupColourScheme();
527     assertTrue(rs.conservationApplied());
528     assertEquals(rs.getConservationInc(), 30);
529     assertEquals(rs.getThreshold(), 40);
530
531     /*
532      * set slider panel focus to the background alignment
533      */
534     af.conservationMenuItem_actionPerformed(true);
535     sp = SliderPanel.getSliderPanel();
536     assertTrue(sp.isForConservation());
537     assertEquals(sp.getTitle(), MessageManager.formatMessage(
538             "label.conservation_colour_increment",
539             new String[] { "Background" }));
540
541     /*
542      * make a new View, wait for Conservation thread to complete,
543      * verify alignment and group colour schemes
544      */
545     af.newView_actionPerformed(null);
546     assertEquals(af.alignPanel.getViewName(), "View 1");
547     AlignViewport av2 = af.getViewport();
548     assertNotSame(av, av2);
549     assertSame(av2, af.alignPanel.av);
550     AlignCalcManagerI calcManager = av2.getCalcManager();
551     do
552     {
553       try
554       {
555         Thread.sleep(50);
556       } catch (InterruptedException x)
557       {
558       }
559     } while (calcManager.isWorking());
560     rs = av2.getResidueShading();
561     assertNotSame(av.getResidueShading(), rs);
562     assertEquals(rs.getThreshold(), 10);
563     assertTrue(rs.conservationApplied(), rs.toString());
564     assertEquals(rs.getConservationInc(), 20);
565     assertEquals(av2.getAlignment().getGroups().size(), 1);
566     sg = av2.getAlignment().getGroups().get(0);
567     rs = sg.getGroupColourScheme();
568     assertTrue(rs.conservationApplied());
569     assertEquals(rs.getConservationInc(), 30);
570     assertEquals(rs.getThreshold(), 40);
571
572     /*
573      * check the Conservation SliderPanel (still open) is linked to 
574      * and updates the new view (JAL-2385)
575      */
576     sp = SliderPanel.getSliderPanel();
577     assertTrue(sp.isForConservation());
578     assertEquals(sp.getTitle(), MessageManager.formatMessage(
579             "label.conservation_colour_increment",
580             new String[] { "View 1" }));
581     sp.valueChanged(22);
582     assertEquals(av2.getResidueShading().getConservationInc(), 22);
583   }
584 }