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