c2983ec14b043df407daec0974e6cf089a02b468
[jalview.git] / test / jalview / gui / AlignViewportTest.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.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertFalse;
25 import static org.testng.AssertJUnit.assertNotNull;
26 import static org.testng.AssertJUnit.assertNotSame;
27 import static org.testng.AssertJUnit.assertSame;
28 import static org.testng.AssertJUnit.assertTrue;
29
30 import java.util.ArrayList;
31 import java.util.List;
32
33 import org.testng.Assert;
34 import org.testng.annotations.BeforeClass;
35 import org.testng.annotations.BeforeMethod;
36 import org.testng.annotations.Test;
37
38 import jalview.api.AlignCalcWorkerI;
39 import jalview.bin.Cache;
40 import jalview.bin.Jalview;
41 import jalview.datamodel.AlignedCodonFrame;
42 import jalview.datamodel.Alignment;
43 import jalview.datamodel.AlignmentAnnotation;
44 import jalview.datamodel.AlignmentI;
45 import jalview.datamodel.Annotation;
46 import jalview.datamodel.SearchResults;
47 import jalview.datamodel.SearchResultsI;
48 import jalview.datamodel.Sequence;
49 import jalview.datamodel.SequenceGroup;
50 import jalview.datamodel.SequenceI;
51 import jalview.io.DataSourceType;
52 import jalview.io.FileLoader;
53 import jalview.schemes.ClustalxColourScheme;
54 import jalview.schemes.ColourSchemeI;
55 import jalview.schemes.PIDColourScheme;
56 import jalview.structure.StructureSelectionManager;
57 import jalview.util.MapList;
58 import jalview.viewmodel.ViewportRanges;
59 import jalview.workers.AlignCalcManager;
60
61 public class AlignViewportTest
62 {
63
64   @BeforeClass(alwaysRun = true)
65   public void setUpJvOptionPane()
66   {
67     JvOptionPane.setInteractiveMode(false);
68     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
69   }
70
71   AlignmentI al;
72
73   AlignViewport testee;
74
75   @BeforeClass(alwaysRun = true)
76   public static void setUpBeforeClass() throws Exception
77   {
78     Jalview.main(new String[] {
79         //"-jabaws", "none", 
80         "-nonews", "-props",
81         "test/jalview/testProps.jvprops" });
82
83     /*
84      * remove any sequence mappings left lying around by other tests
85      */
86     StructureSelectionManager ssm = StructureSelectionManager
87             .getStructureSelectionManager(Desktop.getInstance());
88     ssm.resetAll();
89   }
90
91   @BeforeMethod(alwaysRun = true)
92   public void setUp()
93   {
94     SequenceI seq1 = new Sequence("Seq1", "ABC");
95     SequenceI seq2 = new Sequence("Seq2", "ABC");
96     SequenceI seq3 = new Sequence("Seq3", "ABC");
97     SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3 };
98     al = new Alignment(seqs);
99     al.setDataset(null);
100     testee = new AlignViewport(al);
101   }
102
103   /**
104    * Test that a mapping is not deregistered when a second view is closed but
105    * the first still holds a reference to the mapping
106    */
107   @Test(groups = { "Functional" })
108   public void testDeregisterMapping_onCloseView()
109   {
110     /*
111      * alignment with reference to mappings
112      */
113     AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
114             ">Seq1\nCAGT\n", DataSourceType.PASTE);
115
116     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
117     AlignedCodonFrame acf1 = new AlignedCodonFrame();
118     acf1.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 1, 4 },
119             1, 1));
120     AlignedCodonFrame acf2 = new AlignedCodonFrame();
121     acf2.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 4, 1 },
122             1, 1));
123
124     List<AlignedCodonFrame> mappings = new ArrayList<>();
125     mappings.add(acf1);
126     mappings.add(acf2);
127     af1.getViewport().getAlignment().setCodonFrames(mappings);
128     af1.newView_actionPerformed(null);
129
130     /*
131      * Verify that creating the alignment for the new View has registered the
132      * mappings
133      */
134     StructureSelectionManager ssm = StructureSelectionManager
135             .getStructureSelectionManager(Desktop.getInstance());
136     List<AlignedCodonFrame> sequenceMappings = ssm.getSequenceMappings();
137     assertEquals(2, sequenceMappings.size());
138     assertTrue(sequenceMappings.contains(acf1));
139     assertTrue(sequenceMappings.contains(acf2));
140
141     /*
142      * Close the second view. Verify that mappings are not removed as the first
143      * view still holds a reference to them.
144      */
145     af1.closeMenuItem_actionPerformed(false);
146     assertEquals(2, sequenceMappings.size());
147     assertTrue(sequenceMappings.contains(acf1));
148     assertTrue(sequenceMappings.contains(acf2));
149   }
150
151   /**
152    * Test that a mapping is deregistered if no alignment holds a reference to it
153    */
154   @Test(groups = { "Functional" })
155   public void testDeregisterMapping_withNoReference()
156   {
157     Desktop d = Desktop.getInstance();
158     assertNotNull(d);
159     StructureSelectionManager ssm = StructureSelectionManager
160             .getStructureSelectionManager(Desktop.getInstance());
161     ssm.resetAll();
162
163     AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
164             ">Seq1\nRSVQ\n", DataSourceType.PASTE);
165     AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
166             ">Seq2\nDGEL\n", DataSourceType.PASTE);
167     SequenceI cs1 = new Sequence("cseq1", "CCCGGGTTTAAA");
168     SequenceI cs2 = new Sequence("cseq2", "CTTGAGTCTAGA");
169     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
170     SequenceI s2 = af2.getViewport().getAlignment().getSequenceAt(0);
171     // need to be distinct
172     AlignedCodonFrame acf1 = new AlignedCodonFrame();
173     acf1.addMap(cs1, s1, new MapList(new int[] { 1, 4 },
174             new int[] { 1, 12 }, 1, 3));
175     AlignedCodonFrame acf2 = new AlignedCodonFrame();
176     acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 },
177             new int[] { 1, 12 }, 1, 3));
178     AlignedCodonFrame acf3 = new AlignedCodonFrame();
179     acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1,
180         12 }, 1, 1));
181
182     List<AlignedCodonFrame> mappings1 = new ArrayList<>();
183     mappings1.add(acf1);
184     af1.getViewport().getAlignment().setCodonFrames(mappings1);
185
186     List<AlignedCodonFrame> mappings2 = new ArrayList<>();
187     mappings2.add(acf2);
188     mappings2.add(acf3);
189     af2.getViewport().getAlignment().setCodonFrames(mappings2);
190
191     /*
192      * AlignFrame1 has mapping acf1, AlignFrame2 has acf2 and acf3
193      */
194
195     List<AlignedCodonFrame> ssmMappings = ssm.getSequenceMappings();
196     assertEquals(0, ssmMappings.size());
197     ssm.registerMapping(acf1);
198     assertEquals(1, ssmMappings.size());
199     ssm.registerMapping(acf2);
200     assertEquals(2, ssmMappings.size());
201     ssm.registerMapping(acf3);
202     assertEquals(3, ssmMappings.size());
203
204     /*
205      * Closing AlignFrame2 should remove its mappings from
206      * StructureSelectionManager, since AlignFrame1 has no reference to them
207      */
208     af2.closeMenuItem_actionPerformed(true);
209     assertEquals(1, ssmMappings.size());
210     assertTrue(ssmMappings.contains(acf1));
211   }
212
213   /**
214    * Test that a mapping is not deregistered if another alignment holds a
215    * reference to it
216    */
217   @Test(groups = { "Functional" })
218   public void testDeregisterMapping_withReference()
219   {
220     Desktop d = Desktop.getInstance();
221     assertNotNull(d);
222     StructureSelectionManager ssm = StructureSelectionManager
223             .getStructureSelectionManager(Desktop.getInstance());
224     ssm.resetAll();
225
226     AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
227             ">Seq1\nRSVQ\n", DataSourceType.PASTE);
228     AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
229             ">Seq2\nDGEL\n", DataSourceType.PASTE);
230     SequenceI cs1 = new Sequence("cseq1", "CCCGGGTTTAAA");
231     SequenceI cs2 = new Sequence("cseq2", "CTTGAGTCTAGA");
232     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
233     SequenceI s2 = af2.getViewport().getAlignment().getSequenceAt(0);
234     // need to be distinct
235     AlignedCodonFrame acf1 = new AlignedCodonFrame();
236     acf1.addMap(cs1, s1, new MapList(new int[] { 1, 4 },
237             new int[] { 1, 12 }, 1, 3));
238     AlignedCodonFrame acf2 = new AlignedCodonFrame();
239     acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 },
240             new int[] { 1, 12 }, 1, 3));
241     AlignedCodonFrame acf3 = new AlignedCodonFrame();
242     acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1,
243         12 }, 1, 1));
244
245     List<AlignedCodonFrame> mappings1 = new ArrayList<>();
246     mappings1.add(acf1);
247     mappings1.add(acf2);
248     af1.getViewport().getAlignment().setCodonFrames(mappings1);
249
250     List<AlignedCodonFrame> mappings2 = new ArrayList<>();
251     mappings2.add(acf2);
252     mappings2.add(acf3);
253     af2.getViewport().getAlignment().setCodonFrames(mappings2);
254
255     /*
256      * AlignFrame1 has mappings acf1 and acf2, AlignFrame2 has acf2 and acf3
257      */
258
259     List<AlignedCodonFrame> ssmMappings = ssm.getSequenceMappings();
260     assertEquals(0, ssmMappings.size());
261     ssm.registerMapping(acf1);
262     assertEquals(1, ssmMappings.size());
263     ssm.registerMapping(acf2);
264     assertEquals(2, ssmMappings.size());
265     ssm.registerMapping(acf3);
266     assertEquals(3, ssmMappings.size());
267
268     /*
269      * Closing AlignFrame2 should remove mapping acf3 from
270      * StructureSelectionManager, but not acf2, since AlignFrame1 still has a
271      * reference to it
272      */
273     af2.closeMenuItem_actionPerformed(true);
274     assertEquals(2, ssmMappings.size());
275     assertTrue(ssmMappings.contains(acf1));
276     assertTrue(ssmMappings.contains(acf2));
277     assertFalse(ssmMappings.contains(acf3));
278   }
279
280   /**
281    * Test for JAL-1306 - conservation thread should run even when only Quality
282    * (and not Conservation) is enabled in Preferences
283    */
284   @Test(groups = { "Functional" }, timeOut=2000)
285   public void testUpdateConservation_qualityOnly()
286   {
287     Cache.setPropertyNoSave("SHOW_ANNOTATIONS",
288             Boolean.TRUE.toString());
289     Cache.setPropertyNoSave("SHOW_QUALITY",
290             Boolean.TRUE.toString());
291     Cache.setPropertyNoSave("SHOW_CONSERVATION",
292             Boolean.FALSE.toString());
293     Cache.setPropertyNoSave("SHOW_OCCUPANCY",
294             Boolean.FALSE.toString());
295     Cache.setPropertyNoSave("SHOW_IDENTITY",
296             Boolean.FALSE.toString());
297     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
298             "examples/uniref50.fa", DataSourceType.FILE);
299
300     /*
301      * wait for Conservation thread to complete
302      */
303     AlignViewport viewport = af.getViewport();
304     waitForCalculations(viewport);
305     AlignmentAnnotation[] anns = viewport.getAlignment()
306             .getAlignmentAnnotation();
307     assertNotNull("No annotations found", anns);
308     assertEquals("More than one annotation found", 1, anns.length);
309     assertTrue("Annotation is not Quality",
310             anns[0].description.startsWith("Alignment Quality"));
311     Annotation[] annotations = anns[0].annotations;
312     assertNotNull("Quality annotations are null", annotations);
313     assertNotNull("Quality in column 1 is null", annotations[0]);
314     assertTrue("No quality value in column 1", annotations[0].value > 10f);
315   }
316
317   /**
318    * Wait for consensus etc calculation threads to complete
319    * 
320    * @param viewport
321    */
322   protected void waitForCalculations(AlignViewport viewport)
323   {
324     synchronized (this)
325     {
326       System.out.print("waiting...");
327       int n = 3;
328       while (--n >= 0 || viewport.getCalcManager().isWorking())
329       {
330         try
331         {
332           System.out.print(((AlignCalcManager) viewport.getCalcManager()).getQueueLength());
333           wait(50);
334         } catch (InterruptedException e)
335         {
336         }
337       }
338            System.out.println("...done");
339     }
340   }
341
342   @Test(groups = { "Functional" })
343   public void testSetGlobalColourScheme()
344   {
345     /*
346      * test for JAL-2283: don't inadvertently turn on colour by conservation
347      */
348     Cache.setPropertyNoSave("DEFAULT_COLOUR_PROT", "None");
349     Cache.setPropertyNoSave("SHOW_CONSERVATION",
350             Boolean.TRUE.toString());
351     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
352             "examples/uniref50.fa", DataSourceType.FILE);
353     ColourSchemeI cs = new PIDColourScheme();
354     AlignViewport viewport = af.getViewport();
355     viewport.setGlobalColourScheme(cs);
356     assertFalse(viewport.getResidueShading()
357             .conservationApplied());
358
359     /*
360      * JAL-3201 groups have their own ColourSchemeI instances
361      */
362     AlignmentI aln = viewport.getAlignment();
363     SequenceGroup sg1 = new SequenceGroup();
364     sg1.addSequence(aln.getSequenceAt(0), false);
365     sg1.addSequence(aln.getSequenceAt(2), false);
366     SequenceGroup sg2 = new SequenceGroup();
367     sg2.addSequence(aln.getSequenceAt(1), false);
368     sg2.addSequence(aln.getSequenceAt(3), false);
369     aln.addGroup(sg1);
370     aln.addGroup(sg2);
371     viewport.setColourAppliesToAllGroups(true);
372     viewport.setGlobalColourScheme(new ClustalxColourScheme());
373     ColourSchemeI cs0 = viewport.getGlobalColourScheme();
374     ColourSchemeI cs1 = sg1.getColourScheme();
375     ColourSchemeI cs2 = sg2.getColourScheme();
376     assertTrue(cs0 instanceof ClustalxColourScheme);
377     assertTrue(cs1 instanceof ClustalxColourScheme);
378     assertTrue(cs2 instanceof ClustalxColourScheme);
379     assertNotSame(cs0, cs1);
380     assertNotSame(cs0, cs2);
381     assertNotSame(cs1, cs2);
382   }
383
384   @Test(groups = { "Functional" })
385   public void testSetGetHasSearchResults()
386   {
387     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
388             "examples/uniref50.fa", DataSourceType.FILE);
389     SearchResultsI sr = new SearchResults();
390     SequenceI s1 = af.getViewport().getAlignment().getSequenceAt(0);
391
392     // create arbitrary range on first sequence
393     sr.addResult(s1, s1.getStart() + 10, s1.getStart() + 15);
394
395     // test set
396     af.getViewport().setSearchResults(sr);
397     // has -> true
398     assertTrue(af.getViewport().hasSearchResults());
399     // get == original
400     assertEquals(sr, af.getViewport().getSearchResults());
401
402     // set(null) results in has -> false
403
404     af.getViewport().setSearchResults(null);
405     assertFalse(af.getViewport().hasSearchResults());
406   }
407
408   /**
409    * Verify that setting the selection group has the side-effect of setting the
410    * context on the group, unless it already has one, but does not change
411    * whether the group is defined or not.
412    */
413   @Test(groups = { "Functional" })
414   public void testSetSelectionGroup()
415   {
416     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
417             "examples/uniref50.fa", DataSourceType.FILE);
418     AlignViewport av = af.getViewport();
419     SequenceGroup sg1 = new SequenceGroup();
420     SequenceGroup sg2 = new SequenceGroup();
421     SequenceGroup sg3 = new SequenceGroup();
422
423     av.setSelectionGroup(sg1);
424     assertSame(sg1.getContext(), av.getAlignment()); // context set
425     assertFalse(sg1.isDefined()); // group not defined
426
427     sg2.setContext(sg1, false);
428     av.setSelectionGroup(sg2);
429     assertFalse(sg2.isDefined()); // unchanged
430     assertSame(sg2.getContext(), sg1); // unchanged
431
432     // create a defined group
433     sg3.setContext(av.getAlignment(), true);
434     av.setSelectionGroup(sg3);
435     assertTrue(sg3.isDefined()); // unchanged
436   }
437   /**
438    * Verify that setting/clearing SHOW_OCCUPANCY preference adds or omits occupancy row from viewport
439    */
440   @Test(groups = { "Functional" })
441   public void testShowOrDontShowOccupancy()
442   {
443     // disable occupancy
444     jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.FALSE.toString());
445     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
446             "examples/uniref50.fa", DataSourceType.FILE);
447     AlignViewport av = af.getViewport();
448     Assert.assertNull(av.getAlignmentGapAnnotation(), "Preference did not disable occupancy row.");
449     int c = 0;
450     for (AlignmentAnnotation aa : av.getAlignment().findAnnotations(null,
451             null, "Occupancy"))
452     {
453       c++;
454     }
455     Assert.assertEquals(c, 0, "Expected zero occupancy rows.");
456     
457     // enable occupancy
458     jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.TRUE.toString());
459     af = new FileLoader().LoadFileWaitTillLoaded(
460             "examples/uniref50.fa", DataSourceType.FILE);
461     av = af.getViewport();
462     Assert.assertNotNull(av.getAlignmentGapAnnotation(), "Preference did not enable occupancy row.");
463     c = 0;
464     for (AlignmentAnnotation aa : av.getAlignment().findAnnotations(null,
465             null, av.getAlignmentGapAnnotation().label))
466     {
467       c++;
468     }
469     ;
470     Assert.assertEquals(c, 1, "Expected to find one occupancy row.");
471   }
472
473   @Test(groups = { "Functional" })
474   public void testGetConsensusSeq()
475   {
476     /*
477      * A-C
478      * A-C
479      * A-D
480      * --D
481      * consensus expected to be A-C
482      */
483     String fasta = ">s1\nA-C\n>s2\nA-C\n>s3\nA-D\n>s4\n--D\n";
484     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(fasta,
485             DataSourceType.PASTE);
486     AlignViewport testme = af.getViewport();
487     waitForCalculations(testme);
488     SequenceI cons = testme.getConsensusSeq();
489     String s = cons.getSequenceAsString();
490     System.out.println("s is " + s);
491     
492     assertEquals("A-C", s);
493   }
494
495   @Test(groups = { "Functional" })
496   public void testHideRevealSequences()
497   {
498     ViewportRanges ranges = testee.getRanges();
499     assertEquals(3, al.getHeight());
500     assertEquals(0, ranges.getStartSeq());
501     assertEquals(2, ranges.getEndSeq());
502
503     /*
504      * hide first sequence
505      */
506     testee.hideSequence(new SequenceI[] { al.getSequenceAt(0) });
507     assertEquals(2, al.getHeight());
508     assertEquals(0, ranges.getStartSeq());
509     assertEquals(1, ranges.getEndSeq());
510
511     /*
512      * reveal hidden sequences above the first
513      */
514     testee.showSequence(0);
515     assertEquals(3, al.getHeight());
516     assertEquals(0, ranges.getStartSeq());
517     assertEquals(2, ranges.getEndSeq());
518
519     /*
520      * hide first and third sequences
521      */
522     testee.hideSequence(new SequenceI[] { al.getSequenceAt(0),
523         al.getSequenceAt(2) });
524     assertEquals(1, al.getHeight());
525     assertEquals(0, ranges.getStartSeq());
526     assertEquals(0, ranges.getEndSeq());
527
528     /*
529      * reveal all hidden sequences
530      */
531     testee.showAllHiddenSeqs();
532     assertEquals(3, al.getHeight());
533     assertEquals(0, ranges.getStartSeq());
534     assertEquals(2, ranges.getEndSeq());
535   }
536 }