JAL-3690 Let's enable web services (seriously this time)
[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(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     waitForCalculations(viewport);
303     AlignmentAnnotation[] anns = viewport.getAlignment()
304             .getAlignmentAnnotation();
305     assertNotNull("No annotations found", anns);
306     assertEquals("More than one annotation found", 1, anns.length);
307     assertTrue("Annotation is not Quality",
308             anns[0].description.startsWith("Alignment Quality"));
309     Annotation[] annotations = anns[0].annotations;
310     assertNotNull("Quality annotations are null", annotations);
311     assertNotNull("Quality in column 1 is null", annotations[0]);
312     assertTrue("No quality value in column 1", annotations[0].value > 10f);
313   }
314
315   /**
316    * Wait for consensus etc calculation threads to complete
317    * 
318    * @param viewport
319    */
320   protected void waitForCalculations(AlignViewport viewport)
321   {
322     synchronized (this)
323     {
324       while (viewport.getCalcManager().isWorking())
325       {
326         try
327         {
328           wait(50);
329         } catch (InterruptedException e)
330         {
331         }
332       }
333     }
334   }
335
336   @Test(groups = { "Functional" })
337   public void testSetGlobalColourScheme()
338   {
339     /*
340      * test for JAL-2283: don't inadvertently turn on colour by conservation
341      */
342     Cache.applicationProperties.setProperty("DEFAULT_COLOUR_PROT", "None");
343     Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
344             Boolean.TRUE.toString());
345     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
346             "examples/uniref50.fa", DataSourceType.FILE);
347     ColourSchemeI cs = new PIDColourScheme();
348     AlignViewport viewport = af.getViewport();
349     viewport.setGlobalColourScheme(cs);
350     assertFalse(viewport.getResidueShading()
351             .conservationApplied());
352
353     /*
354      * JAL-3201 groups have their own ColourSchemeI instances
355      */
356     AlignmentI aln = viewport.getAlignment();
357     SequenceGroup sg1 = new SequenceGroup();
358     sg1.addSequence(aln.getSequenceAt(0), false);
359     sg1.addSequence(aln.getSequenceAt(2), false);
360     SequenceGroup sg2 = new SequenceGroup();
361     sg2.addSequence(aln.getSequenceAt(1), false);
362     sg2.addSequence(aln.getSequenceAt(3), false);
363     aln.addGroup(sg1);
364     aln.addGroup(sg2);
365     viewport.setColourAppliesToAllGroups(true);
366     viewport.setGlobalColourScheme(new ClustalxColourScheme());
367     ColourSchemeI cs0 = viewport.getGlobalColourScheme();
368     ColourSchemeI cs1 = sg1.getColourScheme();
369     ColourSchemeI cs2 = sg2.getColourScheme();
370     assertTrue(cs0 instanceof ClustalxColourScheme);
371     assertTrue(cs1 instanceof ClustalxColourScheme);
372     assertTrue(cs2 instanceof ClustalxColourScheme);
373     assertNotSame(cs0, cs1);
374     assertNotSame(cs0, cs2);
375     assertNotSame(cs1, cs2);
376   }
377
378   @Test(groups = { "Functional" })
379   public void testSetGetHasSearchResults()
380   {
381     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
382             "examples/uniref50.fa", DataSourceType.FILE);
383     SearchResultsI sr = new SearchResults();
384     SequenceI s1 = af.getViewport().getAlignment().getSequenceAt(0);
385
386     // create arbitrary range on first sequence
387     sr.addResult(s1, s1.getStart() + 10, s1.getStart() + 15);
388
389     // test set
390     af.getViewport().setSearchResults(sr);
391     // has -> true
392     assertTrue(af.getViewport().hasSearchResults());
393     // get == original
394     assertEquals(sr, af.getViewport().getSearchResults());
395
396     // set(null) results in has -> false
397
398     af.getViewport().setSearchResults(null);
399     assertFalse(af.getViewport().hasSearchResults());
400   }
401
402   /**
403    * Verify that setting the selection group has the side-effect of setting the
404    * context on the group, unless it already has one, but does not change
405    * whether the group is defined or not.
406    */
407   @Test(groups = { "Functional" })
408   public void testSetSelectionGroup()
409   {
410     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
411             "examples/uniref50.fa", DataSourceType.FILE);
412     AlignViewportI av = af.getViewport();
413     SequenceGroup sg1 = new SequenceGroup();
414     SequenceGroup sg2 = new SequenceGroup();
415     SequenceGroup sg3 = new SequenceGroup();
416
417     av.setSelectionGroup(sg1);
418     assertSame(sg1.getContext(), av.getAlignment()); // context set
419     assertFalse(sg1.isDefined()); // group not defined
420
421     sg2.setContext(sg1, false);
422     av.setSelectionGroup(sg2);
423     assertFalse(sg2.isDefined()); // unchanged
424     assertSame(sg2.getContext(), sg1); // unchanged
425
426     // create a defined group
427     sg3.setContext(av.getAlignment(), true);
428     av.setSelectionGroup(sg3);
429     assertTrue(sg3.isDefined()); // unchanged
430   }
431   /**
432    * Verify that setting/clearing SHOW_OCCUPANCY preference adds or omits occupancy row from viewport
433    */
434   @Test(groups = { "Functional" })
435   public void testShowOrDontShowOccupancy()
436   {
437     // disable occupancy
438     jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.FALSE.toString());
439     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
440             "examples/uniref50.fa", DataSourceType.FILE);
441     AlignViewportI av = af.getViewport();
442     Assert.assertNull(av.getAlignmentGapAnnotation(),
443             "Preference did not disable occupancy row.");
444     int c = 0;
445     for (AlignmentAnnotation aa : av.getAlignment().findAnnotations(null,
446             null, "Occupancy"))
447     {
448       c++;
449     }
450     Assert.assertEquals(c, 0, "Expected zero occupancy rows.");
451     
452     // enable occupancy
453     jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.TRUE.toString());
454     af = new FileLoader().LoadFileWaitTillLoaded(
455             "examples/uniref50.fa", DataSourceType.FILE);
456     av = af.getViewport();
457     Assert.assertNotNull(av.getAlignmentGapAnnotation(),
458             "Preference did not enable occupancy row.");
459     c = 0;
460     for (AlignmentAnnotation aa : av.getAlignment().findAnnotations(null,
461             null, av.getAlignmentGapAnnotation().label))
462     {
463       c++;
464     }
465     ;
466     Assert.assertEquals(c, 1, "Expected to find one occupancy row.");
467   }
468
469   @Test(groups = { "Functional" })
470   public void testGetConsensusSeq()
471   {
472     /*
473      * A-C
474      * A-C
475      * A-D
476      * --D
477      * consensus expected to be A-C
478      */
479     String fasta = ">s1\nA-C\n>s2\nA-C\n>s3\nA-D\n>s4\n--D\n";
480     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(fasta,
481             DataSourceType.PASTE);
482     AlignViewport testme = af.getViewport();
483     waitForCalculations(testme);
484     SequenceI cons = testme.getConsensusSeq();
485     assertEquals("A-C", cons.getSequenceAsString());
486   }
487
488   @Test(groups = { "Functional" })
489   public void testHideRevealSequences()
490   {
491     ViewportRanges ranges = testee.getRanges();
492     assertEquals(3, al.getHeight());
493     assertEquals(0, ranges.getStartSeq());
494     assertEquals(2, ranges.getEndSeq());
495
496     /*
497      * hide first sequence
498      */
499     testee.hideSequence(new SequenceI[] { al.getSequenceAt(0) });
500     assertEquals(2, al.getHeight());
501     assertEquals(0, ranges.getStartSeq());
502     assertEquals(1, ranges.getEndSeq());
503
504     /*
505      * reveal hidden sequences above the first
506      */
507     testee.showSequence(0);
508     assertEquals(3, al.getHeight());
509     assertEquals(0, ranges.getStartSeq());
510     assertEquals(2, ranges.getEndSeq());
511
512     /*
513      * hide first and third sequences
514      */
515     testee.hideSequence(new SequenceI[] { al.getSequenceAt(0),
516         al.getSequenceAt(2) });
517     assertEquals(1, al.getHeight());
518     assertEquals(0, ranges.getStartSeq());
519     assertEquals(0, ranges.getEndSeq());
520
521     /*
522      * reveal all hidden sequences
523      */
524     testee.showAllHiddenSeqs();
525     assertEquals(3, al.getHeight());
526     assertEquals(0, ranges.getStartSeq());
527     assertEquals(2, ranges.getEndSeq());
528   }
529 }