JAL-2034 Added isDefined flag to SequenceGroup
[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.assertSame;
27 import static org.testng.AssertJUnit.assertTrue;
28
29 import jalview.bin.Cache;
30 import jalview.bin.Jalview;
31 import jalview.datamodel.AlignedCodonFrame;
32 import jalview.datamodel.Alignment;
33 import jalview.datamodel.AlignmentAnnotation;
34 import jalview.datamodel.AlignmentI;
35 import jalview.datamodel.Annotation;
36 import jalview.datamodel.PDBEntry;
37 import jalview.datamodel.PDBEntry.Type;
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.ColourSchemeI;
46 import jalview.schemes.PIDColourScheme;
47 import jalview.structure.StructureSelectionManager;
48 import jalview.util.MapList;
49
50 import java.util.ArrayList;
51 import java.util.List;
52
53 import org.testng.Assert;
54 import org.testng.annotations.BeforeClass;
55 import org.testng.annotations.BeforeMethod;
56 import org.testng.annotations.Test;
57
58 public class AlignViewportTest
59 {
60
61   @BeforeClass(alwaysRun = true)
62   public void setUpJvOptionPane()
63   {
64     JvOptionPane.setInteractiveMode(false);
65     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
66   }
67
68   AlignmentI al;
69
70   AlignViewport testee;
71
72   @BeforeClass(alwaysRun = true)
73   public static void setUpBeforeClass() throws Exception
74   {
75     Jalview.main(new String[] { "-nonews", "-props",
76         "test/jalview/testProps.jvprops" });
77   }
78
79   @BeforeMethod(alwaysRun = true)
80   public void setUp()
81   {
82     SequenceI seq1 = new Sequence("Seq1", "ABC");
83     SequenceI seq2 = new Sequence("Seq2", "ABC");
84     SequenceI seq3 = new Sequence("Seq3", "ABC");
85     SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3 };
86     al = new Alignment(seqs);
87     al.setDataset(null);
88     testee = new AlignViewport(al);
89   }
90
91   @Test(groups = { "Functional" })
92   public void testCollateForPdb()
93   {
94     // JBP: What behaviour is this supposed to test ?
95     /*
96      * Set up sequence pdb ids
97      */
98     PDBEntry pdb1 = new PDBEntry("1ABC", "B", Type.PDB, "1ABC.pdb");
99     PDBEntry pdb2 = new PDBEntry("2ABC", "C", Type.PDB, "2ABC.pdb");
100     PDBEntry pdb3 = new PDBEntry("3ABC", "D", Type.PDB, "3ABC.pdb");
101
102     /*
103      * seq1 and seq3 refer to 1abcB, seq2 to 2abcC, none to 3abcD
104      */
105     al.getSequenceAt(0).getDatasetSequence()
106             .addPDBId(new PDBEntry("1ABC", "B", Type.PDB, "1ABC.pdb"));
107     al.getSequenceAt(2).getDatasetSequence()
108             .addPDBId(new PDBEntry("1ABC", "B", Type.PDB, "1ABC.pdb"));
109     al.getSequenceAt(1).getDatasetSequence()
110             .addPDBId(new PDBEntry("2ABC", "C", Type.PDB, "2ABC.pdb"));
111     /*
112      * Add a second chain PDB xref to Seq2 - should not result in a duplicate in
113      * the results
114      */
115     al.getSequenceAt(1).getDatasetSequence()
116             .addPDBId(new PDBEntry("2ABC", "D", Type.PDB, "2ABC.pdb"));
117     /*
118      * Seq3 refers to 3abc - this does not match 3ABC (as the code stands)
119      */
120     al.getSequenceAt(2).getDatasetSequence()
121             .addPDBId(new PDBEntry("3abc", "D", Type.PDB, "3ABC.pdb"));
122
123     /*
124      * run method under test
125      */
126     SequenceI[][] seqs = testee.collateForPDB(new PDBEntry[] { pdb1, pdb2,
127         pdb3 });
128
129     // seq1 and seq3 refer to PDBEntry[0]
130     assertEquals(2, seqs[0].length);
131     assertSame(al.getSequenceAt(0), seqs[0][0]);
132     assertSame(al.getSequenceAt(2), seqs[0][1]);
133
134     // seq2 refers to PDBEntry[1]
135     assertEquals(1, seqs[1].length);
136     assertSame(al.getSequenceAt(1), seqs[1][0]);
137
138     // no sequence refers to PDBEntry[2]
139     assertEquals(0, seqs[2].length);
140   }
141
142   /**
143    * Test that a mapping is not deregistered when a second view is closed but
144    * the first still holds a reference to the mapping
145    */
146   @Test(groups = { "Functional" })
147   public void testDeregisterMapping_onCloseView()
148   {
149     /*
150      * alignment with reference to mappings
151      */
152     AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
153             ">Seq1\nCAGT\n", DataSourceType.PASTE);
154
155     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
156     AlignedCodonFrame acf1 = new AlignedCodonFrame();
157     acf1.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 1, 4 },
158             1, 1));
159     AlignedCodonFrame acf2 = new AlignedCodonFrame();
160     acf2.addMap(s1, s1, new MapList(new int[] { 1, 4 }, new int[] { 4, 1 },
161             1, 1));
162
163     List<AlignedCodonFrame> mappings = new ArrayList<>();
164     mappings.add(acf1);
165     mappings.add(acf2);
166     af1.getViewport().getAlignment().setCodonFrames(mappings);
167     af1.newView_actionPerformed(null);
168
169     /*
170      * Verify that creating the alignment for the new View has registered the
171      * mappings
172      */
173     StructureSelectionManager ssm = StructureSelectionManager
174             .getStructureSelectionManager(Desktop.instance);
175     assertEquals(2, ssm.getSequenceMappings().size());
176     assertTrue(ssm.getSequenceMappings().contains(acf1));
177     assertTrue(ssm.getSequenceMappings().contains(acf2));
178
179     /*
180      * Close the second view. Verify that mappings are not removed as the first
181      * view still holds a reference to them.
182      */
183     af1.closeMenuItem_actionPerformed(false);
184     assertEquals(2, ssm.getSequenceMappings().size());
185     assertTrue(ssm.getSequenceMappings().contains(acf1));
186     assertTrue(ssm.getSequenceMappings().contains(acf2));
187   }
188
189   /**
190    * Test that a mapping is deregistered if no alignment holds a reference to it
191    */
192   @Test(groups = { "Functional" })
193   public void testDeregisterMapping_withNoReference()
194   {
195     Desktop d = Desktop.instance;
196     assertNotNull(d);
197     StructureSelectionManager ssm = StructureSelectionManager
198             .getStructureSelectionManager(Desktop.instance);
199     ssm.resetAll();
200
201     AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
202             ">Seq1\nRSVQ\n", DataSourceType.PASTE);
203     AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
204             ">Seq2\nDGEL\n", DataSourceType.PASTE);
205     SequenceI cs1 = new Sequence("cseq1", "CCCGGGTTTAAA");
206     SequenceI cs2 = new Sequence("cseq2", "CTTGAGTCTAGA");
207     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
208     SequenceI s2 = af2.getViewport().getAlignment().getSequenceAt(0);
209     // need to be distinct
210     AlignedCodonFrame acf1 = new AlignedCodonFrame();
211     acf1.addMap(cs1, s1, new MapList(new int[] { 1, 4 },
212             new int[] { 1, 12 }, 1, 3));
213     AlignedCodonFrame acf2 = new AlignedCodonFrame();
214     acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 },
215             new int[] { 1, 12 }, 1, 3));
216     AlignedCodonFrame acf3 = new AlignedCodonFrame();
217     acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1,
218         12 }, 1, 1));
219
220     List<AlignedCodonFrame> mappings1 = new ArrayList<>();
221     mappings1.add(acf1);
222     af1.getViewport().getAlignment().setCodonFrames(mappings1);
223
224     List<AlignedCodonFrame> mappings2 = new ArrayList<>();
225     mappings2.add(acf2);
226     mappings2.add(acf3);
227     af2.getViewport().getAlignment().setCodonFrames(mappings2);
228
229     /*
230      * AlignFrame1 has mapping acf1, AlignFrame2 has acf2 and acf3
231      */
232
233     List<AlignedCodonFrame> ssmMappings = ssm.getSequenceMappings();
234     assertEquals(0, ssmMappings.size());
235     ssm.registerMapping(acf1);
236     assertEquals(1, ssmMappings.size());
237     ssm.registerMapping(acf2);
238     assertEquals(2, ssmMappings.size());
239     ssm.registerMapping(acf3);
240     assertEquals(3, ssmMappings.size());
241
242     /*
243      * Closing AlignFrame2 should remove its mappings from
244      * StructureSelectionManager, since AlignFrame1 has no reference to them
245      */
246     af2.closeMenuItem_actionPerformed(true);
247     assertEquals(1, ssmMappings.size());
248     assertTrue(ssmMappings.contains(acf1));
249   }
250
251   /**
252    * Test that a mapping is not deregistered if another alignment holds a
253    * reference to it
254    */
255   @Test(groups = { "Functional" })
256   public void testDeregisterMapping_withReference()
257   {
258     Desktop d = Desktop.instance;
259     assertNotNull(d);
260     StructureSelectionManager ssm = StructureSelectionManager
261             .getStructureSelectionManager(Desktop.instance);
262     ssm.resetAll();
263
264     AlignFrame af1 = new FileLoader().LoadFileWaitTillLoaded(
265             ">Seq1\nRSVQ\n", DataSourceType.PASTE);
266     AlignFrame af2 = new FileLoader().LoadFileWaitTillLoaded(
267             ">Seq2\nDGEL\n", DataSourceType.PASTE);
268     SequenceI cs1 = new Sequence("cseq1", "CCCGGGTTTAAA");
269     SequenceI cs2 = new Sequence("cseq2", "CTTGAGTCTAGA");
270     SequenceI s1 = af1.getViewport().getAlignment().getSequenceAt(0);
271     SequenceI s2 = af2.getViewport().getAlignment().getSequenceAt(0);
272     // need to be distinct
273     AlignedCodonFrame acf1 = new AlignedCodonFrame();
274     acf1.addMap(cs1, s1, new MapList(new int[] { 1, 4 },
275             new int[] { 1, 12 }, 1, 3));
276     AlignedCodonFrame acf2 = new AlignedCodonFrame();
277     acf2.addMap(cs2, s2, new MapList(new int[] { 1, 4 },
278             new int[] { 1, 12 }, 1, 3));
279     AlignedCodonFrame acf3 = new AlignedCodonFrame();
280     acf3.addMap(cs2, cs2, new MapList(new int[] { 1, 12 }, new int[] { 1,
281         12 }, 1, 1));
282
283     List<AlignedCodonFrame> mappings1 = new ArrayList<>();
284     mappings1.add(acf1);
285     mappings1.add(acf2);
286     af1.getViewport().getAlignment().setCodonFrames(mappings1);
287
288     List<AlignedCodonFrame> mappings2 = new ArrayList<>();
289     mappings2.add(acf2);
290     mappings2.add(acf3);
291     af2.getViewport().getAlignment().setCodonFrames(mappings2);
292
293     /*
294      * AlignFrame1 has mappings acf1 and acf2, AlignFrame2 has acf2 and acf3
295      */
296
297     List<AlignedCodonFrame> ssmMappings = ssm.getSequenceMappings();
298     assertEquals(0, ssmMappings.size());
299     ssm.registerMapping(acf1);
300     assertEquals(1, ssmMappings.size());
301     ssm.registerMapping(acf2);
302     assertEquals(2, ssmMappings.size());
303     ssm.registerMapping(acf3);
304     assertEquals(3, ssmMappings.size());
305
306     /*
307      * Closing AlignFrame2 should remove mapping acf3 from
308      * StructureSelectionManager, but not acf2, since AlignFrame1 still has a
309      * reference to it
310      */
311     af2.closeMenuItem_actionPerformed(true);
312     assertEquals(2, ssmMappings.size());
313     assertTrue(ssmMappings.contains(acf1));
314     assertTrue(ssmMappings.contains(acf2));
315     assertFalse(ssmMappings.contains(acf3));
316   }
317
318   /**
319    * Test for JAL-1306 - conservation thread should run even when only Quality
320    * (and not Conservation) is enabled in Preferences
321    */
322   @Test(groups = { "Functional" })
323   public void testUpdateConservation_qualityOnly()
324   {
325     Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
326             Boolean.TRUE.toString());
327     Cache.applicationProperties.setProperty("SHOW_QUALITY",
328             Boolean.TRUE.toString());
329     Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
330             Boolean.FALSE.toString());
331     Cache.applicationProperties.setProperty("SHOW_OCCUPANCY",
332             Boolean.FALSE.toString());
333     Cache.applicationProperties.setProperty("SHOW_IDENTITY",
334             Boolean.FALSE.toString());
335     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
336             "examples/uniref50.fa", DataSourceType.FILE);
337     AlignmentAnnotation[] anns = af.viewport.getAlignment()
338             .getAlignmentAnnotation();
339     assertNotNull("No annotations found", anns);
340     assertEquals("More than one annotation found", 1, anns.length);
341     assertTrue("Annotation is not Quality",
342             anns[0].description.startsWith("Alignment Quality"));
343     Annotation[] annotations = anns[0].annotations;
344     assertNotNull("Quality annotations are null", annotations);
345     assertNotNull("Quality in column 1 is null", annotations[0]);
346     assertTrue("No quality value in column 1", annotations[0].value > 10f);
347   }
348
349   @Test(groups = { "Functional" })
350   public void testSetGlobalColourScheme()
351   {
352     /*
353      * test for JAL-2283: don't inadvertently turn on colour by conservation
354      */
355     Cache.applicationProperties.setProperty("DEFAULT_COLOUR_PROT", "None");
356     Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
357             Boolean.TRUE.toString());
358     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
359             "examples/uniref50.fa", DataSourceType.FILE);
360     ColourSchemeI cs = new PIDColourScheme();
361     af.getViewport().setGlobalColourScheme(cs);
362     assertFalse(af.getViewport().getResidueShading()
363             .conservationApplied());
364   }
365
366   @Test(groups = { "Functional" })
367   public void testSetGetHasSearchResults()
368   {
369     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
370             "examples/uniref50.fa", DataSourceType.FILE);
371     SearchResultsI sr = new SearchResults();
372     SequenceI s1 = af.getViewport().getAlignment().getSequenceAt(0);
373
374     // create arbitrary range on first sequence
375     sr.addResult(s1, s1.getStart() + 10, s1.getStart() + 15);
376
377     // test set
378     af.getViewport().setSearchResults(sr);
379     // has -> true
380     assertTrue(af.getViewport().hasSearchResults());
381     // get == original
382     assertEquals(sr, af.getViewport().getSearchResults());
383
384     // set(null) results in has -> false
385
386     af.getViewport().setSearchResults(null);
387     assertFalse(af.getViewport().hasSearchResults());
388   }
389
390   /**
391    * Verify that setting the selection group has the side-effect of setting the
392    * context on the group, unless it already has one, but does not change
393    * whether the group is defined or not.
394    */
395   @Test(groups = { "Functional" })
396   public void testSetSelectionGroup()
397   {
398     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
399             "examples/uniref50.fa", DataSourceType.FILE);
400     AlignViewport av = af.getViewport();
401     SequenceGroup sg1 = new SequenceGroup();
402     SequenceGroup sg2 = new SequenceGroup();
403     SequenceGroup sg3 = new SequenceGroup();
404
405     av.setSelectionGroup(sg1);
406     assertSame(sg1.getContext(), av.getAlignment()); // context set
407     assertFalse(sg1.isDefined()); // group not defined
408
409     sg2.setContext(sg1, false);
410     av.setSelectionGroup(sg2);
411     assertFalse(sg2.isDefined()); // unchanged
412     assertSame(sg2.getContext(), sg1); // unchanged
413
414     // create a defined group
415     sg3.setContext(av.getAlignment(), true);
416     av.setSelectionGroup(sg3);
417     assertTrue(sg3.isDefined()); // unchanged
418   }
419   /**
420    * Verify that setting/clearing SHOW_OCCUPANCY preference adds or omits occupancy row from viewport
421    */
422   @Test(groups = { "Functional" })
423   public void testShowOrDontShowOccupancy()
424   {
425     // disable occupancy
426     jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.FALSE.toString());
427     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
428             "examples/uniref50.fa", DataSourceType.FILE);
429     AlignViewport av = af.getViewport();
430     Assert.assertNull(av.getAlignmentGapAnnotation(), "Preference did not disable occupancy row.");
431     int c = 0;
432     for (AlignmentAnnotation aa : av.getAlignment().findAnnotations(null,
433             null, "Occupancy"))
434     {
435       c++;
436     }
437     Assert.assertEquals(c, 0, "Expected zero occupancy rows.");
438     
439     // enable occupancy
440     jalview.bin.Cache.setProperty("SHOW_OCCUPANCY", Boolean.TRUE.toString());
441     af = new FileLoader().LoadFileWaitTillLoaded(
442             "examples/uniref50.fa", DataSourceType.FILE);
443     av = af.getViewport();
444     Assert.assertNotNull(av.getAlignmentGapAnnotation(), "Preference did not enable occupancy row.");
445     c = 0;
446     for (AlignmentAnnotation aa : av.getAlignment().findAnnotations(null,
447             null, av.getAlignmentGapAnnotation().label))
448     {
449       c++;
450     }
451     ;
452     Assert.assertEquals(c, 1, "Expected to find one occupancy row.");
453
454   }
455 }