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