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