JAL-2110 refactor + tests to allow a dbref version to update "0" or
[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.assertFalse;
25 import static org.testng.AssertJUnit.assertNotNull;
26 import static org.testng.AssertJUnit.assertNull;
27 import static org.testng.AssertJUnit.assertSame;
28 import static org.testng.AssertJUnit.assertTrue;
29 import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
30
31 import jalview.datamodel.PDBEntry.Type;
32 import jalview.util.MapList;
33
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.List;
37 import java.util.Vector;
38
39 import org.testng.Assert;
40 import org.testng.annotations.BeforeMethod;
41 import org.testng.annotations.Test;
42
43 public class SequenceTest
44 {
45   Sequence seq;
46
47   @BeforeMethod(alwaysRun = true)
48   public void setUp()
49   {
50     seq = new Sequence("FER1", "AKPNGVL");
51   }
52
53   @Test(groups = { "Functional" })
54   public void testInsertGapsAndGapmaps()
55   {
56     SequenceI aseq = seq.deriveSequence();
57     aseq.insertCharAt(2, 3, '-');
58     aseq.insertCharAt(6, 3, '-');
59     assertEquals("Gap insertions not correct", "AK---P---NGVL",
60             aseq.getSequenceAsString());
61     List<int[]> gapInt = aseq.getInsertions();
62     assertEquals("Gap interval 1 start wrong", 2, gapInt.get(0)[0]);
63     assertEquals("Gap interval 1 end wrong", 4, gapInt.get(0)[1]);
64     assertEquals("Gap interval 2 start wrong", 6, gapInt.get(1)[0]);
65     assertEquals("Gap interval 2 end wrong", 8, gapInt.get(1)[1]);
66   }
67
68   @Test(groups = { "Functional" })
69   public void testGetAnnotation()
70   {
71     // initial state returns null not an empty array
72     assertNull(seq.getAnnotation());
73     AlignmentAnnotation ann = addAnnotation("label1", "desc1", "calcId1",
74             1f);
75     AlignmentAnnotation[] anns = seq.getAnnotation();
76     assertEquals(1, anns.length);
77     assertSame(ann, anns[0]);
78
79     // removing all annotations reverts array to null
80     seq.removeAlignmentAnnotation(ann);
81     assertNull(seq.getAnnotation());
82   }
83
84   @Test(groups = { "Functional" })
85   public void testGetAnnotation_forLabel()
86   {
87     AlignmentAnnotation ann1 = addAnnotation("label1", "desc1", "calcId1",
88             1f);
89     AlignmentAnnotation ann2 = addAnnotation("label2", "desc2", "calcId2",
90             1f);
91     AlignmentAnnotation ann3 = addAnnotation("label1", "desc3", "calcId3",
92             1f);
93     AlignmentAnnotation[] anns = seq.getAnnotation("label1");
94     assertEquals(2, anns.length);
95     assertSame(ann1, anns[0]);
96     assertSame(ann3, anns[1]);
97   }
98
99   private AlignmentAnnotation addAnnotation(String label,
100           String description, String calcId, float value)
101   {
102     final AlignmentAnnotation annotation = new AlignmentAnnotation(label,
103             description, value);
104     annotation.setCalcId(calcId);
105     seq.addAlignmentAnnotation(annotation);
106     return annotation;
107   }
108
109   @Test(groups = { "Functional" })
110   public void testGetAlignmentAnnotations_forCalcIdAndLabel()
111   {
112     AlignmentAnnotation ann1 = addAnnotation("label1", "desc1", "calcId1",
113             1f);
114     AlignmentAnnotation ann2 = addAnnotation("label2", "desc2", "calcId2",
115             1f);
116     AlignmentAnnotation ann3 = addAnnotation("label2", "desc3", "calcId3",
117             1f);
118     AlignmentAnnotation ann4 = addAnnotation("label2", "desc3", "calcId2",
119             1f);
120     AlignmentAnnotation ann5 = addAnnotation("label5", "desc3", null, 1f);
121     AlignmentAnnotation ann6 = addAnnotation(null, "desc3", "calcId3", 1f);
122     List<AlignmentAnnotation> anns = seq.getAlignmentAnnotations("calcId2",
123             "label2");
124     assertEquals(2, anns.size());
125     assertSame(ann2, anns.get(0));
126     assertSame(ann4, anns.get(1));
127
128     assertTrue(seq.getAlignmentAnnotations("calcId2", "label3").isEmpty());
129     assertTrue(seq.getAlignmentAnnotations("calcId3", "label5").isEmpty());
130     assertTrue(seq.getAlignmentAnnotations("calcId2", null).isEmpty());
131     assertTrue(seq.getAlignmentAnnotations(null, "label3").isEmpty());
132     assertTrue(seq.getAlignmentAnnotations(null, null).isEmpty());
133   }
134
135   /**
136    * Tests for addAlignmentAnnotation. Note this method has the side-effect of
137    * setting the sequenceRef on the annotation. Adding the same annotation twice
138    * should be ignored.
139    */
140   @Test(groups = { "Functional" })
141   public void testAddAlignmentAnnotation()
142   {
143     assertNull(seq.getAnnotation());
144     final AlignmentAnnotation annotation = new AlignmentAnnotation("a",
145             "b", 2d);
146     assertNull(annotation.sequenceRef);
147     seq.addAlignmentAnnotation(annotation);
148     assertSame(seq, annotation.sequenceRef);
149     AlignmentAnnotation[] anns = seq.getAnnotation();
150     assertEquals(1, anns.length);
151     assertSame(annotation, anns[0]);
152
153     // re-adding does nothing
154     seq.addAlignmentAnnotation(annotation);
155     anns = seq.getAnnotation();
156     assertEquals(1, anns.length);
157     assertSame(annotation, anns[0]);
158
159     // an identical but different annotation can be added
160     final AlignmentAnnotation annotation2 = new AlignmentAnnotation("a",
161             "b", 2d);
162     seq.addAlignmentAnnotation(annotation2);
163     anns = seq.getAnnotation();
164     assertEquals(2, anns.length);
165     assertSame(annotation, anns[0]);
166     assertSame(annotation2, anns[1]);
167   }
168
169   @Test(groups = { "Functional" })
170   public void testGetStartGetEnd()
171   {
172     SequenceI sq = new Sequence("test", "ABCDEF");
173     assertEquals(1, sq.getStart());
174     assertEquals(6, sq.getEnd());
175
176     sq = new Sequence("test", "--AB-C-DEF--");
177     assertEquals(1, sq.getStart());
178     assertEquals(6, sq.getEnd());
179
180     sq = new Sequence("test", "----");
181     assertEquals(1, sq.getStart());
182     assertEquals(0, sq.getEnd()); // ??
183   }
184
185   /**
186    * Tests for the method that returns an alignment column position (base 1) for
187    * a given sequence position (base 1).
188    */
189   @Test(groups = { "Functional" })
190   public void testFindIndex()
191   {
192     SequenceI sq = new Sequence("test", "ABCDEF");
193     assertEquals(0, sq.findIndex(0));
194     assertEquals(1, sq.findIndex(1));
195     assertEquals(5, sq.findIndex(5));
196     assertEquals(6, sq.findIndex(6));
197     assertEquals(6, sq.findIndex(9));
198
199     sq = new Sequence("test", "-A--B-C-D-E-F--");
200     assertEquals(2, sq.findIndex(1));
201     assertEquals(5, sq.findIndex(2));
202     assertEquals(7, sq.findIndex(3));
203
204     // before start returns 0
205     assertEquals(0, sq.findIndex(0));
206     assertEquals(0, sq.findIndex(-1));
207
208     // beyond end returns last residue column
209     assertEquals(13, sq.findIndex(99));
210
211   }
212
213   /**
214    * Tests for the method that returns a dataset sequence position (base 1) for
215    * an aligned column position (base 0).
216    */
217   @Test(groups = { "Functional" })
218   public void testFindPosition()
219   {
220     SequenceI sq = new Sequence("test", "ABCDEF");
221     assertEquals(1, sq.findPosition(0));
222     assertEquals(6, sq.findPosition(5));
223     // assertEquals(-1, seq.findPosition(6)); // fails
224
225     sq = new Sequence("test", "AB-C-D--");
226     assertEquals(1, sq.findPosition(0));
227     assertEquals(2, sq.findPosition(1));
228     // gap position 'finds' residue to the right (not the left as per javadoc)
229     assertEquals(3, sq.findPosition(2));
230     assertEquals(3, sq.findPosition(3));
231     assertEquals(4, sq.findPosition(4));
232     assertEquals(4, sq.findPosition(5));
233     // returns 1 more than sequence length if off the end ?!?
234     assertEquals(5, sq.findPosition(6));
235     assertEquals(5, sq.findPosition(7));
236
237     sq = new Sequence("test", "--AB-C-DEF--");
238     assertEquals(1, sq.findPosition(0));
239     assertEquals(1, sq.findPosition(1));
240     assertEquals(1, sq.findPosition(2));
241     assertEquals(2, sq.findPosition(3));
242     assertEquals(3, sq.findPosition(4));
243     assertEquals(3, sq.findPosition(5));
244     assertEquals(4, sq.findPosition(6));
245     assertEquals(4, sq.findPosition(7));
246     assertEquals(5, sq.findPosition(8));
247     assertEquals(6, sq.findPosition(9));
248     assertEquals(7, sq.findPosition(10));
249     assertEquals(7, sq.findPosition(11));
250   }
251
252   @Test(groups = { "Functional" })
253   public void testDeleteChars()
254   {
255     SequenceI sq = new Sequence("test", "ABCDEF");
256     assertEquals(1, sq.getStart());
257     assertEquals(6, sq.getEnd());
258     sq.deleteChars(2, 3);
259     assertEquals("ABDEF", sq.getSequenceAsString());
260     assertEquals(1, sq.getStart());
261     assertEquals(5, sq.getEnd());
262
263     sq = new Sequence("test", "ABCDEF");
264     sq.deleteChars(0, 2);
265     assertEquals("CDEF", sq.getSequenceAsString());
266     assertEquals(3, sq.getStart());
267     assertEquals(6, sq.getEnd());
268   }
269
270   @Test(groups = { "Functional" })
271   public void testInsertCharAt()
272   {
273     // non-static methods:
274     SequenceI sq = new Sequence("test", "ABCDEF");
275     sq.insertCharAt(0, 'z');
276     assertEquals("zABCDEF", sq.getSequenceAsString());
277     sq.insertCharAt(2, 2, 'x');
278     assertEquals("zAxxBCDEF", sq.getSequenceAsString());
279
280     // for static method see StringUtilsTest
281   }
282
283   /**
284    * Test the method that returns an array of aligned sequence positions where
285    * the array index is the data sequence position (both base 0).
286    */
287   @Test(groups = { "Functional" })
288   public void testGapMap()
289   {
290     SequenceI sq = new Sequence("test", "-A--B-CD-E--F-");
291     sq.createDatasetSequence();
292     assertEquals("[1, 4, 6, 7, 9, 12]", Arrays.toString(sq.gapMap()));
293   }
294
295   /**
296    * Test the method that gets sequence features, either from the sequence or
297    * its dataset.
298    */
299   @Test(groups = { "Functional" })
300   public void testGetSequenceFeatures()
301   {
302     SequenceI sq = new Sequence("test", "GATCAT");
303     sq.createDatasetSequence();
304
305     assertNull(sq.getSequenceFeatures());
306
307     /*
308      * SequenceFeature on sequence
309      */
310     SequenceFeature sf = new SequenceFeature();
311     sq.addSequenceFeature(sf);
312     SequenceFeature[] sfs = sq.getSequenceFeatures();
313     assertEquals(1, sfs.length);
314     assertSame(sf, sfs[0]);
315
316
317     /*
318      * SequenceFeature on sequence and dataset sequence; returns that on
319      * sequence
320      * 
321      * Note JAL-2046: spurious: we have no use case for this at the moment.
322      * This test also buggy - as sf2.equals(sf), no new feature is added
323      */
324     SequenceFeature sf2 = new SequenceFeature();
325     sq.getDatasetSequence().addSequenceFeature(sf2);
326     sfs = sq.getSequenceFeatures();
327     assertEquals(1, sfs.length);
328     assertSame(sf, sfs[0]);
329
330     /*
331      * SequenceFeature on dataset sequence only
332      * Note JAL-2046: spurious: we have no use case for setting a non-dataset sequence's feature array to null at the moment.
333      */
334     sq.setSequenceFeatures(null);
335     assertNull(sq.getDatasetSequence().getSequenceFeatures());
336
337     /*
338      * Corrupt case - no SequenceFeature, dataset's dataset is the original
339      * sequence. Test shows no infinite loop results.
340      */
341     sq.getDatasetSequence().setSequenceFeatures(null);
342     /**
343      * is there a usecase for this ? setDatasetSequence should throw an error if
344      * this actually occurs.
345      */
346     sq.getDatasetSequence().setDatasetSequence(sq); // loop!
347     assertNull(sq.getSequenceFeatures());
348   }
349
350   /**
351    * Test the method that returns an array, indexed by sequence position, whose
352    * entries are the residue positions at the sequence position (or to the right
353    * if a gap)
354    */
355   @Test(groups = { "Functional" })
356   public void testFindPositionMap()
357   {
358     /*
359      * Note: Javadoc for findPosition says it returns the residue position to
360      * the left of a gapped position; in fact it returns the position to the
361      * right. Also it returns a non-existent residue position for a gap beyond
362      * the sequence.
363      */
364     Sequence sq = new Sequence("TestSeq", "AB.C-D E.");
365     int[] map = sq.findPositionMap();
366     assertEquals(Arrays.toString(new int[] { 1, 2, 3, 3, 4, 4, 5, 5, 6 }),
367             Arrays.toString(map));
368   }
369
370   /**
371    * Test for getSubsequence
372    */
373   @Test(groups = { "Functional" })
374   public void testGetSubsequence()
375   {
376     SequenceI sq = new Sequence("TestSeq", "ABCDEFG");
377     sq.createDatasetSequence();
378
379     // positions are base 0, end position is exclusive
380     SequenceI subseq = sq.getSubSequence(2, 4);
381
382     assertEquals("CD", subseq.getSequenceAsString());
383     // start/end are base 1 positions
384     assertEquals(3, subseq.getStart());
385     assertEquals(4, subseq.getEnd());
386     // subsequence shares the full dataset sequence
387     assertSame(sq.getDatasetSequence(), subseq.getDatasetSequence());
388   }
389
390   /**
391    * Test for deriveSequence applied to a sequence with a dataset
392    */
393   @Test(groups = { "Functional" })
394   public void testDeriveSequence_existingDataset()
395   {
396     Sequence sq = new Sequence("Seq1", "CD");
397     sq.setDatasetSequence(new Sequence("Seq1", "ABCDEF"));
398     sq.getDatasetSequence().addSequenceFeature(
399             new SequenceFeature("", "", 1, 2, 0f, null));
400     sq.setStart(3);
401     sq.setEnd(4);
402
403     sq.setDescription("Test sequence description..");
404     sq.setVamsasId("TestVamsasId");
405     sq.setSourceDBRef(new DBRefEntry("PDB", "version0", "1TST"));
406
407     sq.addDBRef(new DBRefEntry("PDB", "version1", "1Tst"));
408     sq.addDBRef(new DBRefEntry("PDB", "version2", "2Tst"));
409     sq.addDBRef(new DBRefEntry("PDB", "version3", "3Tst"));
410     sq.addDBRef(new DBRefEntry("PDB", "version4", "4Tst"));
411
412     sq.addPDBId(new PDBEntry("1PDB", "A", Type.PDB, "filePath/test1"));
413     sq.addPDBId(new PDBEntry("1PDB", "B", Type.PDB, "filePath/test1"));
414     sq.addPDBId(new PDBEntry("2PDB", "A", Type.MMCIF, "filePath/test2"));
415     sq.addPDBId(new PDBEntry("2PDB", "B", Type.MMCIF, "filePath/test2"));
416
417     sq.getDatasetSequence().addDBRef(
418             new DBRefEntry("PDB", "version1", "1Tst"));
419     sq.getDatasetSequence().addDBRef(
420             new DBRefEntry("PDB", "version2", "2Tst"));
421     sq.getDatasetSequence().addDBRef(
422             new DBRefEntry("PDB", "version3", "3Tst"));
423     sq.getDatasetSequence().addDBRef(
424             new DBRefEntry("PDB", "version4", "4Tst"));
425
426     sq.getDatasetSequence().addPDBId(
427             new PDBEntry("1PDB", "A", Type.PDB, "filePath/test1"));
428     sq.getDatasetSequence().addPDBId(
429             new PDBEntry("1PDB", "B", Type.PDB, "filePath/test1"));
430     sq.getDatasetSequence().addPDBId(
431             new PDBEntry("2PDB", "A", Type.MMCIF, "filePath/test2"));
432     sq.getDatasetSequence().addPDBId(
433             new PDBEntry("2PDB", "B", Type.MMCIF, "filePath/test2"));
434
435     ArrayList<Annotation> annotsList = new ArrayList<Annotation>();
436     System.out.println(">>>>>> " + sq.getSequenceAsString().length());
437     annotsList.add(new Annotation("A", "A", 'X', 0.1f));
438     annotsList.add(new Annotation("A", "A", 'X', 0.1f));
439     Annotation[] annots = annotsList.toArray(new Annotation[0]);
440     sq.addAlignmentAnnotation(new AlignmentAnnotation("Test annot",
441             "Test annot description", annots));
442     sq.getDatasetSequence().addAlignmentAnnotation(
443             new AlignmentAnnotation("Test annot", "Test annot description",
444                     annots));
445     Assert.assertEquals(sq.getDescription(), "Test sequence description..");
446     Assert.assertEquals(sq.getDBRefs().length, 4);
447     Assert.assertEquals(sq.getAllPDBEntries().size(), 4);
448     Assert.assertNotNull(sq.getAnnotation());
449     Assert.assertEquals(sq.getAnnotation()[0].annotations.length, 2);
450     Assert.assertEquals(sq.getDatasetSequence().getDBRefs().length, 4);
451     Assert.assertEquals(sq.getDatasetSequence().getAllPDBEntries().size(),
452             4);
453     Assert.assertNotNull(sq.getDatasetSequence().getAnnotation());
454
455     Sequence derived = (Sequence) sq.deriveSequence();
456
457     Assert.assertEquals(derived.getDescription(),
458             "Test sequence description..");
459     Assert.assertEquals(derived.getDBRefs().length, 4);
460     Assert.assertEquals(derived.getAllPDBEntries().size(), 4);
461     Assert.assertNotNull(derived.getAnnotation());
462     Assert.assertEquals(derived.getAnnotation()[0].annotations.length, 2);
463     Assert.assertEquals(derived.getDatasetSequence().getDBRefs().length, 4);
464     Assert.assertEquals(derived.getDatasetSequence().getAllPDBEntries()
465             .size(), 4);
466     Assert.assertNotNull(derived.getDatasetSequence().getAnnotation());
467
468     assertEquals("CD", derived.getSequenceAsString());
469     assertSame(sq.getDatasetSequence(), derived.getDatasetSequence());
470
471     assertNull(sq.sequenceFeatures);
472     assertNull(derived.sequenceFeatures);
473     // derived sequence should access dataset sequence features
474     assertNotNull(sq.getSequenceFeatures());
475     assertArrayEquals(sq.getSequenceFeatures(),
476             derived.getSequenceFeatures());
477   }
478
479   /**
480    * Test for deriveSequence applied to an ungapped sequence with no dataset
481    */
482   @Test(groups = { "Functional" })
483   public void testDeriveSequence_noDatasetUngapped()
484   {
485     SequenceI sq = new Sequence("Seq1", "ABCDEF");
486     assertEquals(1, sq.getStart());
487     assertEquals(6, sq.getEnd());
488     SequenceI derived = sq.deriveSequence();
489     assertEquals("ABCDEF", derived.getSequenceAsString());
490     assertEquals("ABCDEF", derived.getDatasetSequence()
491             .getSequenceAsString());
492   }
493
494   /**
495    * Test for deriveSequence applied to a gapped sequence with no dataset
496    */
497   @Test(groups = { "Functional" })
498   public void testDeriveSequence_noDatasetGapped()
499   {
500     SequenceI sq = new Sequence("Seq1", "AB-C.D EF");
501     assertEquals(1, sq.getStart());
502     assertEquals(6, sq.getEnd());
503     assertNull(sq.getDatasetSequence());
504     SequenceI derived = sq.deriveSequence();
505     assertEquals("AB-C.D EF", derived.getSequenceAsString());
506     assertEquals("ABCDEF", derived.getDatasetSequence()
507             .getSequenceAsString());
508   }
509
510   @Test(groups = { "Functional" })
511   public void testCopyConstructor_noDataset()
512   {
513     SequenceI seq1 = new Sequence("Seq1", "AB-C.D EF");
514     seq1.setDescription("description");
515     seq1.addAlignmentAnnotation(new AlignmentAnnotation("label", "desc",
516             1.3d));
517     seq1.addSequenceFeature(new SequenceFeature("type", "desc", 22, 33,
518             12.4f, "group"));
519     seq1.addPDBId(new PDBEntry("1A70", "B", Type.PDB, "File"));
520     seq1.addDBRef(new DBRefEntry("EMBL", "1.2", "AZ12345"));
521     
522     SequenceI copy = new Sequence(seq1);
523
524     assertNull(copy.getDatasetSequence());
525
526     verifyCopiedSequence(seq1, copy);
527
528     // copy has a copy of the DBRefEntry
529     // this is murky - DBrefs are only copied for dataset sequences
530     // where the test for 'dataset sequence' is 'dataset is null'
531     // but that doesn't distinguish it from an aligned sequence
532     // which has not yet generated a dataset sequence
533     // NB getDBRef looks inside dataset sequence if not null
534     DBRefEntry[] dbrefs = copy.getDBRefs();
535     assertEquals(1, dbrefs.length);
536     assertFalse(dbrefs[0] == seq1.getDBRefs()[0]);
537     assertTrue(dbrefs[0].equals(seq1.getDBRefs()[0]));
538   }
539
540   @Test(groups = { "Functional" })
541   public void testCopyConstructor_withDataset()
542   {
543     SequenceI seq1 = new Sequence("Seq1", "AB-C.D EF");
544     seq1.createDatasetSequence();
545     seq1.setDescription("description");
546     seq1.addAlignmentAnnotation(new AlignmentAnnotation("label", "desc",
547             1.3d));
548     // JAL-2046 - what is the contract for using a derived sequence's
549     // addSequenceFeature ?
550     seq1.addSequenceFeature(new SequenceFeature("type", "desc", 22, 33,
551             12.4f, "group"));
552     seq1.addPDBId(new PDBEntry("1A70", "B", Type.PDB, "File"));
553     // here we add DBRef to the dataset sequence:
554     seq1.getDatasetSequence().addDBRef(
555             new DBRefEntry("EMBL", "1.2", "AZ12345"));
556
557     SequenceI copy = new Sequence(seq1);
558
559     assertNotNull(copy.getDatasetSequence());
560     assertSame(copy.getDatasetSequence(), seq1.getDatasetSequence());
561
562     verifyCopiedSequence(seq1, copy);
563
564     // getDBRef looks inside dataset sequence and this is shared,
565     // so holds the same dbref objects
566     DBRefEntry[] dbrefs = copy.getDBRefs();
567     assertEquals(1, dbrefs.length);
568     assertSame(dbrefs[0], seq1.getDBRefs()[0]);
569   }
570
571   /**
572    * Helper to make assertions about a copied sequence
573    * 
574    * @param seq1
575    * @param copy
576    */
577   protected void verifyCopiedSequence(SequenceI seq1, SequenceI copy)
578   {
579     // verify basic properties:
580     assertEquals(copy.getName(), seq1.getName());
581     assertEquals(copy.getDescription(), seq1.getDescription());
582     assertEquals(copy.getStart(), seq1.getStart());
583     assertEquals(copy.getEnd(), seq1.getEnd());
584     assertEquals(copy.getSequenceAsString(), seq1.getSequenceAsString());
585
586     // copy has a copy of the annotation:
587     AlignmentAnnotation[] anns = copy.getAnnotation();
588     assertEquals(1, anns.length);
589     assertFalse(anns[0] == seq1.getAnnotation()[0]);
590     assertEquals(anns[0].label, seq1.getAnnotation()[0].label);
591     assertEquals(anns[0].description, seq1.getAnnotation()[0].description);
592     assertEquals(anns[0].score, seq1.getAnnotation()[0].score);
593
594     // copy has a copy of the sequence feature:
595     SequenceFeature[] sfs = copy.getSequenceFeatures();
596     assertEquals(1, sfs.length);
597     if (seq1.getDatasetSequence()!=null && copy.getDatasetSequence()==seq1.getDatasetSequence()) {
598       assertTrue(sfs[0] == seq1.getSequenceFeatures()[0]);
599     } else {
600       assertFalse(sfs[0] == seq1.getSequenceFeatures()[0]);
601     }
602     assertTrue(sfs[0].equals(seq1.getSequenceFeatures()[0]));
603
604     // copy has a copy of the PDB entry
605     Vector<PDBEntry> pdbs = copy.getAllPDBEntries();
606     assertEquals(1, pdbs.size());
607     assertFalse(pdbs.get(0) == seq1.getAllPDBEntries().get(0));
608     assertTrue(pdbs.get(0).equals(seq1.getAllPDBEntries().get(0)));
609   }
610
611   @Test(groups = "Functional")
612   public void testGetCharAt()
613   {
614     SequenceI sq = new Sequence("", "abcde");
615     assertEquals('a', sq.getCharAt(0));
616     assertEquals('e', sq.getCharAt(4));
617     assertEquals(' ', sq.getCharAt(5));
618     assertEquals(' ', sq.getCharAt(-1));
619   }
620
621   /**
622    * Tests for adding (or updating) dbrefs
623    * 
624    * @see DBRefEntry#updateFrom(DBRefEntry)
625    */
626   @Test(groups = { "Functional" })
627   public void testAddDBRef()
628   {
629     SequenceI sq = new Sequence("", "abcde");
630     assertNull(sq.getDBRefs());
631     DBRefEntry dbref = new DBRefEntry("Uniprot", "1", "P00340");
632     sq.addDBRef(dbref);
633     assertEquals(1, sq.getDBRefs().length);
634     assertSame(dbref, sq.getDBRefs()[0]);
635
636     /*
637      * change of version - new entry
638      */
639     DBRefEntry dbref2 = new DBRefEntry("Uniprot", "2", "P00340");
640     sq.addDBRef(dbref2);
641     assertEquals(2, sq.getDBRefs().length);
642     assertSame(dbref, sq.getDBRefs()[0]);
643     assertSame(dbref2, sq.getDBRefs()[1]);
644
645     /*
646      * matches existing entry - not added
647      */
648     sq.addDBRef(new DBRefEntry("UNIPROT", "1", "p00340"));
649     assertEquals(2, sq.getDBRefs().length);
650
651     /*
652      * different source = new entry
653      */
654     DBRefEntry dbref3 = new DBRefEntry("UniRef", "1", "p00340");
655     sq.addDBRef(dbref3);
656     assertEquals(3, sq.getDBRefs().length);
657     assertSame(dbref3, sq.getDBRefs()[2]);
658
659     /*
660      * different ref = new entry
661      */
662     DBRefEntry dbref4 = new DBRefEntry("UniRef", "1", "p00341");
663     sq.addDBRef(dbref4);
664     assertEquals(4, sq.getDBRefs().length);
665     assertSame(dbref4, sq.getDBRefs()[3]);
666
667     /*
668      * matching ref with a mapping - map updated
669      */
670     DBRefEntry dbref5 = new DBRefEntry("UniRef", "1", "p00341");
671     Mapping map = new Mapping(new MapList(new int[] { 1, 3 }, new int[] {
672         1, 1 }, 3, 1));
673     dbref5.setMap(map);
674     sq.addDBRef(dbref5);
675     assertEquals(4, sq.getDBRefs().length);
676     assertSame(dbref4, sq.getDBRefs()[3]);
677     assertSame(map, dbref4.getMap());
678
679     /*
680      * 'real' version replaces "0" version
681      */
682     dbref2.setVersion("0");
683     DBRefEntry dbref6 = new DBRefEntry(dbref2.getSource(), "3",
684             dbref2.getAccessionId());
685     sq.addDBRef(dbref6);
686     assertEquals(4, sq.getDBRefs().length);
687     assertSame(dbref2, sq.getDBRefs()[1]);
688     assertEquals("3", dbref2.getVersion());
689
690     /*
691      * 'real' version replaces "source:0" version
692      */
693     dbref3.setVersion("Uniprot:0");
694     DBRefEntry dbref7 = new DBRefEntry(dbref3.getSource(), "3",
695             dbref3.getAccessionId());
696     sq.addDBRef(dbref7);
697     assertEquals(4, sq.getDBRefs().length);
698     assertSame(dbref3, sq.getDBRefs()[2]);
699     assertEquals("3", dbref2.getVersion());
700   }
701 }