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