JAL-3070 JAL-3066 AlignmentI.findOrAddAnnotation(AlignmentAnnotation ala) - calls...
[jalview.git] / test / jalview / datamodel / AlignmentAnnotationTests.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.datamodel;
22
23 import static org.testng.Assert.assertNull;
24 import static org.testng.AssertJUnit.assertEquals;
25
26 import jalview.analysis.AlignSeq;
27 import jalview.gui.JvOptionPane;
28 import jalview.io.AppletFormatAdapter;
29 import jalview.io.FileFormat;
30
31 import org.testng.Assert;
32 import org.testng.annotations.BeforeClass;
33 import org.testng.annotations.Test;
34
35 public class AlignmentAnnotationTests
36 {
37
38   @BeforeClass(alwaysRun = true)
39   public void setUpJvOptionPane()
40   {
41     JvOptionPane.setInteractiveMode(false);
42     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
43   }
44
45   @Test(groups = { "Functional" })
46   public void testCopyConstructor()
47   {
48     SequenceI sq = new Sequence("Foo", "ARAARARARAWEAWEAWRAWEAWE");
49     createAnnotation(sq);
50     AlignmentAnnotation alc, alo = sq.getAnnotation()[0];
51     alc = new AlignmentAnnotation(alo);
52
53     // TODO: this only tests string equals (which is unreliable), should use
54     // refactored tests from StockholmFileTest
55     Assert.assertEquals(alc.toString(), alo.toString());
56
57     for (String key : alo.getProperties())
58     {
59       assertEquals("Property mismatch", alo.getProperty(key),
60               alc.getProperty(key));
61     }
62   }
63
64   /**
65    * create some dummy annotation derived from the sequence
66    * 
67    * @param sq
68    */
69   public static void createAnnotation(SequenceI sq)
70   {
71     Annotation[] al = new Annotation[sq.getLength()];
72     for (int i = 0; i < al.length; i++)
73     {
74       al[i] = new Annotation(new Annotation("" + sq.getCharAt(i), "",
75               (char) 0, sq.findPosition(i)));
76     }
77     AlignmentAnnotation alan = new AlignmentAnnotation("For "
78             + sq.getName(), "Fake alignment annot", al);
79     // create a sequence mapping for the annotation vector in its current state
80     alan.createSequenceMapping(sq, sq.getStart(), false);
81     alan.setProperty("CreatedBy", "createAnnotation");
82     sq.addAlignmentAnnotation(alan);
83   }
84
85   /**
86    * use this to test annotation derived from method above as it is transferred
87    * across different sequences derived from same dataset coordinate frame
88    * 
89    * @param ala
90    */
91   public static void testAnnotTransfer(AlignmentAnnotation ala)
92   {
93     assertEquals(
94             "Failed - need annotation created by createAnnotation method",
95             ala.description, "Fake alignment annot");
96     ala.adjustForAlignment();
97     for (int p = 0; p < ala.annotations.length; p++)
98     {
99       if (ala.annotations[p] != null)
100       {
101         assertEquals("Mismatch at position " + p
102                 + " between annotation position value and sequence"
103                 + ala.annotations[p], (int) ala.annotations[p].value,
104                 ala.sequenceRef.findPosition(p));
105       }
106     }
107   }
108
109   /**
110    * Tests the liftOver method and also exercises the functions for remapping
111    * annotation across different reference sequences. Here, the test is between
112    * different dataset frames (annotation transferred by mapping between
113    * sequences)
114    */
115   @Test(groups = { "Functional" })
116   public void testLiftOver()
117   {
118     SequenceI sqFrom = new Sequence("fromLong", "QQQCDEWGH");
119     sqFrom.setStart(10);
120     sqFrom.setEnd(sqFrom.findPosition(sqFrom.getLength() - 1));
121     SequenceI sqTo = new Sequence("toShort", "RCDEW");
122     sqTo.setStart(20);
123     sqTo.setEnd(sqTo.findPosition(sqTo.getLength() - 1));
124     createAnnotation(sqTo);
125     AlignmentAnnotation origTo = sqTo.getAnnotation()[0];
126     createAnnotation(sqFrom);
127     AlignmentAnnotation origFrom = sqFrom.getAnnotation()[0];
128     AlignSeq align = AlignSeq.doGlobalNWAlignment(sqFrom, sqTo,
129             AlignSeq.PEP);
130     SequenceI alSeq1 = new Sequence(sqFrom.getName(), align.getAStr1());
131     alSeq1.setStart(sqFrom.getStart() + align.getSeq1Start() - 1);
132     alSeq1.setEnd(sqFrom.getStart() + align.getSeq1End() - 1);
133     alSeq1.setDatasetSequence(sqFrom);
134     SequenceI alSeq2 = new Sequence(sqTo.getName(), align.getAStr2());
135     alSeq2.setStart(sqTo.getStart() + align.getSeq2Start() - 1);
136     alSeq2.setEnd(sqTo.getStart() + align.getSeq2End() - 1);
137     alSeq2.setDatasetSequence(sqTo);
138     System.out.println(new AppletFormatAdapter()
139 .formatSequences(
140             FileFormat.Stockholm, new Alignment(new SequenceI[] { sqFrom,
141                 alSeq1, sqTo, alSeq2 }), true));
142
143     Mapping mp = align.getMappingFromS1(false);
144
145     AlignmentAnnotation almap1 = new AlignmentAnnotation(
146             sqTo.getAnnotation()[0]);
147     almap1.liftOver(sqFrom, mp);
148     assertEquals(almap1.sequenceRef, sqFrom);
149     alSeq1.addAlignmentAnnotation(almap1);
150     almap1.setSequenceRef(alSeq1);
151     almap1.adjustForAlignment();
152     AlignmentAnnotation almap2 = new AlignmentAnnotation(
153             sqFrom.getAnnotation()[0]);
154     almap2.liftOver(sqTo, mp);
155     assertEquals(almap2.sequenceRef, sqTo);
156
157     alSeq2.addAlignmentAnnotation(almap2);
158     almap2.setSequenceRef(alSeq2);
159     almap2.adjustForAlignment();
160
161     AlignmentI all = new Alignment(new SequenceI[] { alSeq1, alSeq2 });
162     all.addAnnotation(almap1);
163     all.addAnnotation(almap2);
164     System.out.println(new AppletFormatAdapter().formatSequences(
165             FileFormat.Stockholm,
166             all, true));
167
168     for (int p = 0; p < alSeq1.getLength(); p++)
169     {
170       Annotation orig1, trans1, orig2, trans2;
171       trans2 = almap2.annotations[p];
172       orig2 = origFrom.annotations[alSeq1.findPosition(p)
173               - sqFrom.getStart()];
174       orig1 = origTo.annotations[alSeq2.findPosition(p) - sqTo.getStart()];
175       trans1 = almap1.annotations[p];
176       if (trans1 == trans2)
177       {
178         System.out.println("Pos " + p + " mismatch");
179         continue;
180       }
181       assertEquals(
182               "Mismatch on Original From and transferred annotation on 2",
183               (orig2 != null) ? orig2.toString() : null,
184               (trans2 != null) ? trans2.toString() : null);
185       assertEquals(
186               "Mismatch on Original To and transferred annotation on 1",
187               (orig1 != null) ? orig1.toString() : null,
188               (trans1 != null) ? trans1.toString() : null);
189       String alm1 = ""
190               + (almap1.annotations.length > p ? almap1.annotations[p].displayCharacter
191                       : "Out of range");
192       String alm2 = ""
193               + (almap2.annotations.length > p ? almap2.annotations[p].displayCharacter
194                       : "Out of range");
195       assertEquals("Position " + p + " " + alm1 + " " + alm2, alm1, alm2);
196     }
197   }
198
199   @Test(groups = { "Functional" })
200   public void testAdjustForAlignment()
201   {
202     SequenceI seq = new Sequence("TestSeq", "ABCDEFG");
203     seq.createDatasetSequence();
204
205     /*
206      * Annotate positions 3/4/5 (CDE) with values 1/2/3
207      */
208     Annotation[] anns = new Annotation[] { null, null, new Annotation(1),
209         new Annotation(2), new Annotation(3) };
210     AlignmentAnnotation ann = new AlignmentAnnotation("SS",
211             "secondary structure", anns);
212     seq.addAlignmentAnnotation(ann);
213
214     /*
215      * Check annotation map before modifying aligned sequence
216      */
217     assertNull(ann.getAnnotationForPosition(1));
218     assertNull(ann.getAnnotationForPosition(2));
219     assertNull(ann.getAnnotationForPosition(6));
220     assertNull(ann.getAnnotationForPosition(7));
221     assertEquals(1, ann.getAnnotationForPosition(3).value, 0.001d);
222     assertEquals(2, ann.getAnnotationForPosition(4).value, 0.001d);
223     assertEquals(3, ann.getAnnotationForPosition(5).value, 0.001d);
224
225     /*
226      * Trim the displayed sequence to BCD and adjust annotations
227      */
228     seq.setSequence("BCD");
229     seq.setStart(2);
230     seq.setEnd(4);
231     ann.adjustForAlignment();
232
233     /*
234      * Should now have annotations for aligned positions 2, 3Q (CD) only
235      */
236     assertEquals(3, ann.annotations.length);
237     assertNull(ann.annotations[0]);
238     assertEquals(1, ann.annotations[1].value, 0.001);
239     assertEquals(2, ann.annotations[2].value, 0.001);
240   }
241
242   /**
243    * Test the method that defaults rna symbol to the one matching the preceding
244    * unmatched opening bracket (if any)
245    */
246   @Test(groups = { "Functional" })
247   public void testGetDefaultRnaHelixSymbol()
248   {
249     AlignmentAnnotation ann = new AlignmentAnnotation("SS",
250             "secondary structure", null);
251     assertEquals("(", ann.getDefaultRnaHelixSymbol(4));
252
253     Annotation[] anns = new Annotation[20];
254     ann.annotations = anns;
255     assertEquals("(", ann.getDefaultRnaHelixSymbol(4));
256
257     anns[1] = new Annotation("(", "S", '(', 0f);
258     assertEquals("(", ann.getDefaultRnaHelixSymbol(0));
259     assertEquals("(", ann.getDefaultRnaHelixSymbol(1));
260     assertEquals(")", ann.getDefaultRnaHelixSymbol(2));
261     assertEquals(")", ann.getDefaultRnaHelixSymbol(3));
262
263     /*
264      * .(.[.{.<.}.>.).].
265      */
266     anns[1] = new Annotation("(", "S", '(', 0f);
267     anns[3] = new Annotation("[", "S", '[', 0f);
268     anns[5] = new Annotation("{", "S", '{', 0f);
269     anns[7] = new Annotation("<", "S", '<', 0f);
270     anns[9] = new Annotation("}", "S", '}', 0f);
271     anns[11] = new Annotation(">", "S", '>', 0f);
272     anns[13] = new Annotation(")", "S", ')', 0f);
273     anns[15] = new Annotation("]", "S", ']', 0f);
274
275     String expected = "(())]]}}>>>>]]]](";
276     for (int i = 0; i < expected.length(); i++)
277     {
278       assertEquals("column " + i, String.valueOf(expected.charAt(i)),
279               ann.getDefaultRnaHelixSymbol(i));
280     }
281
282     /*
283      * .(.[.(.).{.}.<.].D.
284      */
285     anns[1] = new Annotation("(", "S", '(', 0f);
286     anns[3] = new Annotation("[", "S", '[', 0f);
287     anns[5] = new Annotation("(", "S", '(', 0f);
288     anns[7] = new Annotation(")", "S", ')', 0f);
289     anns[9] = new Annotation("{", "S", '{', 0f);
290     anns[11] = new Annotation("}", "S", '}', 0f);
291     anns[13] = new Annotation("<", "S", '>', 0f);
292     anns[15] = new Annotation("]", "S", ']', 0f);
293     anns[17] = new Annotation("D", "S", 'D', 0f);
294
295     expected = "(())]]))]]}}]]>>>>dd";
296     for (int i = 0; i < expected.length(); i++)
297     {
298       assertEquals("column " + i, String.valueOf(expected.charAt(i)),
299               ann.getDefaultRnaHelixSymbol(i));
300     }
301   }
302
303   public static Annotation newAnnotation(String ann)
304   {
305     float val = 0f;
306     try
307     {
308       val = Float.parseFloat(ann);
309     } catch (NumberFormatException q)
310     {
311     }
312     ;
313     return new Annotation(ann, ann, '\0', val);
314   }
315
316   @Test(groups = { "Functional" })
317   public void testIsQuantitative()
318   {
319     AlignmentAnnotation ann = null;
320
321     ann = new AlignmentAnnotation("an", "some an", null);
322     Assert.assertFalse(ann.isQuantitative(),
323             "Empty annotation set should not be quantitative.");
324
325     ann = new AlignmentAnnotation("an", "some an", new Annotation[] {
326         newAnnotation("4"), newAnnotation("1"), newAnnotation("1"),
327         newAnnotation("0.1"), newAnnotation("0.3") });
328     Assert.assertTrue(ann.isQuantitative(),
329             "All numbers annotation set should be quantitative.");
330
331     ann = new AlignmentAnnotation("an", "some an", new Annotation[] {
332         newAnnotation("E"), newAnnotation("E"), newAnnotation("E"),
333         newAnnotation("E"), newAnnotation("E") });
334     Assert.assertFalse(ann.isQuantitative(),
335             "All 'E' annotation set should not be quantitative.");
336
337     ann = new AlignmentAnnotation("an", "some an", new Annotation[] {
338         newAnnotation("E"), newAnnotation("1"), newAnnotation("2"),
339         newAnnotation("3"), newAnnotation("E") });
340     Assert.assertTrue(ann.isQuantitative(),
341             "Mixed 'E' annotation set should be quantitative.");
342   }
343
344   @Test(groups = "Functional")
345   public void testMakeVisibleAnnotation()
346   {
347     HiddenColumns h = new HiddenColumns();
348     Annotation[] anns = new Annotation[] { null, null, new Annotation(1),
349         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
350         new Annotation(5), new Annotation(6), new Annotation(7),
351         new Annotation(8) };
352     AlignmentAnnotation ann = new AlignmentAnnotation("an", "some an",
353             anns);
354
355     // null annotations
356     AlignmentAnnotation emptyann = new AlignmentAnnotation("an", "some ann",
357             null);
358     emptyann.makeVisibleAnnotation(h);
359     assertNull(emptyann.annotations);
360
361     emptyann.makeVisibleAnnotation(3, 4, h);
362     assertNull(emptyann.annotations);
363
364     // without bounds, does everything
365     ann.makeVisibleAnnotation(h);
366     assertEquals(12, ann.annotations.length);
367     assertNull(ann.annotations[0]);
368     assertNull(ann.annotations[1]);
369     assertEquals(1.0f, ann.annotations[2].value);
370     assertEquals(2.0f, ann.annotations[3].value);
371     assertEquals(3.0f, ann.annotations[4].value);
372     assertNull(ann.annotations[5]);
373     assertNull(ann.annotations[6]);
374     assertEquals(4.0f, ann.annotations[7].value);
375     assertEquals(5.0f, ann.annotations[8].value);
376     assertEquals(6.0f, ann.annotations[9].value);
377     assertEquals(7.0f, ann.annotations[10].value);
378     assertEquals(8.0f, ann.annotations[11].value);
379
380     // without hidden cols, just truncates
381     ann.makeVisibleAnnotation(3, 5, h);
382     assertEquals(3, ann.annotations.length);
383     assertEquals(2.0f, ann.annotations[0].value);
384     assertEquals(3.0f, ann.annotations[1].value);
385     assertNull(ann.annotations[2]);
386
387     anns = new Annotation[] { null, null, new Annotation(1),
388         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
389         new Annotation(5), new Annotation(6), new Annotation(7),
390         new Annotation(8) };
391     ann = new AlignmentAnnotation("an", "some an", anns);
392     h.hideColumns(4, 7);
393     ann.makeVisibleAnnotation(1, 9, h);
394     assertEquals(5, ann.annotations.length);
395     assertNull(ann.annotations[0]);
396     assertEquals(1.0f, ann.annotations[1].value);
397     assertEquals(2.0f, ann.annotations[2].value);
398     assertEquals(5.0f, ann.annotations[3].value);
399     assertEquals(6.0f, ann.annotations[4].value);
400
401     anns = new Annotation[] { null, null, new Annotation(1),
402         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
403         new Annotation(5), new Annotation(6), new Annotation(7),
404         new Annotation(8) };
405     ann = new AlignmentAnnotation("an", "some an", anns);
406     h.hideColumns(1, 2);
407     ann.makeVisibleAnnotation(1, 9, h);
408     assertEquals(3, ann.annotations.length);
409     assertEquals(2.0f, ann.annotations[0].value);
410     assertEquals(5.0f, ann.annotations[1].value);
411     assertEquals(6.0f, ann.annotations[2].value);
412
413     anns = new Annotation[] { null, null, new Annotation(1),
414         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
415         new Annotation(5), new Annotation(6), new Annotation(7),
416         new Annotation(8), new Annotation(9), new Annotation(10),
417         new Annotation(11), new Annotation(12), new Annotation(13),
418         new Annotation(14), new Annotation(15) };
419     ann = new AlignmentAnnotation("an", "some an", anns);
420     h = new HiddenColumns();
421     h.hideColumns(5, 18);
422     h.hideColumns(20, 21);
423     ann.makeVisibleAnnotation(1, 21, h);
424     assertEquals(5, ann.annotations.length);
425     assertEquals(1.0f, ann.annotations[1].value);
426     assertEquals(2.0f, ann.annotations[2].value);
427     assertEquals(3.0f, ann.annotations[3].value);
428     assertNull(ann.annotations[0]);
429     assertNull(ann.annotations[4]);
430   }
431 }