JAL-2526 Sequence.findPositions to get residue positions for column
[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.assertNotSame;
27 import static org.testng.AssertJUnit.assertNull;
28 import static org.testng.AssertJUnit.assertSame;
29 import static org.testng.AssertJUnit.assertTrue;
30
31 import jalview.datamodel.PDBEntry.Type;
32 import jalview.gui.JvOptionPane;
33 import jalview.util.MapList;
34
35 import java.io.File;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.List;
39 import java.util.Vector;
40
41 import junit.extensions.PA;
42
43 import org.testng.Assert;
44 import org.testng.annotations.BeforeClass;
45 import org.testng.annotations.BeforeMethod;
46 import org.testng.annotations.Test;
47
48 public class SequenceTest
49 {
50
51   @BeforeClass(alwaysRun = true)
52   public void setUpJvOptionPane()
53   {
54     JvOptionPane.setInteractiveMode(false);
55     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
56   }
57
58   Sequence seq;
59
60   @BeforeMethod(alwaysRun = true)
61   public void setUp()
62   {
63     seq = new Sequence("FER1", "AKPNGVL");
64   }
65
66   @Test(groups = { "Functional" })
67   public void testInsertGapsAndGapmaps()
68   {
69     SequenceI aseq = seq.deriveSequence();
70     aseq.insertCharAt(2, 3, '-');
71     aseq.insertCharAt(6, 3, '-');
72     assertEquals("Gap insertions not correct", "AK---P---NGVL",
73             aseq.getSequenceAsString());
74     List<int[]> gapInt = aseq.getInsertions();
75     assertEquals("Gap interval 1 start wrong", 2, gapInt.get(0)[0]);
76     assertEquals("Gap interval 1 end wrong", 4, gapInt.get(0)[1]);
77     assertEquals("Gap interval 2 start wrong", 6, gapInt.get(1)[0]);
78     assertEquals("Gap interval 2 end wrong", 8, gapInt.get(1)[1]);
79   }
80
81   @Test(groups = ("Functional"))
82   public void testIsProtein()
83   {
84     // test Protein
85     assertTrue(new Sequence("prot", "ASDFASDFASDF").isProtein());
86     // test DNA
87     assertFalse(new Sequence("prot", "ACGTACGTACGT").isProtein());
88     // test RNA
89     SequenceI sq = new Sequence("prot", "ACGUACGUACGU");
90     assertFalse(sq.isProtein());
91     // change sequence, should trigger an update of cached result
92     sq.setSequence("ASDFASDFADSF");
93     assertTrue(sq.isProtein());
94     /*
95      * in situ change of sequence doesn't change hashcode :-O
96      * (sequence should not expose internal implementation)
97      */
98     for (int i = 0; i < sq.getSequence().length; i++)
99     {
100       sq.getSequence()[i] = "acgtu".charAt(i % 5);
101     }
102     assertTrue(sq.isProtein()); // but it isn't
103   }
104
105   @Test(groups = { "Functional" })
106   public void testGetAnnotation()
107   {
108     // initial state returns null not an empty array
109     assertNull(seq.getAnnotation());
110     AlignmentAnnotation ann = addAnnotation("label1", "desc1", "calcId1",
111             1f);
112     AlignmentAnnotation[] anns = seq.getAnnotation();
113     assertEquals(1, anns.length);
114     assertSame(ann, anns[0]);
115
116     // removing all annotations reverts array to null
117     seq.removeAlignmentAnnotation(ann);
118     assertNull(seq.getAnnotation());
119   }
120
121   @Test(groups = { "Functional" })
122   public void testGetAnnotation_forLabel()
123   {
124     AlignmentAnnotation ann1 = addAnnotation("label1", "desc1", "calcId1",
125             1f);
126     addAnnotation("label2", "desc2", "calcId2", 1f);
127     AlignmentAnnotation ann3 = addAnnotation("label1", "desc3", "calcId3",
128             1f);
129     AlignmentAnnotation[] anns = seq.getAnnotation("label1");
130     assertEquals(2, anns.length);
131     assertSame(ann1, anns[0]);
132     assertSame(ann3, anns[1]);
133   }
134
135   private AlignmentAnnotation addAnnotation(String label,
136           String description, String calcId, float value)
137   {
138     final AlignmentAnnotation annotation = new AlignmentAnnotation(label,
139             description, value);
140     annotation.setCalcId(calcId);
141     seq.addAlignmentAnnotation(annotation);
142     return annotation;
143   }
144
145   @Test(groups = { "Functional" })
146   public void testGetAlignmentAnnotations_forCalcIdAndLabel()
147   {
148     addAnnotation("label1", "desc1", "calcId1", 1f);
149     AlignmentAnnotation ann2 = addAnnotation("label2", "desc2", "calcId2",
150             1f);
151     addAnnotation("label2", "desc3", "calcId3", 1f);
152     AlignmentAnnotation ann4 = addAnnotation("label2", "desc3", "calcId2",
153             1f);
154     addAnnotation("label5", "desc3", null, 1f);
155     addAnnotation(null, "desc3", "calcId3", 1f);
156
157     List<AlignmentAnnotation> anns = seq.getAlignmentAnnotations("calcId2",
158             "label2");
159     assertEquals(2, anns.size());
160     assertSame(ann2, anns.get(0));
161     assertSame(ann4, anns.get(1));
162
163     assertTrue(seq.getAlignmentAnnotations("calcId2", "label3").isEmpty());
164     assertTrue(seq.getAlignmentAnnotations("calcId3", "label5").isEmpty());
165     assertTrue(seq.getAlignmentAnnotations("calcId2", null).isEmpty());
166     assertTrue(seq.getAlignmentAnnotations(null, "label3").isEmpty());
167     assertTrue(seq.getAlignmentAnnotations(null, null).isEmpty());
168   }
169
170   /**
171    * Tests for addAlignmentAnnotation. Note this method has the side-effect of
172    * setting the sequenceRef on the annotation. Adding the same annotation twice
173    * should be ignored.
174    */
175   @Test(groups = { "Functional" })
176   public void testAddAlignmentAnnotation()
177   {
178     assertNull(seq.getAnnotation());
179     final AlignmentAnnotation annotation = new AlignmentAnnotation("a",
180             "b", 2d);
181     assertNull(annotation.sequenceRef);
182     seq.addAlignmentAnnotation(annotation);
183     assertSame(seq, annotation.sequenceRef);
184     AlignmentAnnotation[] anns = seq.getAnnotation();
185     assertEquals(1, anns.length);
186     assertSame(annotation, anns[0]);
187
188     // re-adding does nothing
189     seq.addAlignmentAnnotation(annotation);
190     anns = seq.getAnnotation();
191     assertEquals(1, anns.length);
192     assertSame(annotation, anns[0]);
193
194     // an identical but different annotation can be added
195     final AlignmentAnnotation annotation2 = new AlignmentAnnotation("a",
196             "b", 2d);
197     seq.addAlignmentAnnotation(annotation2);
198     anns = seq.getAnnotation();
199     assertEquals(2, anns.length);
200     assertSame(annotation, anns[0]);
201     assertSame(annotation2, anns[1]);
202   }
203
204   @Test(groups = { "Functional" })
205   public void testGetStartGetEnd()
206   {
207     SequenceI sq = new Sequence("test", "ABCDEF");
208     assertEquals(1, sq.getStart());
209     assertEquals(6, sq.getEnd());
210
211     sq = new Sequence("test", "--AB-C-DEF--");
212     assertEquals(1, sq.getStart());
213     assertEquals(6, sq.getEnd());
214
215     sq = new Sequence("test", "----");
216     assertEquals(1, sq.getStart());
217     assertEquals(0, sq.getEnd()); // ??
218   }
219
220   /**
221    * Tests for the method that returns an alignment column position (base 1) for
222    * a given sequence position (base 1).
223    */
224   @Test(groups = { "Functional" })
225   public void testFindIndex()
226   {
227     SequenceI sq = new Sequence("test", "ABCDEF");
228     assertEquals(0, sq.findIndex(0));
229     assertEquals(1, sq.findIndex(1));
230     assertEquals(5, sq.findIndex(5));
231     assertEquals(6, sq.findIndex(6));
232     assertEquals(6, sq.findIndex(9));
233
234     sq = new Sequence("test/8-13", "-A--B-C-D-E-F--");
235     assertEquals(2, sq.findIndex(8));
236     assertEquals(5, sq.findIndex(9));
237     assertEquals(7, sq.findIndex(10));
238
239     // before start returns 0
240     assertEquals(0, sq.findIndex(0));
241     assertEquals(0, sq.findIndex(-1));
242
243     // beyond end returns last residue column
244     assertEquals(13, sq.findIndex(99));
245
246   }
247
248   /**
249    * Tests for the method that returns a dataset sequence position (base 1) for
250    * an aligned column position (base 0).
251    */
252   @Test(groups = { "Functional" })
253   public void testFindPosition()
254   {
255     SequenceI sq = new Sequence("test", "ABCDEF");
256     assertEquals(1, sq.findPosition(0));
257     assertEquals(6, sq.findPosition(5));
258     // assertEquals(-1, seq.findPosition(6)); // fails
259
260     sq = new Sequence("test", "AB-C-D--");
261     assertEquals(1, sq.findPosition(0));
262     assertEquals(2, sq.findPosition(1));
263     // gap position 'finds' residue to the right (not the left as per javadoc)
264     assertEquals(3, sq.findPosition(2));
265     assertEquals(3, sq.findPosition(3));
266     assertEquals(4, sq.findPosition(4));
267     assertEquals(4, sq.findPosition(5));
268     // returns 1 more than sequence length if off the end ?!?
269     assertEquals(5, sq.findPosition(6));
270     assertEquals(5, sq.findPosition(7));
271
272     sq = new Sequence("test", "--AB-C-DEF--");
273     assertEquals(1, sq.findPosition(0));
274     assertEquals(1, sq.findPosition(1));
275     assertEquals(1, sq.findPosition(2));
276     assertEquals(2, sq.findPosition(3));
277     assertEquals(3, sq.findPosition(4));
278     assertEquals(3, sq.findPosition(5));
279     assertEquals(4, sq.findPosition(6));
280     assertEquals(4, sq.findPosition(7));
281     assertEquals(5, sq.findPosition(8));
282     assertEquals(6, sq.findPosition(9));
283     assertEquals(7, sq.findPosition(10));
284     assertEquals(7, sq.findPosition(11));
285   }
286
287   @Test(groups = { "Functional" })
288   public void testDeleteChars()
289   {
290     /*
291      * internal delete
292      */
293     SequenceI sq = new Sequence("test", "ABCDEF");
294     assertNull(PA.getValue(sq, "datasetSequence"));
295     assertEquals(1, sq.getStart());
296     assertEquals(6, sq.getEnd());
297     sq.deleteChars(2, 3);
298     assertEquals("ABDEF", sq.getSequenceAsString());
299     assertEquals(1, sq.getStart());
300     assertEquals(5, sq.getEnd());
301     assertNull(PA.getValue(sq, "datasetSequence"));
302
303     /*
304      * delete at start
305      */
306     sq = new Sequence("test", "ABCDEF");
307     sq.deleteChars(0, 2);
308     assertEquals("CDEF", sq.getSequenceAsString());
309     assertEquals(3, sq.getStart());
310     assertEquals(6, sq.getEnd());
311     assertNull(PA.getValue(sq, "datasetSequence"));
312
313     /*
314      * delete at end
315      */
316     sq = new Sequence("test", "ABCDEF");
317     sq.deleteChars(4, 6);
318     assertEquals("ABCD", sq.getSequenceAsString());
319     assertEquals(1, sq.getStart());
320     assertEquals(4, sq.getEnd());
321     assertNull(PA.getValue(sq, "datasetSequence"));
322   }
323
324   @Test(groups = { "Functional" })
325   public void testDeleteChars_withDbRefsAndFeatures()
326   {
327     /*
328      * internal delete - new dataset sequence created
329      * gets a copy of any dbrefs
330      */
331     SequenceI sq = new Sequence("test", "ABCDEF");
332     sq.createDatasetSequence();
333     DBRefEntry dbr1 = new DBRefEntry("Uniprot", "0", "a123");
334     sq.addDBRef(dbr1);
335     Object ds = PA.getValue(sq, "datasetSequence");
336     assertNotNull(ds);
337     assertEquals(1, sq.getStart());
338     assertEquals(6, sq.getEnd());
339     sq.deleteChars(2, 3);
340     assertEquals("ABDEF", sq.getSequenceAsString());
341     assertEquals(1, sq.getStart());
342     assertEquals(5, sq.getEnd());
343     Object newDs = PA.getValue(sq, "datasetSequence");
344     assertNotNull(newDs);
345     assertNotSame(ds, newDs);
346     assertNotNull(sq.getDBRefs());
347     assertEquals(1, sq.getDBRefs().length);
348     assertNotSame(dbr1, sq.getDBRefs()[0]);
349     assertEquals(dbr1, sq.getDBRefs()[0]);
350
351     /*
352      * internal delete with sequence features
353      * (failure case for JAL-2541)
354      */
355     sq = new Sequence("test", "ABCDEF");
356     sq.createDatasetSequence();
357     SequenceFeature sf1 = new SequenceFeature("Cath", "desc", 2, 4, 2f,
358             "CathGroup");
359     sq.addSequenceFeature(sf1);
360     ds = PA.getValue(sq, "datasetSequence");
361     assertNotNull(ds);
362     assertEquals(1, sq.getStart());
363     assertEquals(6, sq.getEnd());
364     sq.deleteChars(2, 4);
365     assertEquals("ABEF", sq.getSequenceAsString());
366     assertEquals(1, sq.getStart());
367     assertEquals(4, sq.getEnd());
368     newDs = PA.getValue(sq, "datasetSequence");
369     assertNotNull(newDs);
370     assertNotSame(ds, newDs);
371     List<SequenceFeature> sfs = sq.getSequenceFeatures();
372     assertEquals(1, sfs.size());
373     assertNotSame(sf1, sfs.get(0));
374     assertEquals(sf1, sfs.get(0));
375
376     /*
377      * delete at start - no new dataset sequence created
378      * any sequence features remain as before
379      */
380     sq = new Sequence("test", "ABCDEF");
381     sq.createDatasetSequence();
382     ds = PA.getValue(sq, "datasetSequence");
383     sf1 = new SequenceFeature("Cath", "desc", 2, 4, 2f, "CathGroup");
384     sq.addSequenceFeature(sf1);
385     sq.deleteChars(0, 2);
386     assertEquals("CDEF", sq.getSequenceAsString());
387     assertEquals(3, sq.getStart());
388     assertEquals(6, sq.getEnd());
389     assertSame(ds, PA.getValue(sq, "datasetSequence"));
390     sfs = sq.getSequenceFeatures();
391     assertNotNull(sfs);
392     assertEquals(1, sfs.size());
393     assertSame(sf1, sfs.get(0));
394
395     /*
396      * delete at end - no new dataset sequence created
397      * any dbrefs remain as before
398      */
399     sq = new Sequence("test", "ABCDEF");
400     sq.createDatasetSequence();
401     ds = PA.getValue(sq, "datasetSequence");
402     dbr1 = new DBRefEntry("Uniprot", "0", "a123");
403     sq.addDBRef(dbr1);
404     sq.deleteChars(4, 6);
405     assertEquals("ABCD", sq.getSequenceAsString());
406     assertEquals(1, sq.getStart());
407     assertEquals(4, sq.getEnd());
408     assertSame(ds, PA.getValue(sq, "datasetSequence"));
409     assertNotNull(sq.getDBRefs());
410     assertEquals(1, sq.getDBRefs().length);
411     assertSame(dbr1, sq.getDBRefs()[0]);
412   }
413
414   @Test(groups = { "Functional" })
415   public void testInsertCharAt()
416   {
417     // non-static methods:
418     SequenceI sq = new Sequence("test", "ABCDEF");
419     sq.insertCharAt(0, 'z');
420     assertEquals("zABCDEF", sq.getSequenceAsString());
421     sq.insertCharAt(2, 2, 'x');
422     assertEquals("zAxxBCDEF", sq.getSequenceAsString());
423
424     // for static method see StringUtilsTest
425   }
426
427   /**
428    * Test the method that returns an array of aligned sequence positions where
429    * the array index is the data sequence position (both base 0).
430    */
431   @Test(groups = { "Functional" })
432   public void testGapMap()
433   {
434     SequenceI sq = new Sequence("test", "-A--B-CD-E--F-");
435     sq.createDatasetSequence();
436     assertEquals("[1, 4, 6, 7, 9, 12]", Arrays.toString(sq.gapMap()));
437   }
438
439   /**
440    * Test the method that gets sequence features, either from the sequence or
441    * its dataset.
442    */
443   @Test(groups = { "Functional" })
444   public void testGetSequenceFeatures()
445   {
446     SequenceI sq = new Sequence("test", "GATCAT");
447     sq.createDatasetSequence();
448
449     assertTrue(sq.getSequenceFeatures().isEmpty());
450
451     /*
452      * SequenceFeature on sequence
453      */
454     SequenceFeature sf = new SequenceFeature("Cath", "desc", 2, 4, 2f, null);
455     sq.addSequenceFeature(sf);
456     List<SequenceFeature> sfs = sq.getSequenceFeatures();
457     assertEquals(1, sfs.size());
458     assertSame(sf, sfs.get(0));
459
460     /*
461      * SequenceFeature on sequence and dataset sequence; returns that on
462      * sequence
463      * 
464      * Note JAL-2046: spurious: we have no use case for this at the moment.
465      * This test also buggy - as sf2.equals(sf), no new feature is added
466      */
467     SequenceFeature sf2 = new SequenceFeature("Cath", "desc", 2, 4, 2f,
468             null);
469     sq.getDatasetSequence().addSequenceFeature(sf2);
470     sfs = sq.getSequenceFeatures();
471     assertEquals(1, sfs.size());
472     assertSame(sf, sfs.get(0));
473
474     /*
475      * SequenceFeature on dataset sequence only
476      * Note JAL-2046: spurious: we have no use case for setting a non-dataset sequence's feature array to null at the moment.
477      */
478     sq.setSequenceFeatures(null);
479     assertTrue(sq.getDatasetSequence().getSequenceFeatures().isEmpty());
480
481     /*
482      * Corrupt case - no SequenceFeature, dataset's dataset is the original
483      * sequence. Test shows no infinite loop results.
484      */
485     sq.getDatasetSequence().setSequenceFeatures(null);
486     /**
487      * is there a usecase for this ? setDatasetSequence should throw an error if
488      * this actually occurs.
489      */
490     try
491     {
492       sq.getDatasetSequence().setDatasetSequence(sq); // loop!
493       Assert.fail("Expected Error to be raised when calling setDatasetSequence with self reference");
494     } catch (IllegalArgumentException e)
495     {
496       // TODO Jalview error/exception class for raising implementation errors
497       assertTrue(e.getMessage().toLowerCase()
498               .contains("implementation error"));
499     }
500     assertTrue(sq.getSequenceFeatures().isEmpty());
501   }
502
503   /**
504    * Test the method that returns an array, indexed by sequence position, whose
505    * entries are the residue positions at the sequence position (or to the right
506    * if a gap)
507    */
508   @Test(groups = { "Functional" })
509   public void testFindPositionMap()
510   {
511     /*
512      * Note: Javadoc for findPosition says it returns the residue position to
513      * the left of a gapped position; in fact it returns the position to the
514      * right. Also it returns a non-existent residue position for a gap beyond
515      * the sequence.
516      */
517     Sequence sq = new Sequence("TestSeq", "AB.C-D E.");
518     int[] map = sq.findPositionMap();
519     assertEquals(Arrays.toString(new int[] { 1, 2, 3, 3, 4, 4, 5, 5, 6 }),
520             Arrays.toString(map));
521   }
522
523   /**
524    * Test for getSubsequence
525    */
526   @Test(groups = { "Functional" })
527   public void testGetSubsequence()
528   {
529     SequenceI sq = new Sequence("TestSeq", "ABCDEFG");
530     sq.createDatasetSequence();
531
532     // positions are base 0, end position is exclusive
533     SequenceI subseq = sq.getSubSequence(2, 4);
534
535     assertEquals("CD", subseq.getSequenceAsString());
536     // start/end are base 1 positions
537     assertEquals(3, subseq.getStart());
538     assertEquals(4, subseq.getEnd());
539     // subsequence shares the full dataset sequence
540     assertSame(sq.getDatasetSequence(), subseq.getDatasetSequence());
541   }
542
543   /**
544    * test createDatasetSequence behaves to doc
545    */
546   @Test(groups = { "Functional" })
547   public void testCreateDatasetSequence()
548   {
549     SequenceI sq = new Sequence("my", "ASDASD");
550     sq.addSequenceFeature(new SequenceFeature("type", "desc", 1, 10, 1f,
551             "group"));
552     sq.addDBRef(new DBRefEntry("source", "version", "accession"));
553     assertNull(sq.getDatasetSequence());
554     assertNotNull(PA.getValue(sq, "sequenceFeatureStore"));
555     assertNotNull(PA.getValue(sq, "dbrefs"));
556
557     SequenceI rds = sq.createDatasetSequence();
558     assertNotNull(rds);
559     assertNull(rds.getDatasetSequence());
560     assertSame(sq.getDatasetSequence(), rds);
561
562     // sequence features and dbrefs transferred to dataset sequence
563     assertNull(PA.getValue(sq, "sequenceFeatureStore"));
564     assertNull(PA.getValue(sq, "dbrefs"));
565     assertNotNull(PA.getValue(rds, "sequenceFeatureStore"));
566     assertNotNull(PA.getValue(rds, "dbrefs"));
567   }
568
569   /**
570    * Test for deriveSequence applied to a sequence with a dataset
571    */
572   @Test(groups = { "Functional" })
573   public void testDeriveSequence_existingDataset()
574   {
575     Sequence sq = new Sequence("Seq1", "CD");
576     sq.setDatasetSequence(new Sequence("Seq1", "ABCDEF"));
577     sq.getDatasetSequence().addSequenceFeature(
578             new SequenceFeature("", "", 1, 2, 0f, null));
579     sq.setStart(3);
580     sq.setEnd(4);
581
582     sq.setDescription("Test sequence description..");
583     sq.setVamsasId("TestVamsasId");
584     sq.addDBRef(new DBRefEntry("PDB", "version0", "1TST"));
585
586     sq.addDBRef(new DBRefEntry("PDB", "version1", "1PDB"));
587     sq.addDBRef(new DBRefEntry("PDB", "version2", "2PDB"));
588     sq.addDBRef(new DBRefEntry("PDB", "version3", "3PDB"));
589     sq.addDBRef(new DBRefEntry("PDB", "version4", "4PDB"));
590
591     sq.addPDBId(new PDBEntry("1PDB", "A", Type.PDB, "filePath/test1"));
592     sq.addPDBId(new PDBEntry("1PDB", "B", Type.PDB, "filePath/test1"));
593     sq.addPDBId(new PDBEntry("2PDB", "A", Type.MMCIF, "filePath/test2"));
594     sq.addPDBId(new PDBEntry("2PDB", "B", Type.MMCIF, "filePath/test2"));
595
596     // these are the same as ones already added
597     DBRefEntry pdb1pdb = new DBRefEntry("PDB", "version1", "1PDB");
598     DBRefEntry pdb2pdb = new DBRefEntry("PDB", "version2", "2PDB");
599
600     List<DBRefEntry> primRefs = Arrays.asList(new DBRefEntry[] { pdb1pdb,
601         pdb2pdb });
602
603     sq.getDatasetSequence().addDBRef(pdb1pdb); // should do nothing
604     sq.getDatasetSequence().addDBRef(pdb2pdb); // should do nothing
605     sq.getDatasetSequence().addDBRef(
606             new DBRefEntry("PDB", "version3", "3PDB")); // should do nothing
607     sq.getDatasetSequence().addDBRef(
608             new DBRefEntry("PDB", "version4", "4PDB")); // should do nothing
609
610     PDBEntry pdbe1a = new PDBEntry("1PDB", "A", Type.PDB, "filePath/test1");
611     PDBEntry pdbe1b = new PDBEntry("1PDB", "B", Type.PDB, "filePath/test1");
612     PDBEntry pdbe2a = new PDBEntry("2PDB", "A", Type.MMCIF,
613             "filePath/test2");
614     PDBEntry pdbe2b = new PDBEntry("2PDB", "B", Type.MMCIF,
615             "filePath/test2");
616     sq.getDatasetSequence().addPDBId(pdbe1a);
617     sq.getDatasetSequence().addPDBId(pdbe1b);
618     sq.getDatasetSequence().addPDBId(pdbe2a);
619     sq.getDatasetSequence().addPDBId(pdbe2b);
620
621     /*
622      * test we added pdb entries to the dataset sequence
623      */
624     Assert.assertEquals(sq.getDatasetSequence().getAllPDBEntries(), Arrays
625             .asList(new PDBEntry[] { pdbe1a, pdbe1b, pdbe2a, pdbe2b }),
626             "PDB Entries were not found on dataset sequence.");
627
628     /*
629      * we should recover a pdb entry that is on the dataset sequence via PDBEntry
630      */
631     Assert.assertEquals(pdbe1a,
632             sq.getDatasetSequence().getPDBEntry("1PDB"),
633             "PDB Entry '1PDB' not found on dataset sequence via getPDBEntry.");
634     ArrayList<Annotation> annotsList = new ArrayList<Annotation>();
635     System.out.println(">>>>>> " + sq.getSequenceAsString().length());
636     annotsList.add(new Annotation("A", "A", 'X', 0.1f));
637     annotsList.add(new Annotation("A", "A", 'X', 0.1f));
638     Annotation[] annots = annotsList.toArray(new Annotation[0]);
639     sq.addAlignmentAnnotation(new AlignmentAnnotation("Test annot",
640             "Test annot description", annots));
641     sq.getDatasetSequence().addAlignmentAnnotation(
642             new AlignmentAnnotation("Test annot", "Test annot description",
643                     annots));
644     Assert.assertEquals(sq.getDescription(), "Test sequence description..");
645     Assert.assertEquals(sq.getDBRefs().length, 5); // DBRefs are on dataset
646                                                    // sequence
647     Assert.assertEquals(sq.getAllPDBEntries().size(), 4);
648     Assert.assertNotNull(sq.getAnnotation());
649     Assert.assertEquals(sq.getAnnotation()[0].annotations.length, 2);
650     Assert.assertEquals(sq.getDatasetSequence().getDBRefs().length, 5); // same
651                                                                         // as
652                                                                         // sq.getDBRefs()
653     Assert.assertEquals(sq.getDatasetSequence().getAllPDBEntries().size(),
654             4);
655     Assert.assertNotNull(sq.getDatasetSequence().getAnnotation());
656
657     Sequence derived = (Sequence) sq.deriveSequence();
658
659     Assert.assertEquals(derived.getDescription(),
660             "Test sequence description..");
661     Assert.assertEquals(derived.getDBRefs().length, 5); // come from dataset
662     Assert.assertEquals(derived.getAllPDBEntries().size(), 4);
663     Assert.assertNotNull(derived.getAnnotation());
664     Assert.assertEquals(derived.getAnnotation()[0].annotations.length, 2);
665     Assert.assertEquals(derived.getDatasetSequence().getDBRefs().length, 5);
666     Assert.assertEquals(derived.getDatasetSequence().getAllPDBEntries()
667             .size(), 4);
668     Assert.assertNotNull(derived.getDatasetSequence().getAnnotation());
669
670     assertEquals("CD", derived.getSequenceAsString());
671     assertSame(sq.getDatasetSequence(), derived.getDatasetSequence());
672
673     // derived sequence should access dataset sequence features
674     assertNotNull(sq.getSequenceFeatures());
675     assertEquals(sq.getSequenceFeatures(), derived.getSequenceFeatures());
676
677     /*
678      *  verify we have primary db refs *just* for PDB IDs with associated
679      *  PDBEntry objects
680      */
681
682     assertEquals(primRefs, sq.getPrimaryDBRefs());
683     assertEquals(primRefs, sq.getDatasetSequence().getPrimaryDBRefs());
684
685     assertEquals(sq.getPrimaryDBRefs(), derived.getPrimaryDBRefs());
686
687   }
688
689   /**
690    * Test for deriveSequence applied to an ungapped sequence with no dataset
691    */
692   @Test(groups = { "Functional" })
693   public void testDeriveSequence_noDatasetUngapped()
694   {
695     SequenceI sq = new Sequence("Seq1", "ABCDEF");
696     assertEquals(1, sq.getStart());
697     assertEquals(6, sq.getEnd());
698     SequenceI derived = sq.deriveSequence();
699     assertEquals("ABCDEF", derived.getSequenceAsString());
700     assertEquals("ABCDEF", derived.getDatasetSequence()
701             .getSequenceAsString());
702   }
703
704   /**
705    * Test for deriveSequence applied to a gapped sequence with no dataset
706    */
707   @Test(groups = { "Functional" })
708   public void testDeriveSequence_noDatasetGapped()
709   {
710     SequenceI sq = new Sequence("Seq1", "AB-C.D EF");
711     assertEquals(1, sq.getStart());
712     assertEquals(6, sq.getEnd());
713     assertNull(sq.getDatasetSequence());
714     SequenceI derived = sq.deriveSequence();
715     assertEquals("AB-C.D EF", derived.getSequenceAsString());
716     assertEquals("ABCDEF", derived.getDatasetSequence()
717             .getSequenceAsString());
718   }
719
720   @Test(groups = { "Functional" })
721   public void testCopyConstructor_noDataset()
722   {
723     SequenceI seq1 = new Sequence("Seq1", "AB-C.D EF");
724     seq1.setDescription("description");
725     seq1.addAlignmentAnnotation(new AlignmentAnnotation("label", "desc",
726             1.3d));
727     seq1.addSequenceFeature(new SequenceFeature("type", "desc", 22, 33,
728             12.4f, "group"));
729     seq1.addPDBId(new PDBEntry("1A70", "B", Type.PDB, "File"));
730     seq1.addDBRef(new DBRefEntry("EMBL", "1.2", "AZ12345"));
731
732     SequenceI copy = new Sequence(seq1);
733
734     assertNull(copy.getDatasetSequence());
735
736     verifyCopiedSequence(seq1, copy);
737
738     // copy has a copy of the DBRefEntry
739     // this is murky - DBrefs are only copied for dataset sequences
740     // where the test for 'dataset sequence' is 'dataset is null'
741     // but that doesn't distinguish it from an aligned sequence
742     // which has not yet generated a dataset sequence
743     // NB getDBRef looks inside dataset sequence if not null
744     DBRefEntry[] dbrefs = copy.getDBRefs();
745     assertEquals(1, dbrefs.length);
746     assertFalse(dbrefs[0] == seq1.getDBRefs()[0]);
747     assertTrue(dbrefs[0].equals(seq1.getDBRefs()[0]));
748   }
749
750   @Test(groups = { "Functional" })
751   public void testCopyConstructor_withDataset()
752   {
753     SequenceI seq1 = new Sequence("Seq1", "AB-C.D EF");
754     seq1.createDatasetSequence();
755     seq1.setDescription("description");
756     seq1.addAlignmentAnnotation(new AlignmentAnnotation("label", "desc",
757             1.3d));
758     // JAL-2046 - what is the contract for using a derived sequence's
759     // addSequenceFeature ?
760     seq1.addSequenceFeature(new SequenceFeature("type", "desc", 22, 33,
761             12.4f, "group"));
762     seq1.addPDBId(new PDBEntry("1A70", "B", Type.PDB, "File"));
763     // here we add DBRef to the dataset sequence:
764     seq1.getDatasetSequence().addDBRef(
765             new DBRefEntry("EMBL", "1.2", "AZ12345"));
766
767     SequenceI copy = new Sequence(seq1);
768
769     assertNotNull(copy.getDatasetSequence());
770     assertSame(copy.getDatasetSequence(), seq1.getDatasetSequence());
771
772     verifyCopiedSequence(seq1, copy);
773
774     // getDBRef looks inside dataset sequence and this is shared,
775     // so holds the same dbref objects
776     DBRefEntry[] dbrefs = copy.getDBRefs();
777     assertEquals(1, dbrefs.length);
778     assertSame(dbrefs[0], seq1.getDBRefs()[0]);
779   }
780
781   /**
782    * Helper to make assertions about a copied sequence
783    * 
784    * @param seq1
785    * @param copy
786    */
787   protected void verifyCopiedSequence(SequenceI seq1, SequenceI copy)
788   {
789     // verify basic properties:
790     assertEquals(copy.getName(), seq1.getName());
791     assertEquals(copy.getDescription(), seq1.getDescription());
792     assertEquals(copy.getStart(), seq1.getStart());
793     assertEquals(copy.getEnd(), seq1.getEnd());
794     assertEquals(copy.getSequenceAsString(), seq1.getSequenceAsString());
795
796     // copy has a copy of the annotation:
797     AlignmentAnnotation[] anns = copy.getAnnotation();
798     assertEquals(1, anns.length);
799     assertFalse(anns[0] == seq1.getAnnotation()[0]);
800     assertEquals(anns[0].label, seq1.getAnnotation()[0].label);
801     assertEquals(anns[0].description, seq1.getAnnotation()[0].description);
802     assertEquals(anns[0].score, seq1.getAnnotation()[0].score);
803
804     // copy has a copy of the sequence feature:
805     List<SequenceFeature> sfs = copy.getSequenceFeatures();
806     assertEquals(1, sfs.size());
807     if (seq1.getDatasetSequence() != null
808             && copy.getDatasetSequence() == seq1.getDatasetSequence())
809     {
810       assertSame(sfs.get(0), seq1.getSequenceFeatures().get(0));
811     }
812     else
813     {
814       assertNotSame(sfs.get(0), seq1.getSequenceFeatures().get(0));
815     }
816     assertEquals(sfs.get(0), seq1.getSequenceFeatures().get(0));
817
818     // copy has a copy of the PDB entry
819     Vector<PDBEntry> pdbs = copy.getAllPDBEntries();
820     assertEquals(1, pdbs.size());
821     assertFalse(pdbs.get(0) == seq1.getAllPDBEntries().get(0));
822     assertTrue(pdbs.get(0).equals(seq1.getAllPDBEntries().get(0)));
823   }
824
825   @Test(groups = "Functional")
826   public void testGetCharAt()
827   {
828     SequenceI sq = new Sequence("", "abcde");
829     assertEquals('a', sq.getCharAt(0));
830     assertEquals('e', sq.getCharAt(4));
831     assertEquals(' ', sq.getCharAt(5));
832     assertEquals(' ', sq.getCharAt(-1));
833   }
834
835   @Test(groups = { "Functional" })
836   public void testAddSequenceFeatures()
837   {
838     SequenceI sq = new Sequence("", "abcde");
839     // type may not be null
840     assertFalse(sq.addSequenceFeature(new SequenceFeature(null, "desc", 4,
841             8, 0f, null)));
842     assertTrue(sq.addSequenceFeature(new SequenceFeature("Cath", "desc", 4,
843             8, 0f, null)));
844     // can't add a duplicate feature
845     assertFalse(sq.addSequenceFeature(new SequenceFeature("Cath", "desc",
846             4, 8, 0f, null)));
847     // can add a different feature
848     assertTrue(sq.addSequenceFeature(new SequenceFeature("Scop", "desc", 4,
849             8, 0f, null))); // different type
850     assertTrue(sq.addSequenceFeature(new SequenceFeature("Cath",
851             "description", 4, 8, 0f, null)));// different description
852     assertTrue(sq.addSequenceFeature(new SequenceFeature("Cath", "desc", 3,
853             8, 0f, null))); // different start position
854     assertTrue(sq.addSequenceFeature(new SequenceFeature("Cath", "desc", 4,
855             9, 0f, null))); // different end position
856     assertTrue(sq.addSequenceFeature(new SequenceFeature("Cath", "desc", 4,
857             8, 1f, null))); // different score
858     assertTrue(sq.addSequenceFeature(new SequenceFeature("Cath", "desc", 4,
859             8, Float.NaN, null))); // score NaN
860     assertTrue(sq.addSequenceFeature(new SequenceFeature("Cath", "desc", 4,
861             8, 0f, "Metal"))); // different group
862     assertEquals(8, sq.getFeatures().getAllFeatures().size());
863   }
864
865   /**
866    * Tests for adding (or updating) dbrefs
867    * 
868    * @see DBRefEntry#updateFrom(DBRefEntry)
869    */
870   @Test(groups = { "Functional" })
871   public void testAddDBRef()
872   {
873     SequenceI sq = new Sequence("", "abcde");
874     assertNull(sq.getDBRefs());
875     DBRefEntry dbref = new DBRefEntry("Uniprot", "1", "P00340");
876     sq.addDBRef(dbref);
877     assertEquals(1, sq.getDBRefs().length);
878     assertSame(dbref, sq.getDBRefs()[0]);
879
880     /*
881      * change of version - new entry
882      */
883     DBRefEntry dbref2 = new DBRefEntry("Uniprot", "2", "P00340");
884     sq.addDBRef(dbref2);
885     assertEquals(2, sq.getDBRefs().length);
886     assertSame(dbref, sq.getDBRefs()[0]);
887     assertSame(dbref2, sq.getDBRefs()[1]);
888
889     /*
890      * matches existing entry - not added
891      */
892     sq.addDBRef(new DBRefEntry("UNIPROT", "1", "p00340"));
893     assertEquals(2, sq.getDBRefs().length);
894
895     /*
896      * different source = new entry
897      */
898     DBRefEntry dbref3 = new DBRefEntry("UniRef", "1", "p00340");
899     sq.addDBRef(dbref3);
900     assertEquals(3, sq.getDBRefs().length);
901     assertSame(dbref3, sq.getDBRefs()[2]);
902
903     /*
904      * different ref = new entry
905      */
906     DBRefEntry dbref4 = new DBRefEntry("UniRef", "1", "p00341");
907     sq.addDBRef(dbref4);
908     assertEquals(4, sq.getDBRefs().length);
909     assertSame(dbref4, sq.getDBRefs()[3]);
910
911     /*
912      * matching ref with a mapping - map updated
913      */
914     DBRefEntry dbref5 = new DBRefEntry("UniRef", "1", "p00341");
915     Mapping map = new Mapping(new MapList(new int[] { 1, 3 }, new int[] {
916         1, 1 }, 3, 1));
917     dbref5.setMap(map);
918     sq.addDBRef(dbref5);
919     assertEquals(4, sq.getDBRefs().length);
920     assertSame(dbref4, sq.getDBRefs()[3]);
921     assertSame(map, dbref4.getMap());
922
923     /*
924      * 'real' version replaces "0" version
925      */
926     dbref2.setVersion("0");
927     DBRefEntry dbref6 = new DBRefEntry(dbref2.getSource(), "3",
928             dbref2.getAccessionId());
929     sq.addDBRef(dbref6);
930     assertEquals(4, sq.getDBRefs().length);
931     assertSame(dbref2, sq.getDBRefs()[1]);
932     assertEquals("3", dbref2.getVersion());
933
934     /*
935      * 'real' version replaces "source:0" version
936      */
937     dbref3.setVersion("Uniprot:0");
938     DBRefEntry dbref7 = new DBRefEntry(dbref3.getSource(), "3",
939             dbref3.getAccessionId());
940     sq.addDBRef(dbref7);
941     assertEquals(4, sq.getDBRefs().length);
942     assertSame(dbref3, sq.getDBRefs()[2]);
943     assertEquals("3", dbref2.getVersion());
944   }
945
946   @Test(groups = { "Functional" })
947   public void testGetPrimaryDBRefs_peptide()
948   {
949     SequenceI sq = new Sequence("aseq", "ASDFKYLMQPRST", 10, 22);
950
951     // no dbrefs
952     List<DBRefEntry> primaryDBRefs = sq.getPrimaryDBRefs();
953     assertTrue(primaryDBRefs.isEmpty());
954
955     // empty dbrefs
956     sq.setDBRefs(new DBRefEntry[] {});
957     primaryDBRefs = sq.getPrimaryDBRefs();
958     assertTrue(primaryDBRefs.isEmpty());
959
960     // primary - uniprot
961     DBRefEntry upentry1 = new DBRefEntry("UNIPROT", "0", "Q04760");
962     sq.addDBRef(upentry1);
963
964     // primary - uniprot with congruent map
965     DBRefEntry upentry2 = new DBRefEntry("UNIPROT", "0", "Q04762");
966     upentry2.setMap(new Mapping(null, new MapList(new int[] { 10, 22 },
967             new int[] { 10, 22 }, 1, 1)));
968     sq.addDBRef(upentry2);
969
970     // primary - uniprot with map of enclosing sequence
971     DBRefEntry upentry3 = new DBRefEntry("UNIPROT", "0", "Q04763");
972     upentry3.setMap(new Mapping(null, new MapList(new int[] { 8, 24 },
973             new int[] { 8, 24 }, 1, 1)));
974     sq.addDBRef(upentry3);
975
976     // not primary - uniprot with map of sub-sequence (5')
977     DBRefEntry upentry4 = new DBRefEntry("UNIPROT", "0", "Q04764");
978     upentry4.setMap(new Mapping(null, new MapList(new int[] { 10, 18 },
979             new int[] { 10, 18 }, 1, 1)));
980     sq.addDBRef(upentry4);
981
982     // not primary - uniprot with map that overlaps 3'
983     DBRefEntry upentry5 = new DBRefEntry("UNIPROT", "0", "Q04765");
984     upentry5.setMap(new Mapping(null, new MapList(new int[] { 12, 22 },
985             new int[] { 12, 22 }, 1, 1)));
986     sq.addDBRef(upentry5);
987
988     // not primary - uniprot with map to different coordinates frame
989     DBRefEntry upentry6 = new DBRefEntry("UNIPROT", "0", "Q04766");
990     upentry6.setMap(new Mapping(null, new MapList(new int[] { 12, 18 },
991             new int[] { 112, 118 }, 1, 1)));
992     sq.addDBRef(upentry6);
993
994     // not primary - dbref to 'non-core' database
995     DBRefEntry upentry7 = new DBRefEntry("Pfam", "0", "PF00903");
996     sq.addDBRef(upentry7);
997
998     // primary - type is PDB
999     DBRefEntry pdbentry = new DBRefEntry("PDB", "0", "1qip");
1000     sq.addDBRef(pdbentry);
1001
1002     // not primary - PDBEntry has no file
1003     sq.addDBRef(new DBRefEntry("PDB", "0", "1AAA"));
1004
1005     // not primary - no PDBEntry
1006     sq.addDBRef(new DBRefEntry("PDB", "0", "1DDD"));
1007
1008     // add corroborating PDB entry for primary DBref -
1009     // needs to have a file as well as matching ID
1010     // note PDB ID is not treated as case sensitive
1011     sq.addPDBId(new PDBEntry("1QIP", null, Type.PDB, new File("/blah")
1012             .toString()));
1013
1014     // not valid DBRef - no file..
1015     sq.addPDBId(new PDBEntry("1AAA", null, null, null));
1016
1017     primaryDBRefs = sq.getPrimaryDBRefs();
1018     assertEquals(4, primaryDBRefs.size());
1019     assertTrue("Couldn't find simple primary reference (UNIPROT)",
1020             primaryDBRefs.contains(upentry1));
1021     assertTrue("Couldn't find mapped primary reference (UNIPROT)",
1022             primaryDBRefs.contains(upentry2));
1023     assertTrue("Couldn't find mapped context reference (UNIPROT)",
1024             primaryDBRefs.contains(upentry3));
1025     assertTrue("Couldn't find expected PDB primary reference",
1026             primaryDBRefs.contains(pdbentry));
1027   }
1028
1029   @Test(groups = { "Functional" })
1030   public void testGetPrimaryDBRefs_nucleotide()
1031   {
1032     SequenceI sq = new Sequence("aseq", "TGATCACTCGACTAGCATCAGCATA", 10, 34);
1033
1034     // primary - Ensembl
1035     DBRefEntry dbr1 = new DBRefEntry("ENSEMBL", "0", "ENSG1234");
1036     sq.addDBRef(dbr1);
1037
1038     // not primary - Ensembl 'transcript' mapping of sub-sequence
1039     DBRefEntry dbr2 = new DBRefEntry("ENSEMBL", "0", "ENST1234");
1040     dbr2.setMap(new Mapping(null, new MapList(new int[] { 15, 25 },
1041             new int[] { 1, 11 }, 1, 1)));
1042     sq.addDBRef(dbr2);
1043
1044     // primary - EMBL with congruent map
1045     DBRefEntry dbr3 = new DBRefEntry("EMBL", "0", "J1234");
1046     dbr3.setMap(new Mapping(null, new MapList(new int[] { 10, 34 },
1047             new int[] { 10, 34 }, 1, 1)));
1048     sq.addDBRef(dbr3);
1049
1050     // not primary - to non-core database
1051     DBRefEntry dbr4 = new DBRefEntry("CCDS", "0", "J1234");
1052     sq.addDBRef(dbr4);
1053
1054     // not primary - to protein
1055     DBRefEntry dbr5 = new DBRefEntry("UNIPROT", "0", "Q87654");
1056     sq.addDBRef(dbr5);
1057
1058     List<DBRefEntry> primaryDBRefs = sq.getPrimaryDBRefs();
1059     assertEquals(2, primaryDBRefs.size());
1060     assertTrue(primaryDBRefs.contains(dbr1));
1061     assertTrue(primaryDBRefs.contains(dbr3));
1062   }
1063
1064   /**
1065    * Test the method that updates the list of PDBEntry from any new DBRefEntry
1066    * for PDB
1067    */
1068   @Test(groups = { "Functional" })
1069   public void testUpdatePDBIds()
1070   {
1071     PDBEntry pdbe1 = new PDBEntry("3A6S", null, null, null);
1072     seq.addPDBId(pdbe1);
1073     seq.addDBRef(new DBRefEntry("Ensembl", "8", "ENST1234"));
1074     seq.addDBRef(new DBRefEntry("PDB", "0", "1A70"));
1075     seq.addDBRef(new DBRefEntry("PDB", "0", "4BQGa"));
1076     seq.addDBRef(new DBRefEntry("PDB", "0", "3a6sB"));
1077     // 7 is not a valid chain code:
1078     seq.addDBRef(new DBRefEntry("PDB", "0", "2GIS7"));
1079
1080     seq.updatePDBIds();
1081     List<PDBEntry> pdbIds = seq.getAllPDBEntries();
1082     assertEquals(4, pdbIds.size());
1083     assertSame(pdbe1, pdbIds.get(0));
1084     // chain code got added to 3A6S:
1085     assertEquals("B", pdbe1.getChainCode());
1086     assertEquals("1A70", pdbIds.get(1).getId());
1087     // 4BQGA is parsed into id + chain
1088     assertEquals("4BQG", pdbIds.get(2).getId());
1089     assertEquals("a", pdbIds.get(2).getChainCode());
1090     assertEquals("2GIS7", pdbIds.get(3).getId());
1091     assertNull(pdbIds.get(3).getChainCode());
1092   }
1093
1094   /**
1095    * Test the method that either adds a pdbid or updates an existing one
1096    */
1097   @Test(groups = { "Functional" })
1098   public void testAddPDBId()
1099   {
1100     PDBEntry pdbe = new PDBEntry("3A6S", null, null, null);
1101     seq.addPDBId(pdbe);
1102     assertEquals(1, seq.getAllPDBEntries().size());
1103     assertSame(pdbe, seq.getPDBEntry("3A6S"));
1104     assertSame(pdbe, seq.getPDBEntry("3a6s")); // case-insensitive
1105
1106     // add the same entry
1107     seq.addPDBId(pdbe);
1108     assertEquals(1, seq.getAllPDBEntries().size());
1109     assertSame(pdbe, seq.getPDBEntry("3A6S"));
1110
1111     // add an identical entry
1112     seq.addPDBId(new PDBEntry("3A6S", null, null, null));
1113     assertEquals(1, seq.getAllPDBEntries().size());
1114     assertSame(pdbe, seq.getPDBEntry("3A6S"));
1115
1116     // add a different entry
1117     PDBEntry pdbe2 = new PDBEntry("1A70", null, null, null);
1118     seq.addPDBId(pdbe2);
1119     assertEquals(2, seq.getAllPDBEntries().size());
1120     assertSame(pdbe, seq.getAllPDBEntries().get(0));
1121     assertSame(pdbe2, seq.getAllPDBEntries().get(1));
1122
1123     // update pdbe with chain code, file, type
1124     PDBEntry pdbe3 = new PDBEntry("3a6s", "A", Type.PDB, "filepath");
1125     seq.addPDBId(pdbe3);
1126     assertEquals(2, seq.getAllPDBEntries().size());
1127     assertSame(pdbe, seq.getAllPDBEntries().get(0)); // updated in situ
1128     assertEquals("3A6S", pdbe.getId()); // unchanged
1129     assertEquals("A", pdbe.getChainCode()); // updated
1130     assertEquals(Type.PDB.toString(), pdbe.getType()); // updated
1131     assertEquals("filepath", pdbe.getFile()); // updated
1132     assertSame(pdbe2, seq.getAllPDBEntries().get(1));
1133
1134     // add with a different file path
1135     PDBEntry pdbe4 = new PDBEntry("3a6s", "A", Type.PDB, "filepath2");
1136     seq.addPDBId(pdbe4);
1137     assertEquals(3, seq.getAllPDBEntries().size());
1138     assertSame(pdbe4, seq.getAllPDBEntries().get(2));
1139
1140     // add with a different chain code
1141     PDBEntry pdbe5 = new PDBEntry("3a6s", "B", Type.PDB, "filepath");
1142     seq.addPDBId(pdbe5);
1143     assertEquals(4, seq.getAllPDBEntries().size());
1144     assertSame(pdbe5, seq.getAllPDBEntries().get(3));
1145   }
1146
1147   @Test(
1148     groups = { "Functional" },
1149     expectedExceptions = { IllegalArgumentException.class })
1150   public void testSetDatasetSequence_toSelf()
1151   {
1152     seq.setDatasetSequence(seq);
1153   }
1154
1155   @Test(
1156     groups = { "Functional" },
1157     expectedExceptions = { IllegalArgumentException.class })
1158   public void testSetDatasetSequence_cascading()
1159   {
1160     SequenceI seq2 = new Sequence("Seq2", "xyz");
1161     seq2.createDatasetSequence();
1162     seq.setDatasetSequence(seq2);
1163   }
1164
1165   @Test
1166   public void testFindPositions()
1167   {
1168     SequenceI sq = new Sequence("Seq", "ABC--DE-F", 8, 13);
1169
1170     Range range = sq.findPositions(1, 4); // BC
1171     assertEquals(new Range(9, 10), range);
1172
1173     range = sq.findPositions(2, 4); // C
1174     assertEquals(new Range(10, 10), range);
1175
1176     assertNull(sq.findPositions(3, 4)); // all gaps
1177
1178     range = sq.findPositions(2, 6); // CDE
1179     assertEquals(new Range(10, 12), range);
1180
1181     range = sq.findPositions(3, 7); // DE
1182     assertEquals(new Range(11, 12), range);
1183   }
1184 }