Merge branch 'develop' into update_212_Dec_merge_with_21125_chamges
[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(
78             "For " + 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(
102                 "Mismatch at position " + p
103                         + " between annotation position value and sequence"
104                         + ala.annotations[p],
105                 (int) ala.annotations[p].value,
106                 ala.sequenceRef.findPosition(p));
107       }
108     }
109   }
110
111   /**
112    * Tests the liftOver method and also exercises the functions for remapping
113    * annotation across different reference sequences. Here, the test is between
114    * different dataset frames (annotation transferred by mapping between
115    * sequences)
116    */
117   @Test(groups = { "Functional" })
118   public void testLiftOver()
119   {
120     SequenceI sqFrom = new Sequence("fromLong", "QQQCDEWGH");
121     sqFrom.setStart(10);
122     sqFrom.setEnd(sqFrom.findPosition(sqFrom.getLength() - 1));
123     SequenceI sqTo = new Sequence("toShort", "RCDEW");
124     sqTo.setStart(20);
125     sqTo.setEnd(sqTo.findPosition(sqTo.getLength() - 1));
126     createAnnotation(sqTo);
127     AlignmentAnnotation origTo = sqTo.getAnnotation()[0];
128     createAnnotation(sqFrom);
129     AlignmentAnnotation origFrom = sqFrom.getAnnotation()[0];
130     AlignSeq align = AlignSeq.doGlobalNWAlignment(sqFrom, sqTo,
131             AlignSeq.PEP);
132     SequenceI alSeq1 = new Sequence(sqFrom.getName(), align.getAStr1());
133     alSeq1.setStart(sqFrom.getStart() + align.getSeq1Start() - 1);
134     alSeq1.setEnd(sqFrom.getStart() + align.getSeq1End() - 1);
135     alSeq1.setDatasetSequence(sqFrom);
136     SequenceI alSeq2 = new Sequence(sqTo.getName(), align.getAStr2());
137     alSeq2.setStart(sqTo.getStart() + align.getSeq2Start() - 1);
138     alSeq2.setEnd(sqTo.getStart() + align.getSeq2End() - 1);
139     alSeq2.setDatasetSequence(sqTo);
140     System.out.println(new AppletFormatAdapter().formatSequences(
141             FileFormat.Stockholm, new Alignment(new SequenceI[]
142             { sqFrom, alSeq1, sqTo, alSeq2 }), true));
143
144     Mapping mp = align.getMappingFromS1(false);
145
146     AlignmentAnnotation almap1 = new AlignmentAnnotation(
147             sqTo.getAnnotation()[0]);
148     almap1.liftOver(sqFrom, mp);
149     assertEquals(almap1.sequenceRef, sqFrom);
150     alSeq1.addAlignmentAnnotation(almap1);
151     almap1.setSequenceRef(alSeq1);
152     almap1.adjustForAlignment();
153     AlignmentAnnotation almap2 = new AlignmentAnnotation(
154             sqFrom.getAnnotation()[0]);
155     almap2.liftOver(sqTo, mp);
156     assertEquals(almap2.sequenceRef, sqTo);
157
158     alSeq2.addAlignmentAnnotation(almap2);
159     almap2.setSequenceRef(alSeq2);
160     almap2.adjustForAlignment();
161
162     AlignmentI all = new Alignment(new SequenceI[] { alSeq1, alSeq2 });
163     all.addAnnotation(almap1);
164     all.addAnnotation(almap2);
165     System.out.println(new AppletFormatAdapter()
166             .formatSequences(FileFormat.Stockholm, 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 = "" + (almap1.annotations.length > p
190               ? almap1.annotations[p].displayCharacter
191               : "Out of range");
192       String alm2 = "" + (almap2.annotations.length > p
193               ? 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",
326             new Annotation[]
327             { newAnnotation("4"), newAnnotation("1"), newAnnotation("1"),
328                 newAnnotation("0.1"), newAnnotation("0.3") });
329     Assert.assertTrue(ann.isQuantitative(),
330             "All numbers annotation set should be quantitative.");
331
332     ann = new AlignmentAnnotation("an", "some an",
333             new Annotation[]
334             { newAnnotation("E"), newAnnotation("E"), newAnnotation("E"),
335                 newAnnotation("E"), newAnnotation("E") });
336     Assert.assertFalse(ann.isQuantitative(),
337             "All 'E' annotation set should not be quantitative.");
338
339     ann = new AlignmentAnnotation("an", "some an",
340             new Annotation[]
341             { newAnnotation("E"), newAnnotation("1"), newAnnotation("2"),
342                 newAnnotation("3"), newAnnotation("E") });
343     Assert.assertTrue(ann.isQuantitative(),
344             "Mixed 'E' annotation set should be quantitative.");
345   }
346
347   @Test(groups = "Functional")
348   public void testMakeVisibleAnnotation()
349   {
350     HiddenColumns h = new HiddenColumns();
351     Annotation[] anns = new Annotation[] { null, null, new Annotation(1),
352         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
353         new Annotation(5), new Annotation(6), new Annotation(7),
354         new Annotation(8) };
355     AlignmentAnnotation ann = new AlignmentAnnotation("an", "some an",
356             anns);
357
358     // null annotations
359     AlignmentAnnotation emptyann = new AlignmentAnnotation("an", "some ann",
360             null);
361     emptyann.makeVisibleAnnotation(h);
362     assertNull(emptyann.annotations);
363
364     emptyann.makeVisibleAnnotation(3, 4, h);
365     assertNull(emptyann.annotations);
366
367     // without bounds, does everything
368     ann.makeVisibleAnnotation(h);
369     assertEquals(12, ann.annotations.length);
370     assertNull(ann.annotations[0]);
371     assertNull(ann.annotations[1]);
372     assertEquals(1.0f, ann.annotations[2].value);
373     assertEquals(2.0f, ann.annotations[3].value);
374     assertEquals(3.0f, ann.annotations[4].value);
375     assertNull(ann.annotations[5]);
376     assertNull(ann.annotations[6]);
377     assertEquals(4.0f, ann.annotations[7].value);
378     assertEquals(5.0f, ann.annotations[8].value);
379     assertEquals(6.0f, ann.annotations[9].value);
380     assertEquals(7.0f, ann.annotations[10].value);
381     assertEquals(8.0f, ann.annotations[11].value);
382
383     // without hidden cols, just truncates
384     ann.makeVisibleAnnotation(3, 5, h);
385     assertEquals(3, ann.annotations.length);
386     assertEquals(2.0f, ann.annotations[0].value);
387     assertEquals(3.0f, ann.annotations[1].value);
388     assertNull(ann.annotations[2]);
389
390     anns = new Annotation[] { null, null, new Annotation(1),
391         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
392         new Annotation(5), new Annotation(6), new Annotation(7),
393         new Annotation(8) };
394     ann = new AlignmentAnnotation("an", "some an", anns);
395     h.hideColumns(4, 7);
396     ann.makeVisibleAnnotation(1, 9, h);
397     assertEquals(5, ann.annotations.length);
398     assertNull(ann.annotations[0]);
399     assertEquals(1.0f, ann.annotations[1].value);
400     assertEquals(2.0f, ann.annotations[2].value);
401     assertEquals(5.0f, ann.annotations[3].value);
402     assertEquals(6.0f, ann.annotations[4].value);
403
404     anns = new Annotation[] { null, null, new Annotation(1),
405         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
406         new Annotation(5), new Annotation(6), new Annotation(7),
407         new Annotation(8) };
408     ann = new AlignmentAnnotation("an", "some an", anns);
409     h.hideColumns(1, 2);
410     ann.makeVisibleAnnotation(1, 9, h);
411     assertEquals(3, ann.annotations.length);
412     assertEquals(2.0f, ann.annotations[0].value);
413     assertEquals(5.0f, ann.annotations[1].value);
414     assertEquals(6.0f, ann.annotations[2].value);
415
416     anns = new Annotation[] { null, null, new Annotation(1),
417         new Annotation(2), new Annotation(3), null, null, new Annotation(4),
418         new Annotation(5), new Annotation(6), new Annotation(7),
419         new Annotation(8), new Annotation(9), new Annotation(10),
420         new Annotation(11), new Annotation(12), new Annotation(13),
421         new Annotation(14), new Annotation(15) };
422     ann = new AlignmentAnnotation("an", "some an", anns);
423     h = new HiddenColumns();
424     h.hideColumns(5, 18);
425     h.hideColumns(20, 21);
426     ann.makeVisibleAnnotation(1, 21, h);
427     assertEquals(5, ann.annotations.length);
428     assertEquals(1.0f, ann.annotations[1].value);
429     assertEquals(2.0f, ann.annotations[2].value);
430     assertEquals(3.0f, ann.annotations[3].value);
431     assertNull(ann.annotations[0]);
432     assertNull(ann.annotations[4]);
433   }
434 }