JAL-3700 additions and corrections to unit tests
[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(cdna
87             .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(aa
93             .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 seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T");
104     seq1.createDatasetSequence();
105     final Sequence seq2 = new Sequence("Seq2", "-TA-gG-Gg-CG-a");
106     seq2.createDatasetSequence();
107
108     final Sequence aseq1 = new Sequence("Seq1", "-P-R");
109     aseq1.createDatasetSequence();
110     final Sequence aseq2 = new Sequence("Seq2", "-LY-Q");
111     aseq2.createDatasetSequence();
112
113     /*
114      * First with no mappings
115      */
116     AlignedCodonFrame acf = new AlignedCodonFrame();
117
118     assertNull(acf.getMappedRegion(seq1, aseq1, 1));
119
120     /*
121      * Set up the mappings for the exons (upper-case bases)
122      * Note residue Q is unmapped
123      */
124     MapList map = new MapList(new int[] { 2, 4, 6, 6, 8, 9 }, new int[] {
125         1, 2 }, 3, 1);
126     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
127     map = new MapList(new int[] { 1, 2, 4, 5, 7, 8 }, new int[] { 1, 2 },
128             3, 1);
129     acf.addMap(seq2.getDatasetSequence(), aseq2.getDatasetSequence(), map);
130
131     assertArrayEquals(new int[] { 2, 4 },
132             acf.getMappedRegion(seq1, aseq1, 1));
133     assertArrayEquals(new int[] { 6, 6, 8, 9 },
134             acf.getMappedRegion(seq1, aseq1, 2));
135     assertArrayEquals(new int[] { 1, 2, 4, 4 },
136             acf.getMappedRegion(seq2, aseq2, 1));
137     assertArrayEquals(new int[] { 5, 5, 7, 8 },
138             acf.getMappedRegion(seq2, aseq2, 2));
139
140     /*
141      * No mapping from seq2 to Q
142      */
143     assertNull(acf.getMappedRegion(seq2, aseq2, 3));
144
145     /*
146      * No mapping from sequence 1 to sequence 2
147      */
148     assertNull(acf.getMappedRegion(seq1, aseq2, 1));
149   }
150
151   @Test(groups = { "Functional" })
152   public void testGetMappedCodons()
153   {
154     final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T");
155     seq1.createDatasetSequence();
156     final Sequence aseq1 = new Sequence("Seq1", "-V-L");
157     aseq1.createDatasetSequence();
158
159     /*
160      * First with no mappings
161      */
162     AlignedCodonFrame acf = new AlignedCodonFrame();
163
164     assertNull(acf.getMappedCodons(seq1.getDatasetSequence(), 0));
165
166     /*
167      * Set up the mappings for the exons (upper-case bases)
168      */
169     MapList map = new MapList(new int[] { 2, 4, 6, 6, 8, 9 }, new int[] {
170         1, 2 }, 3, 1);
171     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
172
173     assertEquals(1, acf.getMappedCodons(aseq1.getDatasetSequence(), 1)
174             .size());
175     assertEquals(
176             "[G, T, A]",
177             Arrays.toString(acf.getMappedCodons(aseq1.getDatasetSequence(),
178                     1).get(0)));
179     assertEquals(
180             "[C, T, T]",
181             Arrays.toString(acf.getMappedCodons(aseq1.getDatasetSequence(),
182                     2).get(0)));
183   }
184
185   /**
186    * Test for the case where there is more than one variant of the DNA mapping
187    * to a protein sequence
188    */
189   @Test(groups = { "Functional" })
190   public void testGetMappedCodons_dnaVariants()
191   {
192     final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T");
193     seq1.createDatasetSequence();
194     final Sequence seq2 = new Sequence("Seq2", "c-G-TT-gT-gT-A");
195     seq2.createDatasetSequence();
196     final Sequence aseq1 = new Sequence("Seq1", "-V-L");
197     aseq1.createDatasetSequence();
198
199     AlignedCodonFrame acf = new AlignedCodonFrame();
200
201     /*
202      * Set up the mappings for the exons (upper-case bases)
203      */
204     MapList map = new MapList(new int[] { 2, 4, 6, 6, 8, 9 }, new int[] {
205         1, 2 }, 3, 1);
206     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
207     acf.addMap(seq2.getDatasetSequence(), aseq1.getDatasetSequence(), map);
208
209     assertEquals(2, acf.getMappedCodons(aseq1.getDatasetSequence(), 1)
210             .size());
211     List<char[]> codonsForV = acf.getMappedCodons(
212             aseq1.getDatasetSequence(), 1);
213     assertEquals("[G, T, A]", Arrays.toString(codonsForV.get(0)));
214     assertEquals("[G, T, T]", Arrays.toString(codonsForV.get(1)));
215     List<char[]> codonsForL = acf.getMappedCodons(
216             aseq1.getDatasetSequence(), 2);
217     assertEquals("[C, T, T]", Arrays.toString(codonsForL.get(0)));
218     assertEquals("[T, T, A]", Arrays.toString(codonsForL.get(1)));
219   }
220
221   /**
222    * Test for the case where sequences have start > 1
223    */
224   @Test(groups = { "Functional" })
225   public void testGetMappedCodons_forSubSequences()
226   {
227     final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T", 27, 35);
228     seq1.createDatasetSequence();
229
230     final Sequence aseq1 = new Sequence("Seq1", "-V-L", 12, 13);
231     aseq1.createDatasetSequence();
232
233     /*
234      * Set up the mappings for the exons (upper-case bases)
235      */
236     AlignedCodonFrame acf = new AlignedCodonFrame();
237     MapList map = new MapList(new int[] { 28, 30, 32, 32, 34, 35 },
238             new int[] { 12, 13 }, 3, 1);
239     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
240
241     assertEquals(
242             "[G, T, A]",
243             Arrays.toString(acf.getMappedCodons(aseq1.getDatasetSequence(),
244                     12).get(0)));
245     assertEquals(
246             "[C, T, T]",
247             Arrays.toString(acf.getMappedCodons(aseq1.getDatasetSequence(),
248                     13).get(0)));
249   }
250
251   @Test(groups = { "Functional" })
252   public void testCouldReplaceSequence()
253   {
254     SequenceI seq1 = new Sequence("Seq1/10-21", "aaacccgggttt");
255     SequenceI seq1proxy = new SequenceDummy("Seq1");
256
257     // map to region within sequence is ok
258     assertTrue(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 12,
259             17));
260     // map to region overlapping sequence is ok
261     assertTrue(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 5,
262             10));
263     assertTrue(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 21,
264             26));
265     // map to region before sequence is not ok
266     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 4,
267             9));
268     // map to region after sequence is not ok
269     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 22,
270             27));
271
272     /*
273      * test should fail if name doesn't match
274      */
275     seq1proxy.setName("Seq1a");
276     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 12,
277             17));
278     seq1proxy.setName("Seq1");
279     seq1.setName("Seq1a");
280     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1proxy, seq1, 12,
281             17));
282
283     /*
284      * a dummy sequence can't replace a real one
285      */
286     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1, seq1proxy, 12,
287             17));
288
289     /*
290      * a dummy sequence can't replace a dummy sequence
291      */
292     SequenceI seq1proxy2 = new SequenceDummy("Seq1");
293     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1proxy,
294             seq1proxy2, 12, 17));
295
296     /*
297      * a real sequence can't replace a real one
298      */
299     SequenceI seq1a = new Sequence("Seq1/10-21", "aaacccgggttt");
300     assertFalse(AlignedCodonFrame.couldRealiseSequence(seq1, seq1a, 12, 17));
301   }
302
303   /**
304    * Tests for the method that tests whether any mapping to a dummy sequence can
305    * be 'realised' to a given real sequence
306    */
307   @Test(groups = { "Functional" })
308   public void testIsRealisableWith()
309   {
310     SequenceI seq1 = new Sequence("Seq1", "tttaaaCCCGGGtttaaa");
311     SequenceI seq2 = new Sequence("Seq2", "PG");
312     SequenceI seq1proxy = new SequenceDummy("Seq1");
313     seq1.createDatasetSequence();
314     seq2.createDatasetSequence();
315     MapList mapList = new MapList(new int[] { 7, 12 }, new int[] { 2, 3 },
316             3, 1);
317     AlignedCodonFrame acf = new AlignedCodonFrame();
318     acf.addMap(seq1proxy, seq2, mapList);
319
320     /*
321      * Seq2 is mapped to SequenceDummy seq1proxy bases 4-9
322      * This is 'realisable' from real sequence Seq1
323      */
324     assertTrue(acf.isRealisableWith(seq1));
325
326     /*
327      * test should fail if name doesn't match
328      */
329     seq1proxy.setName("Seq1a");
330     assertFalse(acf.isRealisableWith(seq1));
331     seq1proxy.setName("Seq1");
332
333     SequenceI seq1ds = seq1.getDatasetSequence();
334     seq1ds.setName("Seq1a");
335     assertFalse(acf.isRealisableWith(seq1));
336     seq1ds.setName("Seq1");
337
338     /*
339      * test should fail if no sequence overlap with mapping of bases 7-12
340      * use artificial start/end values to test this
341      */
342     seq1ds.setStart(1);
343     seq1ds.setEnd(6);
344     // seq1 precedes mapped region:
345     assertFalse(acf.isRealisableWith(seq1));
346     seq1ds.setEnd(7);
347     // seq1 includes first mapped base:
348     assertTrue(acf.isRealisableWith(seq1));
349     seq1ds.setStart(13);
350     seq1ds.setEnd(18);
351     // seq1 follows mapped region:
352     assertFalse(acf.isRealisableWith(seq1));
353     seq1ds.setStart(12);
354     // seq1 includes last mapped base:
355     assertTrue(acf.isRealisableWith(seq1));
356   }
357
358   /**
359    * Tests for the method that converts mappings to a dummy sequence to mappings
360    * to a compatible real sequence
361    */
362   @Test(groups = { "Functional" })
363   public void testRealiseWith()
364   {
365     SequenceI seq1 = new Sequence("Seq1", "tttCAACCCGGGtttaaa");
366     SequenceI seq2 = new Sequence("Seq2", "QPG");
367     SequenceI seq2a = new Sequence("Seq2a", "QPG");
368     SequenceI seq1proxy = new SequenceDummy("Seq1");
369     seq1.createDatasetSequence();
370     seq2.createDatasetSequence();
371     seq2a.createDatasetSequence();
372
373     /*
374      * Make mappings from Seq2 and Seq2a peptides to dummy sequence Seq1
375      */
376     AlignedCodonFrame acf = new AlignedCodonFrame();
377
378     // map PG to codons 7-12 (CCCGGG)
379     MapList mapping1 = new MapList(new int[] { 7, 12 }, new int[] { 2, 3 },
380             3, 1);
381     acf.addMap(seq1proxy, seq2, mapping1);
382     acf.addMap(seq1proxy, seq2a, mapping1);
383
384     // map QP to codons 4-9 (CAACCC)
385     MapList mapping2 = new MapList(new int[] { 4, 9 }, new int[] { 1, 2 },
386             3, 1);
387     acf.addMap(seq1proxy, seq2, mapping2);
388     acf.addMap(seq1proxy, seq2a, mapping2);
389
390     /*
391      * acf now has two mappings one from Seq1 to Seq2, one from Seq1 to Seq2a
392      */
393     assertEquals(2, acf.getdnaSeqs().length);
394     assertSame(seq1proxy, acf.getdnaSeqs()[0]);
395     assertSame(seq1proxy, acf.getdnaSeqs()[1]);
396     assertEquals(2, acf.getProtMappings().length);
397
398     // 'realise' these mappings with the compatible sequence seq1
399     // two mappings should be updated:
400     assertEquals(2, acf.realiseWith(seq1));
401     assertSame(seq1.getDatasetSequence(), acf.getdnaSeqs()[0]);
402     assertSame(seq1.getDatasetSequence(), acf.getdnaSeqs()[1]);
403   }
404
405   /**
406    * Test the method that locates the mapped codon for a protein position.
407    */
408   @Test(groups = { "Functional" })
409   public void testGetMappedRegion_eitherWay()
410   {
411     final Sequence seq1 = new Sequence("Seq1", "AAACCCGGGTTT");
412     seq1.createDatasetSequence();
413     final Sequence seq2 = new Sequence("Seq2", "KPGF");
414     seq2.createDatasetSequence();
415     final Sequence seq3 = new Sequence("Seq3", "QYKPGFSW");
416     seq3.createDatasetSequence();
417
418     /*
419      * map Seq1 to all of Seq2 and part of Seq3
420      */
421     AlignedCodonFrame acf = new AlignedCodonFrame();
422     MapList map = new MapList(new int[] { 1, 12 }, new int[] { 1, 4 }, 3, 1);
423     acf.addMap(seq1.getDatasetSequence(), seq2.getDatasetSequence(), map);
424     map = new MapList(new int[] { 1, 12 }, new int[] { 3, 6 }, 3, 1);
425     acf.addMap(seq1.getDatasetSequence(), seq3.getDatasetSequence(), map);
426
427     /*
428      * map part of Seq3 to Seq2
429      */
430     map = new MapList(new int[] { 3, 6 }, new int[] { 1, 4 }, 1, 1);
431     acf.addMap(seq3.getDatasetSequence(), seq2.getDatasetSequence(), map);
432
433     /*
434      * original case - locate mapped codon for protein position
435      */
436     assertArrayEquals(new int[] { 4, 6 },
437             acf.getMappedRegion(seq1, seq2, 2));
438     assertArrayEquals(new int[] { 7, 9 },
439             acf.getMappedRegion(seq1, seq3, 5));
440     assertNull(acf.getMappedRegion(seq1, seq3, 1));
441
442     /*
443      * locate mapped protein for protein position
444      */
445     assertArrayEquals(new int[] { 4, 4 },
446             acf.getMappedRegion(seq3, seq2, 2));
447
448     /*
449      * reverse location protein-to-protein
450      */
451     assertArrayEquals(new int[] { 2, 2 },
452             acf.getMappedRegion(seq2, seq3, 4));
453
454     /*
455      * reverse location protein-from-nucleotide
456      * any of codon [4, 5, 6] positions map to seq2/2
457      */
458     assertArrayEquals(new int[] { 2, 2 },
459             acf.getMappedRegion(seq2, seq1, 4));
460     assertArrayEquals(new int[] { 2, 2 },
461             acf.getMappedRegion(seq2, seq1, 5));
462     assertArrayEquals(new int[] { 2, 2 },
463             acf.getMappedRegion(seq2, seq1, 6));
464   }
465
466   /**
467    * Tests for addMap. See also tests for MapList.addMapList
468    */
469   @Test(groups = { "Functional" })
470   public void testAddMap()
471   {
472     final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T");
473     seq1.createDatasetSequence();
474     final Sequence aseq1 = new Sequence("Seq1", "-V-L");
475     aseq1.createDatasetSequence();
476
477     AlignedCodonFrame acf = new AlignedCodonFrame();
478     MapList map = new MapList(new int[] { 2, 4, 6, 6, 8, 9 }, new int[] {
479         1, 2 }, 3, 1);
480     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
481     assertEquals(1, acf.getMappingsFromSequence(seq1).size());
482     Mapping before = acf.getMappingsFromSequence(seq1).get(0);
483
484     /*
485      * add the same map again, verify it doesn't get duplicated
486      */
487     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
488     assertEquals(1, acf.getMappingsFromSequence(seq1).size());
489     assertSame(before, acf.getMappingsFromSequence(seq1).get(0));
490   }
491   
492   @Test(groups = { "Functional" })
493   public void testGetCoveringMapping()
494   {
495     SequenceI dna = new Sequence("dna", "acttcaATGGCGGACtaattt");
496     SequenceI cds = new Sequence("cds/7-15", "ATGGCGGAC");
497     cds.setDatasetSequence(dna);
498     SequenceI pep = new Sequence("pep", "MAD");
499     
500     /*
501      * with null argument or no mappings
502      */
503     AlignedCodonFrame acf = new AlignedCodonFrame();
504     assertNull(acf.getCoveringMapping(null,  null));
505     assertNull(acf.getCoveringMapping(dna,  null));
506     assertNull(acf.getCoveringMapping(null,  pep));
507     assertNull(acf.getCoveringMapping(dna,  pep));
508
509     /*
510      * with a non-covering mapping e.g. overlapping exon
511      */
512     MapList map = new MapList(new int[] { 7, 9 }, new int[] {
513         1, 1 }, 3, 1);
514     acf.addMap(dna, pep, map);
515     assertNull(acf.getCoveringMapping(dna,  pep));
516     
517     acf = new AlignedCodonFrame();
518     MapList map2 = new MapList(new int[] { 13, 18 }, new int[] {
519         2, 2 }, 3, 1);
520     acf.addMap(dna, pep, map2);
521     assertNull(acf.getCoveringMapping(dna,  pep));
522     
523     /*
524      * with a covering mapping from CDS (dataset) to protein
525      */
526     acf = new AlignedCodonFrame();
527     MapList map3 = new MapList(new int[] { 7, 15 }, new int[] {
528         1, 3 }, 3, 1);
529     acf.addMap(dna, pep, map3);
530     assertNull(acf.getCoveringMapping(dna,  pep));
531     SequenceToSequenceMapping mapping = acf.getCoveringMapping(cds,  pep);
532     assertNotNull(mapping);
533     
534     /*
535      * with a mapping that extends to stop codon
536      */
537     acf = new AlignedCodonFrame();
538     MapList map4 = new MapList(new int[] { 7, 18 }, new int[] {
539         1, 3 }, 3, 1);
540     acf.addMap(dna, pep, map4);
541     assertNull(acf.getCoveringMapping(dna,  pep));
542     assertNull(acf.getCoveringMapping(cds,  pep));
543     SequenceI cds2 = new Sequence("cds/7-18", "ATGGCGGACtaa");
544     cds2.setDatasetSequence(dna);
545     mapping = acf.getCoveringMapping(cds2,  pep);
546     assertNotNull(mapping);
547   }
548 }