JAL-3438 spotless for 2.11.2.0
[jalview.git] / test / jalview / datamodel / AlignedCodonFrameTest.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 java.util.Arrays;
32 import java.util.List;
33
34 import org.testng.annotations.BeforeClass;
35 import org.testng.annotations.Test;
36
37 import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
38 import jalview.gui.JvOptionPane;
39 import jalview.util.MapList;
40
41 public class AlignedCodonFrameTest
42 {
43
44   @BeforeClass(alwaysRun = true)
45   public void setUpJvOptionPane()
46   {
47     JvOptionPane.setInteractiveMode(false);
48     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
49   }
50
51   /**
52    * Test the method that locates the first aligned sequence that has a mapping.
53    */
54   @Test(groups = { "Functional" })
55   public void testFindAlignedSequence()
56   {
57     AlignmentI cdna = new Alignment(new SequenceI[] {});
58     final Sequence seq1 = new Sequence("Seq1", "C-G-TA-GC");
59     seq1.createDatasetSequence();
60     cdna.addSequence(seq1);
61     final Sequence seq2 = new Sequence("Seq2", "-TA-GG-GG");
62     seq2.createDatasetSequence();
63     cdna.addSequence(seq2);
64
65     AlignmentI aa = new Alignment(new SequenceI[] {});
66     final Sequence aseq1 = new Sequence("Seq1", "-P-R");
67     aseq1.createDatasetSequence();
68     aa.addSequence(aseq1);
69     final Sequence aseq2 = new Sequence("Seq2", "-LY-");
70     aseq2.createDatasetSequence();
71     aa.addSequence(aseq2);
72
73     /*
74      * Mapping from first DNA sequence to second AA sequence.
75      */
76     AlignedCodonFrame acf = new AlignedCodonFrame();
77
78     assertNull(acf.findAlignedSequence(seq1, aa));
79
80     MapList map = new MapList(new int[] { 1, 6 }, new int[] { 1, 2 }, 3, 1);
81     acf.addMap(seq1.getDatasetSequence(), aseq2.getDatasetSequence(), map);
82
83     /*
84      * DNA seq1 maps to AA seq2
85      */
86     assertEquals(aa.getSequenceAt(1), acf.findAlignedSequence(
87             cdna.getSequenceAt(0).getDatasetSequence(), aa));
88     // can also find this from the dna aligned sequence
89     assertEquals(aa.getSequenceAt(1),
90             acf.findAlignedSequence(cdna.getSequenceAt(0), aa));
91
92     assertEquals(cdna.getSequenceAt(0), acf.findAlignedSequence(
93             aa.getSequenceAt(1).getDatasetSequence(), cdna));
94   }
95
96   /**
97    * Test the method that locates the mapped codon for a protein position.
98    */
99   @Test(groups = { "Functional" })
100   public void testGetMappedRegion()
101   {
102     // introns lower case, exons upper case
103     final Sequence dna1 = new Sequence("Seq1/10-18", "c-G-TA-gC-gT-T");
104     dna1.createDatasetSequence();
105     final Sequence dna2 = new Sequence("Seq2/20-28", "-TA-gG-Gg-CG-a");
106     dna2.createDatasetSequence();
107
108     final Sequence pep1 = new Sequence("Seq1/3-4", "-P-R");
109     pep1.createDatasetSequence();
110     final Sequence pep2 = new Sequence("Seq2/7-9", "-LY-Q");
111     pep2.createDatasetSequence();
112
113     /*
114      * First with no mappings
115      */
116     AlignedCodonFrame acf = new AlignedCodonFrame();
117
118     assertNull(acf.getMappedRegion(dna1, pep1, 3));
119
120     /*
121      * Set up the mappings for the exons (upper-case bases)
122      * Note residue Q is unmapped
123      */
124     MapList map1 = new MapList(new int[] { 11, 13, 15, 15, 17, 18 },
125             new int[]
126             { 3, 4 }, 3, 1);
127     acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map1);
128     MapList map2 = new MapList(new int[] { 20, 21, 23, 24, 26, 27 },
129             new int[]
130             { 7, 9 }, 3, 1);
131     acf.addMap(dna2.getDatasetSequence(), pep2.getDatasetSequence(), map2);
132
133     /*
134      * get codon positions for peptide position
135      */
136     assertArrayEquals(new int[] { 11, 13 },
137             acf.getMappedRegion(dna1, pep1, 3));
138     assertArrayEquals(new int[] { 15, 15, 17, 18 },
139             acf.getMappedRegion(dna1, pep1, 4));
140     assertArrayEquals(new int[] { 20, 21, 23, 23 },
141             acf.getMappedRegion(dna2, pep2, 7));
142     assertArrayEquals(new int[] { 24, 24, 26, 27 },
143             acf.getMappedRegion(dna2, pep2, 8));
144
145     /*
146      * No mapping from dna2 to Q
147      */
148     assertNull(acf.getMappedRegion(dna2, pep2, 9));
149
150     /*
151      * No mapping from dna1 to pep2
152      */
153     assertNull(acf.getMappedRegion(dna1, pep2, 7));
154
155     /*
156      * get peptide position for codon position
157      */
158     assertArrayEquals(new int[] { 3, 3 },
159             acf.getMappedRegion(pep1, dna1, 11));
160     assertArrayEquals(new int[] { 3, 3 },
161             acf.getMappedRegion(pep1, dna1, 12));
162     assertArrayEquals(new int[] { 3, 3 },
163             acf.getMappedRegion(pep1, dna1, 13));
164     assertNull(acf.getMappedRegion(pep1, dna1, 14)); // intron base, not mapped
165
166   }
167
168   @Test(groups = { "Functional" })
169   public void testGetMappedCodons()
170   {
171     final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T");
172     seq1.createDatasetSequence();
173     final Sequence aseq1 = new Sequence("Seq1", "-V-L");
174     aseq1.createDatasetSequence();
175
176     /*
177      * First with no mappings
178      */
179     AlignedCodonFrame acf = new AlignedCodonFrame();
180
181     assertNull(acf.getMappedCodons(seq1.getDatasetSequence(), 0));
182
183     /*
184      * Set up the mappings for the exons (upper-case bases)
185      */
186     MapList map = new MapList(new int[] { 2, 4, 6, 6, 8, 9 },
187             new int[]
188             { 1, 2 }, 3, 1);
189     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
190
191     assertEquals(1,
192             acf.getMappedCodons(aseq1.getDatasetSequence(), 1).size());
193     assertEquals("[G, T, A]", Arrays.toString(
194             acf.getMappedCodons(aseq1.getDatasetSequence(), 1).get(0)));
195     assertEquals("[C, T, T]", Arrays.toString(
196             acf.getMappedCodons(aseq1.getDatasetSequence(), 2).get(0)));
197   }
198
199   /**
200    * Test for the case where there is more than one variant of the DNA mapping
201    * to a protein sequence
202    */
203   @Test(groups = { "Functional" })
204   public void testGetMappedCodons_dnaVariants()
205   {
206     final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T");
207     seq1.createDatasetSequence();
208     final Sequence seq2 = new Sequence("Seq2", "c-G-TT-gT-gT-A");
209     seq2.createDatasetSequence();
210     final Sequence aseq1 = new Sequence("Seq1", "-V-L");
211     aseq1.createDatasetSequence();
212
213     AlignedCodonFrame acf = new AlignedCodonFrame();
214
215     /*
216      * Set up the mappings for the exons (upper-case bases)
217      */
218     MapList map = new MapList(new int[] { 2, 4, 6, 6, 8, 9 },
219             new int[]
220             { 1, 2 }, 3, 1);
221     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
222     acf.addMap(seq2.getDatasetSequence(), aseq1.getDatasetSequence(), map);
223
224     assertEquals(2,
225             acf.getMappedCodons(aseq1.getDatasetSequence(), 1).size());
226     List<char[]> codonsForV = acf
227             .getMappedCodons(aseq1.getDatasetSequence(), 1);
228     assertEquals("[G, T, A]", Arrays.toString(codonsForV.get(0)));
229     assertEquals("[G, T, T]", Arrays.toString(codonsForV.get(1)));
230     List<char[]> codonsForL = acf
231             .getMappedCodons(aseq1.getDatasetSequence(), 2);
232     assertEquals("[C, T, T]", Arrays.toString(codonsForL.get(0)));
233     assertEquals("[T, T, A]", Arrays.toString(codonsForL.get(1)));
234   }
235
236   /**
237    * Test for the case where sequences have start > 1
238    */
239   @Test(groups = { "Functional" })
240   public void testGetMappedCodons_forSubSequences()
241   {
242     final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T", 27, 35);
243     seq1.createDatasetSequence();
244
245     final Sequence aseq1 = new Sequence("Seq1", "-V-L", 12, 13);
246     aseq1.createDatasetSequence();
247
248     /*
249      * Set up the mappings for the exons (upper-case bases)
250      */
251     AlignedCodonFrame acf = new AlignedCodonFrame();
252     MapList map = new MapList(new int[] { 28, 30, 32, 32, 34, 35 },
253             new int[]
254             { 12, 13 }, 3, 1);
255     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
256
257     assertEquals("[G, T, A]", Arrays.toString(
258             acf.getMappedCodons(aseq1.getDatasetSequence(), 12).get(0)));
259     assertEquals("[C, T, T]", Arrays.toString(
260             acf.getMappedCodons(aseq1.getDatasetSequence(), 13).get(0)));
261   }
262
263   @Test(groups = { "Functional" })
264   public void testCouldReplaceSequence()
265   {
266     SequenceI seq1 = new Sequence("Seq1/10-21", "aaacccgggttt");
267     SequenceI seq1proxy = new SequenceDummy("Seq1");
268
269     // map to region within sequence is ok
270     assertTrue(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 12,
271             17));
272     // map to region overlapping sequence is ok
273     assertTrue(
274             AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 5, 10));
275     assertTrue(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 21,
276             26));
277     // map to region before sequence is not ok
278     assertFalse(
279             AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 4, 9));
280     // map to region after sequence is not ok
281     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 22,
282             27));
283
284     /*
285      * test should fail if name doesn't match
286      */
287     seq1proxy.setName("Seq1a");
288     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 12,
289             17));
290     seq1proxy.setName("Seq1");
291     seq1.setName("Seq1a");
292     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 12,
293             17));
294
295     /*
296      * a dummy sequence can't replace a real one
297      */
298     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1, seq1proxy, 12,
299             17));
300
301     /*
302      * a dummy sequence can't replace a dummy sequence
303      */
304     SequenceI seq1proxy2 = new SequenceDummy("Seq1");
305     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1proxy,
306             seq1proxy2, 12, 17));
307
308     /*
309      * a real sequence can't replace a real one
310      */
311     SequenceI seq1a = new Sequence("Seq1/10-21", "aaacccgggttt");
312     assertFalse(
313             AlignedCodonFrame.couldRealiseSequence(seq1, seq1a, 12, 17));
314   }
315
316   /**
317    * Tests for the method that tests whether any mapping to a dummy sequence can
318    * be 'realised' to a given real sequence
319    */
320   @Test(groups = { "Functional" })
321   public void testIsRealisableWith()
322   {
323     SequenceI seq1 = new Sequence("Seq1", "tttaaaCCCGGGtttaaa");
324     SequenceI seq2 = new Sequence("Seq2", "PG");
325     SequenceI seq1proxy = new SequenceDummy("Seq1");
326     seq1.createDatasetSequence();
327     seq2.createDatasetSequence();
328     MapList mapList = new MapList(new int[] { 7, 12 }, new int[] { 2, 3 },
329             3, 1);
330     AlignedCodonFrame acf = new AlignedCodonFrame();
331     acf.addMap(seq1proxy, seq2, mapList);
332
333     /*
334      * Seq2 is mapped to SequenceDummy seq1proxy bases 4-9
335      * This is 'realisable' from real sequence Seq1
336      */
337     assertTrue(acf.isRealisableWith(seq1));
338
339     /*
340      * test should fail if name doesn't match
341      */
342     seq1proxy.setName("Seq1a");
343     assertFalse(acf.isRealisableWith(seq1));
344     seq1proxy.setName("Seq1");
345
346     SequenceI seq1ds = seq1.getDatasetSequence();
347     seq1ds.setName("Seq1a");
348     assertFalse(acf.isRealisableWith(seq1));
349     seq1ds.setName("Seq1");
350
351     /*
352      * test should fail if no sequence overlap with mapping of bases 7-12
353      * use artificial start/end values to test this
354      */
355     seq1ds.setStart(1);
356     seq1ds.setEnd(6);
357     // seq1 precedes mapped region:
358     assertFalse(acf.isRealisableWith(seq1));
359     seq1ds.setEnd(7);
360     // seq1 includes first mapped base:
361     assertTrue(acf.isRealisableWith(seq1));
362     seq1ds.setStart(13);
363     seq1ds.setEnd(18);
364     // seq1 follows mapped region:
365     assertFalse(acf.isRealisableWith(seq1));
366     seq1ds.setStart(12);
367     // seq1 includes last mapped base:
368     assertTrue(acf.isRealisableWith(seq1));
369   }
370
371   /**
372    * Tests for the method that converts mappings to a dummy sequence to mappings
373    * to a compatible real sequence
374    */
375   @Test(groups = { "Functional" })
376   public void testRealiseWith()
377   {
378     SequenceI seq1 = new Sequence("Seq1", "tttCAACCCGGGtttaaa");
379     SequenceI seq2 = new Sequence("Seq2", "QPG");
380     SequenceI seq2a = new Sequence("Seq2a", "QPG");
381     SequenceI seq1proxy = new SequenceDummy("Seq1");
382     seq1.createDatasetSequence();
383     seq2.createDatasetSequence();
384     seq2a.createDatasetSequence();
385
386     /*
387      * Make mappings from Seq2 and Seq2a peptides to dummy sequence Seq1
388      */
389     AlignedCodonFrame acf = new AlignedCodonFrame();
390
391     // map PG to codons 7-12 (CCCGGG)
392     MapList mapping1 = new MapList(new int[] { 7, 12 }, new int[] { 2, 3 },
393             3, 1);
394     acf.addMap(seq1proxy, seq2, mapping1);
395     acf.addMap(seq1proxy, seq2a, mapping1);
396
397     // map QP to codons 4-9 (CAACCC)
398     MapList mapping2 = new MapList(new int[] { 4, 9 }, new int[] { 1, 2 },
399             3, 1);
400     acf.addMap(seq1proxy, seq2, mapping2);
401     acf.addMap(seq1proxy, seq2a, mapping2);
402
403     /*
404      * acf now has two mappings one from Seq1 to Seq2, one from Seq1 to Seq2a
405      */
406     assertEquals(2, acf.getdnaSeqs().length);
407     assertSame(seq1proxy, acf.getdnaSeqs()[0]);
408     assertSame(seq1proxy, acf.getdnaSeqs()[1]);
409     assertEquals(2, acf.getProtMappings().length);
410
411     // 'realise' these mappings with the compatible sequence seq1
412     // two mappings should be updated:
413     assertEquals(2, acf.realiseWith(seq1));
414     assertSame(seq1.getDatasetSequence(), acf.getdnaSeqs()[0]);
415     assertSame(seq1.getDatasetSequence(), acf.getdnaSeqs()[1]);
416   }
417
418   /**
419    * Test the method that locates the mapped codon for a protein position.
420    */
421   @Test(groups = { "Functional" })
422   public void testGetMappedRegion_eitherWay()
423   {
424     final Sequence seq1 = new Sequence("Seq1", "AAACCCGGGTTT");
425     seq1.createDatasetSequence();
426     final Sequence seq2 = new Sequence("Seq2", "KPGF");
427     seq2.createDatasetSequence();
428     final Sequence seq3 = new Sequence("Seq3", "QYKPGFSW");
429     seq3.createDatasetSequence();
430
431     /*
432      * map Seq1 to all of Seq2 and part of Seq3
433      */
434     AlignedCodonFrame acf = new AlignedCodonFrame();
435     MapList map = new MapList(new int[] { 1, 12 }, new int[] { 1, 4 }, 3,
436             1);
437     acf.addMap(seq1.getDatasetSequence(), seq2.getDatasetSequence(), map);
438     map = new MapList(new int[] { 1, 12 }, new int[] { 3, 6 }, 3, 1);
439     acf.addMap(seq1.getDatasetSequence(), seq3.getDatasetSequence(), map);
440
441     /*
442      * map part of Seq3 to Seq2
443      */
444     map = new MapList(new int[] { 3, 6 }, new int[] { 1, 4 }, 1, 1);
445     acf.addMap(seq3.getDatasetSequence(), seq2.getDatasetSequence(), map);
446
447     /*
448      * original case - locate mapped codon for protein position
449      */
450     assertArrayEquals(new int[] { 4, 6 },
451             acf.getMappedRegion(seq1, seq2, 2));
452     assertArrayEquals(new int[] { 7, 9 },
453             acf.getMappedRegion(seq1, seq3, 5));
454     assertNull(acf.getMappedRegion(seq1, seq3, 1));
455
456     /*
457      * locate mapped protein for protein position
458      */
459     assertArrayEquals(new int[] { 4, 4 },
460             acf.getMappedRegion(seq3, seq2, 2));
461
462     /*
463      * reverse location protein-to-protein
464      */
465     assertArrayEquals(new int[] { 2, 2 },
466             acf.getMappedRegion(seq2, seq3, 4));
467
468     /*
469      * reverse location protein-from-nucleotide
470      * any of codon [4, 5, 6] positions map to seq2/2
471      */
472     assertArrayEquals(new int[] { 2, 2 },
473             acf.getMappedRegion(seq2, seq1, 4));
474     assertArrayEquals(new int[] { 2, 2 },
475             acf.getMappedRegion(seq2, seq1, 5));
476     assertArrayEquals(new int[] { 2, 2 },
477             acf.getMappedRegion(seq2, seq1, 6));
478   }
479
480   /**
481    * Tests for addMap. See also tests for MapList.addMapList
482    */
483   @Test(groups = { "Functional" })
484   public void testAddMap()
485   {
486     final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T");
487     seq1.createDatasetSequence();
488     final Sequence aseq1 = new Sequence("Seq1", "-V-L");
489     aseq1.createDatasetSequence();
490
491     AlignedCodonFrame acf = new AlignedCodonFrame();
492     MapList map = new MapList(new int[] { 2, 4, 6, 6, 8, 9 },
493             new int[]
494             { 1, 2 }, 3, 1);
495     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
496     assertEquals(1, acf.getMappingsFromSequence(seq1).size());
497     Mapping before = acf.getMappingsFromSequence(seq1).get(0);
498
499     /*
500      * add the same map again, verify it doesn't get duplicated
501      */
502     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
503     assertEquals(1, acf.getMappingsFromSequence(seq1).size());
504     assertSame(before, acf.getMappingsFromSequence(seq1).get(0));
505   }
506
507   @Test(groups = { "Functional" })
508   public void testGetCoveringMapping()
509   {
510     SequenceI dna = new Sequence("dna", "acttcaATGGCGGACtaattt");
511     SequenceI cds = new Sequence("cds/7-15", "ATGGCGGAC");
512     cds.setDatasetSequence(dna);
513     SequenceI pep = new Sequence("pep", "MAD");
514
515     /*
516      * with null argument or no mappings
517      */
518     AlignedCodonFrame acf = new AlignedCodonFrame();
519     assertNull(acf.getCoveringMapping(null, null));
520     assertNull(acf.getCoveringMapping(dna, null));
521     assertNull(acf.getCoveringMapping(null, pep));
522     assertNull(acf.getCoveringMapping(dna, pep));
523
524     /*
525      * with a non-covering mapping e.g. overlapping exon
526      */
527     MapList map = new MapList(new int[] { 7, 9 }, new int[] { 1, 1 }, 3, 1);
528     acf.addMap(dna, pep, map);
529     assertNull(acf.getCoveringMapping(dna, pep));
530
531     acf = new AlignedCodonFrame();
532     MapList map2 = new MapList(new int[] { 13, 18 }, new int[] { 2, 2 }, 3,
533             1);
534     acf.addMap(dna, pep, map2);
535     assertNull(acf.getCoveringMapping(dna, pep));
536
537     /*
538      * with a covering mapping from CDS (dataset) to protein
539      */
540     acf = new AlignedCodonFrame();
541     MapList map3 = new MapList(new int[] { 7, 15 }, new int[] { 1, 3 }, 3,
542             1);
543     acf.addMap(dna, pep, map3);
544     assertNull(acf.getCoveringMapping(dna, pep));
545     SequenceToSequenceMapping mapping = acf.getCoveringMapping(cds, pep);
546     assertNotNull(mapping);
547
548     /*
549      * with a mapping that extends to stop codon
550      */
551     acf = new AlignedCodonFrame();
552     MapList map4 = new MapList(new int[] { 7, 18 }, new int[] { 1, 3 }, 3,
553             1);
554     acf.addMap(dna, pep, map4);
555     assertNull(acf.getCoveringMapping(dna, pep));
556     assertNull(acf.getCoveringMapping(cds, pep));
557     SequenceI cds2 = new Sequence("cds/7-18", "ATGGCGGACtaa");
558     cds2.setDatasetSequence(dna);
559     mapping = acf.getCoveringMapping(cds2, pep);
560     assertNotNull(mapping);
561   }
562
563   /**
564    * Test the method that adds mapped positions to SearchResults
565    */
566   @Test(groups = { "Functional" })
567   public void testMarkMappedRegion()
568   {
569     // introns lower case, exons upper case
570     final Sequence dna1 = new Sequence("Seq1/10-18", "c-G-TA-gC-gT-T");
571     dna1.createDatasetSequence();
572     final Sequence dna2 = new Sequence("Seq2/20-28", "-TA-gG-Gg-CG-a");
573     dna2.createDatasetSequence();
574
575     final Sequence pep1 = new Sequence("Seq1/3-4", "-P-R");
576     pep1.createDatasetSequence();
577     final Sequence pep2 = new Sequence("Seq2/7-9", "-LY-Q");
578     pep2.createDatasetSequence();
579
580     /*
581      * First with no mappings
582      */
583     AlignedCodonFrame acf = new AlignedCodonFrame();
584     SearchResults sr = new SearchResults();
585     acf.markMappedRegion(dna1, 12, sr);
586     assertTrue(sr.isEmpty());
587
588     /*
589      * Set up the mappings for the exons (upper-case bases)
590      * Note residue Q is unmapped
591      */
592     MapList map1 = new MapList(new int[] { 11, 13, 15, 15, 17, 18 },
593             new int[]
594             { 3, 4 }, 3, 1);
595     acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map1);
596     MapList map2 = new MapList(new int[] { 20, 21, 23, 24, 26, 27 },
597             new int[]
598             { 7, 8 }, 3, 1);
599     acf.addMap(dna2.getDatasetSequence(), pep2.getDatasetSequence(), map2);
600
601     /*
602      * intron bases are not mapped
603      */
604     acf.markMappedRegion(dna1, 10, sr);
605     assertTrue(sr.isEmpty());
606
607     /*
608      * Q is not mapped
609      */
610     acf.markMappedRegion(pep2, 9, sr);
611     assertTrue(sr.isEmpty());
612
613     /*
614      * mark peptide position for exon position (of aligned sequence)
615      */
616     acf.markMappedRegion(dna1, 11, sr);
617     SearchResults expected = new SearchResults();
618     expected.addResult(pep1.getDatasetSequence(), 3, 3);
619     assertEquals(sr, expected);
620
621     /*
622      * mark peptide position for exon position of dataset sequence - same result
623      */
624     sr = new SearchResults();
625     acf.markMappedRegion(dna1.getDatasetSequence(), 11, sr);
626     assertEquals(sr, expected);
627
628     /*
629      * marking the same position a second time should not create a duplicate match
630      */
631     acf.markMappedRegion(dna1.getDatasetSequence(), 12, sr);
632     assertEquals(sr, expected);
633
634     /*
635      * mark exon positions for peptide position (of aligned sequence)
636      */
637     sr = new SearchResults();
638     acf.markMappedRegion(pep2, 7, sr); // codon positions 20, 21, 23
639     expected = new SearchResults();
640     expected.addResult(dna2.getDatasetSequence(), 20, 21);
641     expected.addResult(dna2.getDatasetSequence(), 23, 23);
642     assertEquals(sr, expected);
643
644     /*
645      * add another codon to the same SearchResults
646      */
647     acf.markMappedRegion(pep1.getDatasetSequence(), 4, sr); // codon positions
648                                                             // 15, 17, 18
649     expected.addResult(dna1.getDatasetSequence(), 15, 15);
650     expected.addResult(dna1.getDatasetSequence(), 17, 18);
651     assertEquals(sr, expected);
652   }
653
654   @Test(groups = { "Functional" })
655   public void testGetCoveringCodonMapping()
656   {
657     SequenceI dna = new Sequence("dna/10-30", "acttcaATGGCGGACtaattt");
658     // CDS sequence with its own dataset sequence (JAL-3763)
659     SequenceI cds = new Sequence("cds/1-9", "-A--TGGC-GGAC");
660     cds.createDatasetSequence();
661     SequenceI pep = new Sequence("pep/1-3", "MAD");
662
663     /*
664      * with null argument or no mappings
665      */
666     AlignedCodonFrame acf = new AlignedCodonFrame();
667     assertNull(acf.getCoveringCodonMapping(null));
668     assertNull(acf.getCoveringCodonMapping(dna));
669     assertNull(acf.getCoveringCodonMapping(pep));
670
671     /*
672      * with a non-covering mapping e.g. overlapping exon
673      */
674     MapList map = new MapList(new int[] { 16, 18 }, new int[] { 1, 1 }, 3,
675             1);
676     acf.addMap(dna, pep, map);
677     assertNull(acf.getCoveringCodonMapping(dna));
678     assertNull(acf.getCoveringCodonMapping(pep));
679
680     acf = new AlignedCodonFrame();
681     MapList map2 = new MapList(new int[] { 13, 18 }, new int[] { 2, 2 }, 3,
682             1);
683     acf.addMap(dna, pep, map2);
684     assertNull(acf.getCoveringCodonMapping(dna));
685     assertNull(acf.getCoveringCodonMapping(pep));
686
687     /*
688      * with a covering mapping from CDS (dataset) to protein
689      */
690     acf = new AlignedCodonFrame();
691     MapList map3 = new MapList(new int[] { 1, 9 }, new int[] { 1, 3 }, 3,
692             1);
693     acf.addMap(cds.getDatasetSequence(), pep, map3);
694     assertNull(acf.getCoveringCodonMapping(dna));
695     SequenceToSequenceMapping mapping = acf.getCoveringCodonMapping(pep);
696     assertNotNull(mapping);
697     SequenceToSequenceMapping mapping2 = acf
698             .getCoveringCodonMapping(cds.getDatasetSequence());
699     assertSame(mapping, mapping2);
700
701     /*
702      * with a mapping that extends to stop codon
703      * (EMBL CDS location often includes the stop codon)
704      * - getCoveringCodonMapping is lenient (doesn't require exact length match)
705      */
706     SequenceI cds2 = new Sequence("cds/1-12", "-A--TGGC-GGACTAA");
707     cds2.createDatasetSequence();
708     acf = new AlignedCodonFrame();
709     MapList map4 = new MapList(new int[] { 1, 12 }, new int[] { 1, 3 }, 3,
710             1);
711     acf.addMap(cds2, pep, map4);
712     mapping = acf.getCoveringCodonMapping(cds2.getDatasetSequence());
713     assertNotNull(mapping);
714     mapping2 = acf.getCoveringCodonMapping(pep);
715     assertSame(mapping, mapping2);
716   }
717 }