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