Merge branch 'JAL-3878_web_services_overhaul' into try-to-update-slivka-jar
[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 java.util.ArrayList;
32 import java.util.List;
33
34 import org.testng.Assert;
35 import org.testng.annotations.BeforeClass;
36 import org.testng.annotations.BeforeMethod;
37 import org.testng.annotations.Test;
38
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.AlignmentViewport;
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   AlignmentViewport 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.getInstance());
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().LoadFileWaitTillLoaded(
113             ">Seq1\nCAGT\n", DataSourceType.PASTE);
114
115     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
116     AlignedCodonFrame acf1 = new AlignedCodonFrame();
117     acf1.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 1, 4 },
118             1, 1));
119     AlignedCodonFrame acf2 = new AlignedCodonFrame();
120     acf2.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 4, 1 },
121             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.getInstance());
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.getInstance();
157     assertNotNull(d);
158     StructureSelectionManager ssm = StructureSelectionManager
159             .getStructureSelectionManager(Desktop.getInstance());
160     ssm.resetAll();
161
162     AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
163             ">Seq1\nRSVQ\n", DataSourceType.PASTE);
164     AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
165             ">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, new MapList(new int[] { 1, 4 },
173             new int[] { 1, 12 }, 1, 3));
174     AlignedCodonFrame acf2 = new AlignedCodonFrame();
175     acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 },
176             new int[] { 1, 12 }, 1, 3));
177     AlignedCodonFrame acf3 = new AlignedCodonFrame();
178     acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1,
179         12 }, 1, 1));
180
181     List<AlignedCodonFrame> mappings1 = new ArrayList<>();
182     mappings1.add(acf1);
183     af1.getViewport().getAlignment().setCodonFrames(mappings1);
184
185     List<AlignedCodonFrame> mappings2 = new ArrayList<>();
186     mappings2.add(acf2);
187     mappings2.add(acf3);
188     af2.getViewport().getAlignment().setCodonFrames(mappings2);
189
190     /*
191      * AlignFrame1 has mapping acf1, AlignFrame2 has acf2 and acf3
192      */
193
194     List<AlignedCodonFrame> ssmMappings = ssm.getSequenceMappings();
195     assertEquals(0, ssmMappings.size());
196     ssm.registerMapping(acf1);
197     assertEquals(1, ssmMappings.size());
198     ssm.registerMapping(acf2);
199     assertEquals(2, ssmMappings.size());
200     ssm.registerMapping(acf3);
201     assertEquals(3, ssmMappings.size());
202
203     /*
204      * Closing AlignFrame2 should remove its mappings from
205      * StructureSelectionManager, since AlignFrame1 has no reference to them
206      */
207     af2.closeMenuItem_actionPerformed(true);
208     assertEquals(1, ssmMappings.size());
209     assertTrue(ssmMappings.contains(acf1));
210   }
211
212   /**
213    * Test that a mapping is not deregistered if another alignment holds a
214    * reference to it
215    */
216   @Test(groups = { "Functional" })
217   public void testDeregisterMapping_withReference()
218   {
219     Desktop d = Desktop.getInstance();
220     assertNotNull(d);
221     StructureSelectionManager ssm = StructureSelectionManager
222             .getStructureSelectionManager(Desktop.getInstance());
223     ssm.resetAll();
224
225     AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
226             ">Seq1\nRSVQ\n", DataSourceType.PASTE);
227     AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
228             ">Seq2\nDGEL\n", DataSourceType.PASTE);
229     SequenceI cs1 = new Sequence("cseq1", "CCCGGGTTTAAA");
230     SequenceI cs2 = new Sequence("cseq2", "CTTGAGTCTAGA");
231     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
232     SequenceI s2 = af2.getViewport().getAlignment().getSequenceAt(0);
233     // need to be distinct
234     AlignedCodonFrame acf1 = new AlignedCodonFrame();
235     acf1.addMap(cs1, s1, new MapList(new int[] { 1, 4 },
236             new int[] { 1, 12 }, 1, 3));
237     AlignedCodonFrame acf2 = new AlignedCodonFrame();
238     acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 },
239             new int[] { 1, 12 }, 1, 3));
240     AlignedCodonFrame acf3 = new AlignedCodonFrame();
241     acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1,
242         12 }, 1, 1));
243
244     List<AlignedCodonFrame> mappings1 = new ArrayList<>();
245     mappings1.add(acf1);
246     mappings1.add(acf2);
247     af1.getViewport().getAlignment().setCodonFrames(mappings1);
248
249     List<AlignedCodonFrame> mappings2 = new ArrayList<>();
250     mappings2.add(acf2);
251     mappings2.add(acf3);
252     af2.getViewport().getAlignment().setCodonFrames(mappings2);
253
254     /*
255      * AlignFrame1 has mappings acf1 and acf2, AlignFrame2 has acf2 and acf3
256      */
257
258     List<AlignedCodonFrame> ssmMappings = ssm.getSequenceMappings();
259     assertEquals(0, ssmMappings.size());
260     ssm.registerMapping(acf1);
261     assertEquals(1, ssmMappings.size());
262     ssm.registerMapping(acf2);
263     assertEquals(2, ssmMappings.size());
264     ssm.registerMapping(acf3);
265     assertEquals(3, ssmMappings.size());
266
267     /*
268      * Closing AlignFrame2 should remove mapping acf3 from
269      * StructureSelectionManager, but not acf2, since AlignFrame1 still has a
270      * reference to it
271      */
272     af2.closeMenuItem_actionPerformed(true);
273     assertEquals(2, ssmMappings.size());
274     assertTrue(ssmMappings.contains(acf1));
275     assertTrue(ssmMappings.contains(acf2));
276     assertFalse(ssmMappings.contains(acf3));
277   }
278
279   /**
280    * Test for JAL-1306 - conservation thread should run even when only Quality
281    * (and not Conservation) is enabled in Preferences
282    */
283   @Test(groups = { "Functional" }, timeOut=2000)
284   public void testUpdateConservation_qualityOnly()
285   {
286     Cache.setPropertyNoSave("SHOW_ANNOTATIONS",
287             Boolean.TRUE.toString());
288     Cache.setPropertyNoSave("SHOW_QUALITY",
289             Boolean.TRUE.toString());
290     Cache.setPropertyNoSave("SHOW_CONSERVATION",
291             Boolean.FALSE.toString());
292     Cache.setPropertyNoSave("SHOW_OCCUPANCY",
293             Boolean.FALSE.toString());
294     Cache.setPropertyNoSave("SHOW_IDENTITY",
295             Boolean.FALSE.toString());
296     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
297             "examples/uniref50.fa", DataSourceType.FILE);
298
299     /*
300      * wait for Conservation thread to complete
301      */
302     AlignViewport viewport = af.getViewport();
303     waitForCalculations(viewport);
304     AlignmentAnnotation[] anns = viewport.getAlignment()
305             .getAlignmentAnnotation();
306     assertNotNull("No annotations found", anns);
307     assertEquals("More than one annotation found", 1, anns.length);
308     assertTrue("Annotation is not Quality",
309             anns[0].description.startsWith("Alignment Quality"));
310     Annotation[] annotations = anns[0].annotations;
311     assertNotNull("Quality annotations are null", annotations);
312     assertNotNull("Quality in column 1 is null", annotations[0]);
313     assertTrue("No quality value in column 1", annotations[0].value > 10f);
314   }
315
316   /**
317    * Wait for consensus etc calculation threads to complete
318    * 
319    * @param viewport
320    */
321   protected void waitForCalculations(AlignViewport viewport)
322   {
323     synchronized (this)
324     {
325       System.out.print("waiting...");
326       int n = 3;
327       while (--n >= 0 || viewport.getCalcManager().isWorking())
328       {
329         try
330         {
331           wait(50);
332         } catch (InterruptedException e)
333         {
334         }
335       }
336            System.out.println("...done");
337     }
338   }
339
340   @Test(groups = { "Functional" })
341   public void testSetGlobalColourScheme()
342   {
343     /*
344      * test for JAL-2283: don't inadvertently turn on colour by conservation
345      */
346     Cache.setPropertyNoSave("DEFAULT_COLOUR_PROT", "None");
347     Cache.setPropertyNoSave("SHOW_CONSERVATION",
348             Boolean.TRUE.toString());
349     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
350             "examples/uniref50.fa", DataSourceType.FILE);
351     ColourSchemeI cs = new PIDColourScheme();
352     AlignViewport viewport = af.getViewport();
353     viewport.setGlobalColourScheme(cs);
354     assertFalse(viewport.getResidueShading()
355             .conservationApplied());
356
357     /*
358      * JAL-3201 groups have their own ColourSchemeI instances
359      */
360     AlignmentI aln = viewport.getAlignment();
361     SequenceGroup sg1 = new SequenceGroup();
362     sg1.addSequence(aln.getSequenceAt(0), false);
363     sg1.addSequence(aln.getSequenceAt(2), false);
364     SequenceGroup sg2 = new SequenceGroup();
365     sg2.addSequence(aln.getSequenceAt(1), false);
366     sg2.addSequence(aln.getSequenceAt(3), false);
367     aln.addGroup(sg1);
368     aln.addGroup(sg2);
369     viewport.setColourAppliesToAllGroups(true);
370     viewport.setGlobalColourScheme(new ClustalxColourScheme());
371     ColourSchemeI cs0 = viewport.getGlobalColourScheme();
372     ColourSchemeI cs1 = sg1.getColourScheme();
373     ColourSchemeI cs2 = sg2.getColourScheme();
374     assertTrue(cs0 instanceof ClustalxColourScheme);
375     assertTrue(cs1 instanceof ClustalxColourScheme);
376     assertTrue(cs2 instanceof ClustalxColourScheme);
377     assertNotSame(cs0, cs1);
378     assertNotSame(cs0, cs2);
379     assertNotSame(cs1, cs2);
380   }
381
382   @Test(groups = { "Functional" })
383   public void testSetGetHasSearchResults()
384   {
385     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
386             "examples/uniref50.fa", DataSourceType.FILE);
387     SearchResultsI sr = new SearchResults();
388     SequenceI s1 = af.getViewport().getAlignment().getSequenceAt(0);
389
390     // create arbitrary range on first sequence
391     sr.addResult(s1, s1.getStart() + 10, s1.getStart() + 15);
392
393     // test set
394     af.getViewport().setSearchResults(sr);
395     // has -> true
396     assertTrue(af.getViewport().hasSearchResults());
397     // get == original
398     assertEquals(sr, af.getViewport().getSearchResults());
399
400     // set(null) results in has -> false
401
402     af.getViewport().setSearchResults(null);
403     assertFalse(af.getViewport().hasSearchResults());
404   }
405
406   /**
407    * Verify that setting the selection group has the side-effect of setting the
408    * context on the group, unless it already has one, but does not change
409    * whether the group is defined or not.
410    */
411   @Test(groups = { "Functional" })
412   public void testSetSelectionGroup()
413   {
414     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
415             "examples/uniref50.fa", DataSourceType.FILE);
416     AlignViewportI av = af.getViewport();
417     SequenceGroup sg1 = new SequenceGroup();
418     SequenceGroup sg2 = new SequenceGroup();
419     SequenceGroup sg3 = new SequenceGroup();
420
421     av.setSelectionGroup(sg1);
422     assertSame(sg1.getContext(), av.getAlignment()); // context set
423     assertFalse(sg1.isDefined()); // group not defined
424
425     sg2.setContext(sg1, false);
426     av.setSelectionGroup(sg2);
427     assertFalse(sg2.isDefined()); // unchanged
428     assertSame(sg2.getContext(), sg1); // unchanged
429
430     // create a defined group
431     sg3.setContext(av.getAlignment(), true);
432     av.setSelectionGroup(sg3);
433     assertTrue(sg3.isDefined()); // unchanged
434   }
435   /**
436    * Verify that setting/clearing SHOW_OCCUPANCY preference adds or omits occupancy row from viewport
437    */
438   @Test(groups = { "Functional" })
439   public void testShowOrDontShowOccupancy()
440   {
441     // disable occupancy
442     jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.FALSE.toString());
443     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
444             "examples/uniref50.fa", DataSourceType.FILE);
445     AlignViewportI av = af.getViewport();
446     Assert.assertNull(av.getAlignmentGapAnnotation(),
447             "Preference did not disable occupancy row.");
448     int c = 0;
449     for (AlignmentAnnotation aa : av.getAlignment().findAnnotations(null,
450             null, "Occupancy"))
451     {
452       c++;
453     }
454     Assert.assertEquals(c, 0, "Expected zero occupancy rows.");
455     
456     // enable occupancy
457     jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.TRUE.toString());
458     af = new FileLoader().LoadFileWaitTillLoaded(
459             "examples/uniref50.fa", DataSourceType.FILE);
460     av = af.getViewport();
461     Assert.assertNotNull(av.getAlignmentGapAnnotation(),
462             "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     assertEquals("A-C", cons.getSequenceAsString());
490   }
491
492   @Test(groups = { "Functional" })
493   public void testHideRevealSequences()
494   {
495     ViewportRanges ranges = testee.getRanges();
496     assertEquals(3, al.getHeight());
497     assertEquals(0, ranges.getStartSeq());
498     assertEquals(2, ranges.getEndSeq());
499
500     /*
501      * hide first sequence
502      */
503     testee.hideSequence(new SequenceI[] { al.getSequenceAt(0) });
504     assertEquals(2, al.getHeight());
505     assertEquals(0, ranges.getStartSeq());
506     assertEquals(1, ranges.getEndSeq());
507
508     /*
509      * reveal hidden sequences above the first
510      */
511     testee.showSequence(0);
512     assertEquals(3, al.getHeight());
513     assertEquals(0, ranges.getStartSeq());
514     assertEquals(2, ranges.getEndSeq());
515
516     /*
517      * hide first and third sequences
518      */
519     testee.hideSequence(
520             new SequenceI[]
521             { al.getSequenceAt(0), al.getSequenceAt(2) });
522     assertEquals(1, al.getHeight());
523     assertEquals(0, ranges.getStartSeq());
524     assertEquals(0, ranges.getEndSeq());
525
526     /*
527      * reveal all hidden sequences
528      */
529     testee.showAllHiddenSeqs();
530     assertEquals(3, al.getHeight());
531     assertEquals(0, ranges.getStartSeq());
532     assertEquals(2, ranges.getEndSeq());
533   }
534 }