9c306a3f6dedfa41a42ebf2f3d6d8f6f950891d1
[jalview.git] / test / jalview / datamodel / SequenceTest.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.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertNull;
25 import static org.testng.AssertJUnit.assertSame;
26 import static org.testng.AssertJUnit.assertTrue;
27
28 import java.util.Arrays;
29 import java.util.List;
30
31 import org.testng.annotations.BeforeMethod;
32 import org.testng.annotations.Test;
33
34 public class SequenceTest
35 {
36   SequenceI seq;
37
38   @BeforeMethod(alwaysRun = true)
39   public void setUp()
40   {
41     seq = new Sequence("FER1", "AKPNGVL");
42   }
43
44   @Test(groups = { "Functional" })
45   public void testInsertGapsAndGapmaps()
46   {
47     SequenceI aseq = seq.deriveSequence();
48     aseq.insertCharAt(2, 3, '-');
49     aseq.insertCharAt(6, 3, '-');
50     assertEquals("Gap insertions not correct", "AK---P---NGVL",
51             aseq.getSequenceAsString());
52     List<int[]> gapInt = aseq.getInsertions();
53     assertEquals("Gap interval 1 start wrong", 2, gapInt.get(0)[0]);
54     assertEquals("Gap interval 1 end wrong", 4, gapInt.get(0)[1]);
55     assertEquals("Gap interval 2 start wrong", 6, gapInt.get(1)[0]);
56     assertEquals("Gap interval 2 end wrong", 8, gapInt.get(1)[1]);
57   }
58
59   @Test(groups = { "Functional" })
60   public void testGetAnnotation()
61   {
62     // initial state returns null not an empty array
63     assertNull(seq.getAnnotation());
64     AlignmentAnnotation ann = addAnnotation("label1", "desc1", "calcId1",
65             1f);
66     AlignmentAnnotation[] anns = seq.getAnnotation();
67     assertEquals(1, anns.length);
68     assertSame(ann, anns[0]);
69
70     // removing all annotations reverts array to null
71     seq.removeAlignmentAnnotation(ann);
72     assertNull(seq.getAnnotation());
73   }
74
75   @Test(groups = { "Functional" })
76   public void testGetAnnotation_forLabel()
77   {
78     AlignmentAnnotation ann1 = addAnnotation("label1", "desc1", "calcId1",
79             1f);
80     AlignmentAnnotation ann2 = addAnnotation("label2", "desc2", "calcId2",
81             1f);
82     AlignmentAnnotation ann3 = addAnnotation("label1", "desc3", "calcId3",
83             1f);
84     AlignmentAnnotation[] anns = seq.getAnnotation("label1");
85     assertEquals(2, anns.length);
86     assertSame(ann1, anns[0]);
87     assertSame(ann3, anns[1]);
88   }
89
90   private AlignmentAnnotation addAnnotation(String label,
91           String description, String calcId, float value)
92   {
93     final AlignmentAnnotation annotation = new AlignmentAnnotation(label,
94             description, value);
95     annotation.setCalcId(calcId);
96     seq.addAlignmentAnnotation(annotation);
97     return annotation;
98   }
99
100   @Test(groups = { "Functional" })
101   public void testGetAlignmentAnnotations_forCalcIdAndLabel()
102   {
103     AlignmentAnnotation ann1 = addAnnotation("label1", "desc1", "calcId1",
104             1f);
105     AlignmentAnnotation ann2 = addAnnotation("label2", "desc2", "calcId2",
106             1f);
107     AlignmentAnnotation ann3 = addAnnotation("label2", "desc3", "calcId3",
108             1f);
109     AlignmentAnnotation ann4 = addAnnotation("label2", "desc3", "calcId2",
110             1f);
111     AlignmentAnnotation ann5 = addAnnotation("label5", "desc3", null, 1f);
112     AlignmentAnnotation ann6 = addAnnotation(null, "desc3", "calcId3", 1f);
113     List<AlignmentAnnotation> anns = seq.getAlignmentAnnotations("calcId2",
114             "label2");
115     assertEquals(2, anns.size());
116     assertSame(ann2, anns.get(0));
117     assertSame(ann4, anns.get(1));
118
119     assertTrue(seq.getAlignmentAnnotations("calcId2", "label3").isEmpty());
120     assertTrue(seq.getAlignmentAnnotations("calcId3", "label5").isEmpty());
121     assertTrue(seq.getAlignmentAnnotations("calcId2", null).isEmpty());
122     assertTrue(seq.getAlignmentAnnotations(null, "label3").isEmpty());
123     assertTrue(seq.getAlignmentAnnotations(null, null).isEmpty());
124   }
125
126   /**
127    * Tests for addAlignmentAnnotation. Note this method has the side-effect of
128    * setting the sequenceRef on the annotation. Adding the same annotation twice
129    * should be ignored.
130    */
131   @Test(groups = { "Functional" })
132   public void testAddAlignmentAnnotation()
133   {
134     assertNull(seq.getAnnotation());
135     final AlignmentAnnotation annotation = new AlignmentAnnotation("a",
136             "b", 2d);
137     assertNull(annotation.sequenceRef);
138     seq.addAlignmentAnnotation(annotation);
139     assertSame(seq, annotation.sequenceRef);
140     AlignmentAnnotation[] anns = seq.getAnnotation();
141     assertEquals(1, anns.length);
142     assertSame(annotation, anns[0]);
143
144     // re-adding does nothing
145     seq.addAlignmentAnnotation(annotation);
146     anns = seq.getAnnotation();
147     assertEquals(1, anns.length);
148     assertSame(annotation, anns[0]);
149
150     // an identical but different annotation can be added
151     final AlignmentAnnotation annotation2 = new AlignmentAnnotation("a",
152             "b", 2d);
153     seq.addAlignmentAnnotation(annotation2);
154     anns = seq.getAnnotation();
155     assertEquals(2, anns.length);
156     assertSame(annotation, anns[0]);
157     assertSame(annotation2, anns[1]);
158
159   }
160
161   @Test(groups = { "Functional" })
162   public void testGetStartGetEnd()
163   {
164     SequenceI seq = new Sequence("test", "ABCDEF");
165     assertEquals(1, seq.getStart());
166     assertEquals(6, seq.getEnd());
167
168     seq = new Sequence("test", "--AB-C-DEF--");
169     assertEquals(1, seq.getStart());
170     assertEquals(6, seq.getEnd());
171
172     seq = new Sequence("test", "----");
173     assertEquals(1, seq.getStart());
174     assertEquals(0, seq.getEnd()); // ??
175   }
176
177   /**
178    * Tests for the method that returns an alignment column position (base 1) for
179    * a given sequence position (base 1).
180    */
181   @Test(groups = { "Functional" })
182   public void testFindIndex()
183   {
184     SequenceI seq = new Sequence("test", "ABCDEF");
185     assertEquals(0, seq.findIndex(0));
186     assertEquals(1, seq.findIndex(1));
187     assertEquals(5, seq.findIndex(5));
188     assertEquals(6, seq.findIndex(6));
189     assertEquals(6, seq.findIndex(9));
190
191     seq = new Sequence("test", "-A--B-C-D-E-F--");
192     assertEquals(2, seq.findIndex(1));
193     assertEquals(5, seq.findIndex(2));
194     assertEquals(7, seq.findIndex(3));
195
196     // before start returns 0
197     assertEquals(0, seq.findIndex(0));
198     assertEquals(0, seq.findIndex(-1));
199
200     // beyond end returns last residue column
201     assertEquals(13, seq.findIndex(99));
202
203   }
204
205   /**
206    * Tests for the method that returns a dataset sequence position (base 1) for
207    * an aligned column position (base 0).
208    */
209   @Test(groups = { "Functional" })
210   public void testFindPosition()
211   {
212     SequenceI seq = new Sequence("test", "ABCDEF");
213     assertEquals(1, seq.findPosition(0));
214     assertEquals(6, seq.findPosition(5));
215     // assertEquals(-1, seq.findPosition(6)); // fails
216
217     seq = new Sequence("test", "AB-C-D--");
218     assertEquals(1, seq.findPosition(0));
219     assertEquals(2, seq.findPosition(1));
220     // gap position 'finds' residue to the right (not the left as per javadoc)
221     assertEquals(3, seq.findPosition(2));
222     assertEquals(3, seq.findPosition(3));
223     assertEquals(4, seq.findPosition(4));
224     assertEquals(4, seq.findPosition(5));
225     // returns 1 more than sequence length if off the end ?!?
226     assertEquals(5, seq.findPosition(6));
227     assertEquals(5, seq.findPosition(7));
228
229     seq = new Sequence("test", "--AB-C-DEF--");
230     assertEquals(1, seq.findPosition(0));
231     assertEquals(1, seq.findPosition(1));
232     assertEquals(1, seq.findPosition(2));
233     assertEquals(2, seq.findPosition(3));
234     assertEquals(3, seq.findPosition(4));
235     assertEquals(3, seq.findPosition(5));
236     assertEquals(4, seq.findPosition(6));
237     assertEquals(4, seq.findPosition(7));
238     assertEquals(5, seq.findPosition(8));
239     assertEquals(6, seq.findPosition(9));
240     assertEquals(7, seq.findPosition(10));
241     assertEquals(7, seq.findPosition(11));
242   }
243
244   @Test(groups = { "Functional" })
245   public void testDeleteChars()
246   {
247     SequenceI seq = new Sequence("test", "ABCDEF");
248     assertEquals(1, seq.getStart());
249     assertEquals(6, seq.getEnd());
250     seq.deleteChars(2, 3);
251     assertEquals("ABDEF", seq.getSequenceAsString());
252     assertEquals(1, seq.getStart());
253     assertEquals(5, seq.getEnd());
254
255     seq = new Sequence("test", "ABCDEF");
256     seq.deleteChars(0, 2);
257     assertEquals("CDEF", seq.getSequenceAsString());
258     assertEquals(3, seq.getStart());
259     assertEquals(6, seq.getEnd());
260   }
261
262   @Test(groups = { "Functional" })
263   public void testInsertCharAt()
264   {
265     // non-static methods:
266     SequenceI seq = new Sequence("test", "ABCDEF");
267     seq.insertCharAt(0, 'z');
268     assertEquals("zABCDEF", seq.getSequenceAsString());
269     seq.insertCharAt(2, 2, 'x');
270     assertEquals("zAxxBCDEF", seq.getSequenceAsString());
271
272     // for static method see StringUtilsTest
273   }
274
275   /**
276    * Test the method that returns an array of aligned sequence positions where
277    * the array index is the data sequence position (both base 0).
278    */
279   @Test(groups = { "Functional" })
280   public void testGapMap()
281   {
282     SequenceI seq = new Sequence("test", "-A--B-CD-E--F-");
283     seq.createDatasetSequence();
284     assertEquals("[1, 4, 6, 7, 9, 12]", Arrays.toString(seq.gapMap()));
285   }
286
287   /**
288    * Test the method that gets sequence features, either from the sequence or
289    * its dataset.
290    */
291   @Test(groups = { "Functional" })
292   public void testGetSequenceFeatures()
293   {
294     SequenceI seq = new Sequence("test", "GATCAT");
295     seq.createDatasetSequence();
296
297     assertNull(seq.getSequenceFeatures());
298
299     /*
300      * SequenceFeature on sequence
301      */
302     SequenceFeature sf = new SequenceFeature();
303     seq.addSequenceFeature(sf);
304     SequenceFeature[] sfs = seq.getSequenceFeatures();
305     assertEquals(1, sfs.length);
306     assertSame(sf, sfs[0]);
307
308     /*
309      * SequenceFeature on sequence and dataset sequence; returns that on
310      * sequence
311      */
312     SequenceFeature sf2 = new SequenceFeature();
313     seq.getDatasetSequence().addSequenceFeature(sf2);
314     sfs = seq.getSequenceFeatures();
315     assertEquals(1, sfs.length);
316     assertSame(sf, sfs[0]);
317
318     /*
319      * SequenceFeature on dataset sequence only
320      */
321     seq.setSequenceFeatures(null);
322     sfs = seq.getSequenceFeatures();
323     assertEquals(1, sfs.length);
324     assertSame(sf2, sfs[0]);
325
326     /*
327      * Corrupt case - no SequenceFeature, dataset's dataset is the original
328      * sequence. Test shows no infinite loop results.
329      */
330     seq.getDatasetSequence().setSequenceFeatures(null);
331     seq.getDatasetSequence().setDatasetSequence(seq); // loop!
332     assertNull(seq.getSequenceFeatures());
333   }
334
335   /**
336    * Test the method that returns an array, indexed by sequence position, whose
337    * entries are the residue positions at the sequence position (or to the right
338    * if a gap)
339    */
340   @Test(groups = { "Functional" })
341   public void testFindPositionMap()
342   {
343     /*
344      * Note: Javadoc for findPosition says it returns the residue position to
345      * the left of a gapped position; in fact it returns the position to the
346      * right. Also it returns a non-existent residue position for a gap beyond
347      * the sequence.
348      */
349     Sequence seq = new Sequence("TestSeq", "AB.C-D E.");
350     int[] map = seq.findPositionMap();
351     assertEquals(Arrays.toString(new int[] { 1, 2, 3, 3, 4, 4, 5, 5, 6 }),
352             Arrays.toString(map));
353   }
354
355   /**
356    * Test for getSubsequence
357    */
358   @Test(groups = { "Functional" })
359   public void testGetSubsequence()
360   {
361     SequenceI seq = new Sequence("TestSeq", "ABCDEFG");
362     seq.createDatasetSequence();
363
364     // positions are base 0, end position is exclusive
365     SequenceI subseq = seq.getSubSequence(2, 4);
366
367     assertEquals("CD", subseq.getSequenceAsString());
368     // start/end are base 1 positions
369     assertEquals(3, subseq.getStart());
370     assertEquals(4, subseq.getEnd());
371     // subsequence shares the full dataset sequence
372     assertSame(seq.getDatasetSequence(), subseq.getDatasetSequence());
373   }
374
375   /**
376    * Test for deriveSequence applied to a sequence with a dataset
377    */
378   @Test(groups = { "Functional" })
379   public void testDeriveSequence_existingDataset()
380   {
381     SequenceI seq = new Sequence("Seq1", "CD");
382     seq.setDatasetSequence(new Sequence("Seq1", "ABCDEF"));
383     seq.setStart(3);
384     seq.setEnd(4);
385     SequenceI derived = seq.deriveSequence();
386     assertEquals("CD", derived.getSequenceAsString());
387     assertSame(seq.getDatasetSequence(), derived.getDatasetSequence());
388   }
389
390   /**
391    * Test for deriveSequence applied to an ungapped sequence with no dataset
392    */
393   @Test(groups = { "Functional" })
394   public void testDeriveSequence_noDatasetUngapped()
395   {
396     SequenceI seq = new Sequence("Seq1", "ABCDEF");
397     assertEquals(1, seq.getStart());
398     assertEquals(6, seq.getEnd());
399     SequenceI derived = seq.deriveSequence();
400     assertEquals("ABCDEF", derived.getSequenceAsString());
401     assertEquals("ABCDEF", derived.getDatasetSequence()
402             .getSequenceAsString());
403   }
404
405   /**
406    * Test for deriveSequence applied to a gapped sequence with no dataset
407    */
408   @Test(groups = { "Functional" })
409   public void testDeriveSequence_noDatasetGapped()
410   {
411     SequenceI seq = new Sequence("Seq1", "AB-C.D EF");
412     assertEquals(1, seq.getStart());
413     assertEquals(6, seq.getEnd());
414     assertNull(seq.getDatasetSequence());
415     SequenceI derived = seq.deriveSequence();
416     assertEquals("AB-C.D EF", derived.getSequenceAsString());
417     assertEquals("ABCDEF", derived.getDatasetSequence()
418             .getSequenceAsString());
419   }
420 }