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