cfb256337964205aba9054b9771636c1c974201b
[jalview.git] / test / jalview / util / MappingUtilsTest.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.util;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertFalse;
25 import static org.testng.AssertJUnit.assertSame;
26 import static org.testng.AssertJUnit.assertTrue;
27 import static org.testng.AssertJUnit.fail;
28
29 import java.awt.Color;
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Iterator;
34 import java.util.List;
35
36 import org.testng.annotations.BeforeClass;
37 import org.testng.annotations.Test;
38
39 import jalview.api.AlignViewportI;
40 import jalview.bin.Cache;
41 import jalview.commands.EditCommand;
42 import jalview.commands.EditCommand.Action;
43 import jalview.commands.EditCommand.Edit;
44 import jalview.datamodel.AlignedCodonFrame;
45 import jalview.datamodel.Alignment;
46 import jalview.datamodel.AlignmentI;
47 import jalview.datamodel.ColumnSelection;
48 import jalview.datamodel.HiddenColumns;
49 import jalview.datamodel.SearchResultMatchI;
50 import jalview.datamodel.SearchResultsI;
51 import jalview.datamodel.Sequence;
52 import jalview.datamodel.SequenceGroup;
53 import jalview.datamodel.SequenceI;
54 import jalview.gui.AlignViewport;
55 import jalview.gui.JvOptionPane;
56 import jalview.io.DataSourceType;
57 import jalview.io.FileFormat;
58 import jalview.io.FileFormatI;
59 import jalview.io.FormatAdapter;
60
61 public class MappingUtilsTest
62 {
63   @BeforeClass(alwaysRun = true)
64   public void setUp()
65   {
66     Cache.initLogger();
67   }
68   
69
70   @BeforeClass(alwaysRun = true)
71   public void setUpJvOptionPane()
72   {
73     JvOptionPane.setInteractiveMode(false);
74     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
75   }
76
77   private AlignViewportI dnaView;
78
79   private AlignViewportI proteinView;
80
81   /**
82    * Simple test of mapping with no intron involved.
83    */
84   @Test(groups = { "Functional" })
85   public void testBuildSearchResults()
86   {
87     final Sequence seq1 = new Sequence("Seq1/5-10", "C-G-TA-GC");
88     seq1.createDatasetSequence();
89
90     final Sequence aseq1 = new Sequence("Seq1/12-13", "-P-R");
91     aseq1.createDatasetSequence();
92
93     /*
94      * Map dna bases 5-10 to protein residues 12-13
95      */
96     AlignedCodonFrame acf = new AlignedCodonFrame();
97     MapList map = new MapList(new int[] { 5, 10 }, new int[] { 12, 13 }, 3,
98             1);
99     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
100     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
101     { acf });
102
103     /*
104      * Check protein residue 12 maps to codon 5-7, 13 to codon 8-10
105      */
106     SearchResultsI sr = MappingUtils.buildSearchResults(aseq1, 12, acfList);
107     assertEquals(1, sr.getResults().size());
108     SearchResultMatchI m = sr.getResults().get(0);
109     assertEquals(seq1.getDatasetSequence(), m.getSequence());
110     assertEquals(5, m.getStart());
111     assertEquals(7, m.getEnd());
112     sr = MappingUtils.buildSearchResults(aseq1, 13, acfList);
113     assertEquals(1, sr.getResults().size());
114     m = sr.getResults().get(0);
115     assertEquals(seq1.getDatasetSequence(), m.getSequence());
116     assertEquals(8, m.getStart());
117     assertEquals(10, m.getEnd());
118
119     /*
120      * Check inverse mappings, from codons 5-7, 8-10 to protein 12, 13
121      */
122     for (int i = 5; i < 11; i++)
123     {
124       sr = MappingUtils.buildSearchResults(seq1, i, acfList);
125       assertEquals(1, sr.getResults().size());
126       m = sr.getResults().get(0);
127       assertEquals(aseq1.getDatasetSequence(), m.getSequence());
128       int residue = i > 7 ? 13 : 12;
129       assertEquals(residue, m.getStart());
130       assertEquals(residue, m.getEnd());
131     }
132   }
133
134   /**
135    * Simple test of mapping with introns involved.
136    */
137   @Test(groups = { "Functional" })
138   public void testBuildSearchResults_withIntron()
139   {
140     final Sequence seq1 = new Sequence("Seq1/5-17", "c-G-tAGa-GcAgCtt");
141     seq1.createDatasetSequence();
142
143     final Sequence aseq1 = new Sequence("Seq1/8-9", "-E-D");
144     aseq1.createDatasetSequence();
145
146     /*
147      * Map dna bases [6, 8, 9], [11, 13, 115] to protein residues 8 and 9
148      */
149     AlignedCodonFrame acf = new AlignedCodonFrame();
150     MapList map = new MapList(new int[] { 6, 6, 8, 9, 11, 11, 13, 13, 15,
151         15 }, new int[] { 8, 9 }, 3, 1);
152     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
153     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
154     { acf });
155
156     /*
157      * Check protein residue 8 maps to [6, 8, 9]
158      */
159     SearchResultsI sr = MappingUtils.buildSearchResults(aseq1, 8, acfList);
160     assertEquals(2, sr.getResults().size());
161     SearchResultMatchI m = sr.getResults().get(0);
162     assertEquals(seq1.getDatasetSequence(), m.getSequence());
163     assertEquals(6, m.getStart());
164     assertEquals(6, m.getEnd());
165     m = sr.getResults().get(1);
166     assertEquals(seq1.getDatasetSequence(), m.getSequence());
167     assertEquals(8, m.getStart());
168     assertEquals(9, m.getEnd());
169
170     /*
171      * Check protein residue 9 maps to [11, 13, 15]
172      */
173     sr = MappingUtils.buildSearchResults(aseq1, 9, acfList);
174     assertEquals(3, sr.getResults().size());
175     m = sr.getResults().get(0);
176     assertEquals(seq1.getDatasetSequence(), m.getSequence());
177     assertEquals(11, m.getStart());
178     assertEquals(11, m.getEnd());
179     m = sr.getResults().get(1);
180     assertEquals(seq1.getDatasetSequence(), m.getSequence());
181     assertEquals(13, m.getStart());
182     assertEquals(13, m.getEnd());
183     m = sr.getResults().get(2);
184     assertEquals(seq1.getDatasetSequence(), m.getSequence());
185     assertEquals(15, m.getStart());
186     assertEquals(15, m.getEnd());
187
188     /*
189      * Check inverse mappings, from codons to protein
190      */
191     for (int i = 5; i < 18; i++)
192     {
193       sr = MappingUtils.buildSearchResults(seq1, i, acfList);
194       int residue = (i == 6 || i == 8 || i == 9) ? 8 : (i == 11 || i == 13
195               || i == 15 ? 9 : 0);
196       if (residue == 0)
197       {
198         assertEquals(0, sr.getResults().size());
199         continue;
200       }
201       assertEquals(1, sr.getResults().size());
202       m = sr.getResults().get(0);
203       assertEquals(aseq1.getDatasetSequence(), m.getSequence());
204       assertEquals(residue, m.getStart());
205       assertEquals(residue, m.getEnd());
206     }
207   }
208
209   /**
210    * Test mapping a sequence group made of entire sequences.
211    * 
212    * @throws IOException
213    */
214   @Test(groups = { "Functional" })
215   public void testMapSequenceGroup_sequences() throws IOException
216   {
217     /*
218      * Set up dna and protein Seq1/2/3 with mappings (held on the protein
219      * viewport).
220      */
221     AlignmentI cdna = loadAlignment(">Seq1\nACG\n>Seq2\nTGA\n>Seq3\nTAC\n",
222             FileFormat.Fasta);
223     cdna.setDataset(null);
224     AlignmentI protein = loadAlignment(">Seq1\nK\n>Seq2\nL\n>Seq3\nQ\n",
225             FileFormat.Fasta);
226     protein.setDataset(null);
227     AlignedCodonFrame acf = new AlignedCodonFrame();
228     MapList map = new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 3, 1);
229     for (int seq = 0; seq < 3; seq++)
230     {
231       acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(), protein
232               .getSequenceAt(seq).getDatasetSequence(), map);
233     }
234     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
235     { acf });
236
237     AlignViewportI dnaView = new AlignViewport(cdna);
238     AlignViewportI proteinView = new AlignViewport(protein);
239     protein.setCodonFrames(acfList);
240
241     /*
242      * Select Seq1 and Seq3 in the protein (startRes=endRes=0)
243      */
244     SequenceGroup sg = new SequenceGroup();
245     sg.setColourText(true);
246     sg.setIdColour(Color.GREEN);
247     sg.setOutlineColour(Color.LIGHT_GRAY);
248     sg.addSequence(protein.getSequenceAt(0), false);
249     sg.addSequence(protein.getSequenceAt(2), false);
250
251     /*
252      * Verify the mapped sequence group in dna
253      */
254     SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg,
255             proteinView, dnaView);
256     assertTrue(mappedGroup.getColourText());
257     assertSame(sg.getIdColour(), mappedGroup.getIdColour());
258     assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
259     assertEquals(2, mappedGroup.getSequences().size());
260     assertSame(cdna.getSequenceAt(0), mappedGroup.getSequences().get(0));
261     assertSame(cdna.getSequenceAt(2), mappedGroup.getSequences().get(1));
262     assertEquals(0, mappedGroup.getStartRes());
263     assertEquals(2, mappedGroup.getEndRes());
264
265     /*
266      * Verify mapping sequence group from dna to protein
267      */
268     sg.clear();
269     sg.addSequence(cdna.getSequenceAt(1), false);
270     sg.addSequence(cdna.getSequenceAt(0), false);
271     sg.setStartRes(0);
272     sg.setEndRes(2);
273     mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
274     assertTrue(mappedGroup.getColourText());
275     assertSame(sg.getIdColour(), mappedGroup.getIdColour());
276     assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
277     assertEquals(2, mappedGroup.getSequences().size());
278     assertSame(protein.getSequenceAt(1), mappedGroup.getSequences().get(0));
279     assertSame(protein.getSequenceAt(0), mappedGroup.getSequences().get(1));
280     assertEquals(0, mappedGroup.getStartRes());
281     assertEquals(0, mappedGroup.getEndRes());
282   }
283
284   /**
285    * Helper method to load an alignment and ensure dataset sequences are set up.
286    * 
287    * @param data
288    * @param format
289    *          TODO
290    * @return
291    * @throws IOException
292    */
293   protected AlignmentI loadAlignment(final String data, FileFormatI format)
294           throws IOException
295   {
296     AlignmentI a = new FormatAdapter().readFile(data,
297             DataSourceType.PASTE, format);
298     a.setDataset(null);
299     return a;
300   }
301
302   /**
303    * Test mapping a column selection in protein to its dna equivalent
304    * 
305    * @throws IOException
306    */
307   @Test(groups = { "Functional" })
308   public void testMapColumnSelection_proteinToDna() throws IOException
309   {
310     setupMappedAlignments();
311
312     ColumnSelection colsel = new ColumnSelection();
313     HiddenColumns hidden = new HiddenColumns();
314
315     /*
316      * Column 0 in protein picks up Seq2/L, Seq3/G which map to cols 0-4 and 0-3
317      * in dna respectively, overall 0-4
318      */
319     colsel.addElement(0);
320     ColumnSelection cs = new ColumnSelection();
321     HiddenColumns hs = new HiddenColumns();
322     MappingUtils.mapColumnSelection(colsel, hidden, proteinView, dnaView,
323             cs, hs);
324     assertEquals("[0, 1, 2, 3, 4]", cs.getSelected().toString());
325
326     /*
327      * Column 1 in protein picks up Seq1/K which maps to cols 0-3 in dna
328      */
329     cs.clear();
330     colsel.clear();
331     colsel.addElement(1);
332     MappingUtils.mapColumnSelection(colsel, hidden, proteinView, dnaView,
333             cs, hs);
334     assertEquals("[0, 1, 2, 3]", cs.getSelected().toString());
335
336     /*
337      * Column 2 in protein picks up gaps only - no mapping
338      */
339     cs.clear();
340     colsel.clear();
341     colsel.addElement(2);
342     MappingUtils.mapColumnSelection(colsel, hidden, proteinView,
343             dnaView, cs, hs);
344     assertEquals("[]", cs.getSelected().toString());
345
346     /*
347      * Column 3 in protein picks up Seq1/P, Seq2/Q, Seq3/S which map to columns
348      * 6-9, 6-10, 5-8 respectively, overall to 5-10
349      */
350     cs.clear();
351     colsel.clear();
352     colsel.addElement(3);
353     MappingUtils.mapColumnSelection(colsel, hidden, proteinView,
354             dnaView, cs, hs);
355     assertEquals("[5, 6, 7, 8, 9, 10]", cs.getSelected().toString());
356
357     /*
358      * Combine selection of columns 1 and 3 to get a discontiguous mapped
359      * selection
360      */
361     cs.clear();
362     colsel.clear();
363     colsel.addElement(1);
364     colsel.addElement(3);
365     MappingUtils.mapColumnSelection(colsel, hidden, proteinView,
366             dnaView, cs, hs);
367     assertEquals("[0, 1, 2, 3, 5, 6, 7, 8, 9, 10]", cs.getSelected()
368             .toString());
369   }
370
371   /**
372    * Set up mappings for tests from 3 dna to 3 protein sequences. Sequences have
373    * offset start positions for a more general test case.
374    * 
375    * @throws IOException
376    */
377   protected void setupMappedAlignments() throws IOException
378   {
379     /*
380      * Map (upper-case = coding):
381      * Seq1/10-18 AC-GctGtC-T to Seq1/40 -K-P
382      * Seq2/20-27 Tc-GA-G-T-T to Seq2/20-27 L--Q
383      * Seq3/30-38 TtTT-AaCGg- to Seq3/60-61\nG--S
384      */
385     AlignmentI cdna = loadAlignment(">Seq1/10-18\nAC-GctGtC-T\n"
386             + ">Seq2/20-27\nTc-GA-G-T-Tc\n" + ">Seq3/30-38\nTtTT-AaCGg-\n",
387             FileFormat.Fasta);
388     cdna.setDataset(null);
389     AlignmentI protein = loadAlignment(
390             ">Seq1/40-41\n-K-P\n>Seq2/50-51\nL--Q\n>Seq3/60-61\nG--S\n",
391             FileFormat.Fasta);
392     protein.setDataset(null);
393
394     // map first dna to first protein seq
395     AlignedCodonFrame acf = new AlignedCodonFrame();
396     MapList map = new MapList(new int[] { 10, 12, 15, 15, 17, 18 },
397             new int[] { 40, 41 }, 3, 1);
398     acf.addMap(cdna.getSequenceAt(0).getDatasetSequence(), protein
399             .getSequenceAt(0).getDatasetSequence(), map);
400
401     // map second dna to second protein seq
402     map = new MapList(new int[] { 20, 20, 22, 23, 24, 26 }, new int[] { 50,
403         51 }, 3, 1);
404     acf.addMap(cdna.getSequenceAt(1).getDatasetSequence(), protein
405             .getSequenceAt(1).getDatasetSequence(), map);
406
407     // map third dna to third protein seq
408     map = new MapList(new int[] { 30, 30, 32, 34, 36, 37 }, new int[] { 60,
409         61 }, 3, 1);
410     acf.addMap(cdna.getSequenceAt(2).getDatasetSequence(), protein
411             .getSequenceAt(2).getDatasetSequence(), map);
412     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
413     { acf });
414
415     dnaView = new AlignViewport(cdna);
416     proteinView = new AlignViewport(protein);
417     protein.setCodonFrames(acfList);
418   }
419
420   /**
421    * Test mapping a column selection in dna to its protein equivalent
422    * 
423    * @throws IOException
424    */
425   @Test(groups = { "Functional" })
426   public void testMapColumnSelection_dnaToProtein() throws IOException
427   {
428     setupMappedAlignments();
429
430     ColumnSelection colsel = new ColumnSelection();
431     HiddenColumns hidden = new HiddenColumns();
432
433     /*
434      * Column 0 in dna picks up first bases which map to residue 1, columns 0-1
435      * in protein.
436      */
437     ColumnSelection cs = new ColumnSelection();
438     HiddenColumns hs = new HiddenColumns();
439     colsel.addElement(0);
440     MappingUtils.mapColumnSelection(colsel, hidden, dnaView, proteinView,
441             cs, hs);
442     assertEquals("[0, 1]", cs.getSelected().toString());
443
444     /*
445      * Columns 3-5 in dna map to the first residues in protein Seq1, Seq2, and
446      * the first two in Seq3. Overall to columns 0, 1, 3 (col2 is all gaps).
447      */
448     colsel.addElement(3);
449     colsel.addElement(4);
450     colsel.addElement(5);
451     cs.clear();
452     MappingUtils.mapColumnSelection(colsel, hidden, dnaView, proteinView,
453             cs, hs);
454     assertEquals("[0, 1, 3]", cs.getSelected().toString());
455   }
456
457   @Test(groups = { "Functional" })
458   public void testMapColumnSelection_null() throws IOException
459   {
460     setupMappedAlignments();
461     ColumnSelection cs = new ColumnSelection();
462     HiddenColumns hs = new HiddenColumns();
463     MappingUtils.mapColumnSelection(null, null, dnaView, proteinView, cs,
464             hs);
465     assertTrue("mapped selection not empty", cs.getSelected().isEmpty());
466   }
467
468   /**
469    * Tests for the method that converts a series of [start, end] ranges to
470    * single positions
471    */
472   @Test(groups = { "Functional" })
473   public void testFlattenRanges()
474   {
475     assertEquals("[1, 2, 3, 4]",
476             Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 4 })));
477     assertEquals(
478             "[1, 2, 3, 4]",
479             Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 2, 3,
480                 4 })));
481     assertEquals(
482             "[1, 2, 3, 4]",
483             Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 1, 2,
484                 2, 3, 3, 4, 4 })));
485     assertEquals(
486             "[1, 2, 3, 4, 7, 8, 9, 12]",
487             Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 4, 7,
488                 9, 12, 12 })));
489     // trailing unpaired start position is ignored:
490     assertEquals(
491             "[1, 2, 3, 4, 7, 8, 9, 12]",
492             Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 4, 7,
493                 9, 12, 12, 15 })));
494   }
495
496   /**
497    * Test mapping a sequence group made of entire columns.
498    * 
499    * @throws IOException
500    */
501   @Test(groups = { "Functional" })
502   public void testMapSequenceGroup_columns() throws IOException
503   {
504     /*
505      * Set up dna and protein Seq1/2/3 with mappings (held on the protein
506      * viewport).
507      */
508     AlignmentI cdna = loadAlignment(
509             ">Seq1\nACGGCA\n>Seq2\nTGACAG\n>Seq3\nTACGTA\n",
510             FileFormat.Fasta);
511     cdna.setDataset(null);
512     AlignmentI protein = loadAlignment(">Seq1\nKA\n>Seq2\nLQ\n>Seq3\nQV\n",
513             FileFormat.Fasta);
514     protein.setDataset(null);
515     AlignedCodonFrame acf = new AlignedCodonFrame();
516     MapList map = new MapList(new int[] { 1, 6 }, new int[] { 1, 2 }, 3, 1);
517     for (int seq = 0; seq < 3; seq++)
518     {
519       acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(), protein
520               .getSequenceAt(seq).getDatasetSequence(), map);
521     }
522     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
523     { acf });
524
525     AlignViewportI dnaView = new AlignViewport(cdna);
526     AlignViewportI proteinView = new AlignViewport(protein);
527     protein.setCodonFrames(acfList);
528
529     /*
530      * Select all sequences, column 2 in the protein
531      */
532     SequenceGroup sg = new SequenceGroup();
533     sg.setColourText(true);
534     sg.setIdColour(Color.GREEN);
535     sg.setOutlineColour(Color.LIGHT_GRAY);
536     sg.addSequence(protein.getSequenceAt(0), false);
537     sg.addSequence(protein.getSequenceAt(1), false);
538     sg.addSequence(protein.getSequenceAt(2), false);
539     sg.setStartRes(1);
540     sg.setEndRes(1);
541
542     /*
543      * Verify the mapped sequence group in dna
544      */
545     SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg,
546             proteinView, dnaView);
547     assertTrue(mappedGroup.getColourText());
548     assertSame(sg.getIdColour(), mappedGroup.getIdColour());
549     assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
550     assertEquals(3, mappedGroup.getSequences().size());
551     assertSame(cdna.getSequenceAt(0), mappedGroup.getSequences().get(0));
552     assertSame(cdna.getSequenceAt(1), mappedGroup.getSequences().get(1));
553     assertSame(cdna.getSequenceAt(2), mappedGroup.getSequences().get(2));
554     assertEquals(3, mappedGroup.getStartRes());
555     assertEquals(5, mappedGroup.getEndRes());
556
557     /*
558      * Verify mapping sequence group from dna to protein
559      */
560     sg.clear();
561     sg.addSequence(cdna.getSequenceAt(0), false);
562     sg.addSequence(cdna.getSequenceAt(1), false);
563     sg.addSequence(cdna.getSequenceAt(2), false);
564     // select columns 2 and 3 in DNA which span protein columns 0 and 1
565     sg.setStartRes(2);
566     sg.setEndRes(3);
567     mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
568     assertTrue(mappedGroup.getColourText());
569     assertSame(sg.getIdColour(), mappedGroup.getIdColour());
570     assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
571     assertEquals(3, mappedGroup.getSequences().size());
572     assertSame(protein.getSequenceAt(0), mappedGroup.getSequences().get(0));
573     assertSame(protein.getSequenceAt(1), mappedGroup.getSequences().get(1));
574     assertSame(protein.getSequenceAt(2), mappedGroup.getSequences().get(2));
575     assertEquals(0, mappedGroup.getStartRes());
576     assertEquals(1, mappedGroup.getEndRes());
577   }
578
579   /**
580    * Test mapping a sequence group made of a sequences/columns region.
581    * 
582    * @throws IOException
583    */
584   @Test(groups = { "Functional" })
585   public void testMapSequenceGroup_region() throws IOException
586   {
587     /*
588      * Set up gapped dna and protein Seq1/2/3 with mappings (held on the protein
589      * viewport).
590      */
591     AlignmentI cdna = loadAlignment(
592             ">Seq1\nA-CG-GC--AT-CA\n>Seq2\n-TG-AC-AG-T-AT\n>Seq3\n-T--ACG-TAAT-G\n",
593             FileFormat.Fasta);
594     cdna.setDataset(null);
595     AlignmentI protein = loadAlignment(
596             ">Seq1\n-KA-S\n>Seq2\n--L-QY\n>Seq3\nQ-V-M\n", FileFormat.Fasta);
597     protein.setDataset(null);
598     AlignedCodonFrame acf = new AlignedCodonFrame();
599     MapList map = new MapList(new int[] { 1, 9 }, new int[] { 1, 3 }, 3, 1);
600     for (int seq = 0; seq < 3; seq++)
601     {
602       acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(), protein
603               .getSequenceAt(seq).getDatasetSequence(), map);
604     }
605     List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
606     { acf });
607
608     AlignViewportI dnaView = new AlignViewport(cdna);
609     AlignViewportI proteinView = new AlignViewport(protein);
610     protein.setCodonFrames(acfList);
611
612     /*
613      * Select Seq1 and Seq2 in the protein, column 1 (K/-). Expect mapped
614      * sequence group to cover Seq1, columns 0-3 (ACG). Because the selection
615      * only includes a gap in Seq2 there is no mappable selection region in the
616      * corresponding DNA.
617      */
618     SequenceGroup sg = new SequenceGroup();
619     sg.setColourText(true);
620     sg.setIdColour(Color.GREEN);
621     sg.setOutlineColour(Color.LIGHT_GRAY);
622     sg.addSequence(protein.getSequenceAt(0), false);
623     sg.addSequence(protein.getSequenceAt(1), false);
624     sg.setStartRes(1);
625     sg.setEndRes(1);
626
627     /*
628      * Verify the mapped sequence group in dna
629      */
630     SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg,
631             proteinView, dnaView);
632     assertTrue(mappedGroup.getColourText());
633     assertSame(sg.getIdColour(), mappedGroup.getIdColour());
634     assertSame(sg.getOutlineColour(), mappedGroup.getOutlineColour());
635     assertEquals(1, mappedGroup.getSequences().size());
636     assertSame(cdna.getSequenceAt(0), mappedGroup.getSequences().get(0));
637     // Seq2 in protein has a gap in column 1 - ignored
638     // Seq1 has K which should map to columns 0-3 in Seq1
639     assertEquals(0, mappedGroup.getStartRes());
640     assertEquals(3, mappedGroup.getEndRes());
641
642     /*
643      * Now select cols 2-4 in protein. These cover Seq1:AS Seq2:LQ Seq3:VM which
644      * extend over DNA columns 3-12, 1-7, 6-13 respectively, or 1-13 overall.
645      */
646     sg.setStartRes(2);
647     sg.setEndRes(4);
648     mappedGroup = MappingUtils.mapSequenceGroup(sg, proteinView, dnaView);
649     assertEquals(1, mappedGroup.getStartRes());
650     assertEquals(13, mappedGroup.getEndRes());
651
652     /*
653      * Verify mapping sequence group from dna to protein
654      */
655     sg.clear();
656     sg.addSequence(cdna.getSequenceAt(0), false);
657
658     // select columns 4,5 - includes Seq1:codon2 (A) only
659     sg.setStartRes(4);
660     sg.setEndRes(5);
661     mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
662     assertEquals(2, mappedGroup.getStartRes());
663     assertEquals(2, mappedGroup.getEndRes());
664
665     // add Seq2 to dna selection cols 4-5 include codons 1 and 2 (LQ)
666     sg.addSequence(cdna.getSequenceAt(1), false);
667     mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
668     assertEquals(2, mappedGroup.getStartRes());
669     assertEquals(4, mappedGroup.getEndRes());
670
671     // add Seq3 to dna selection cols 4-5 include codon 1 (Q)
672     sg.addSequence(cdna.getSequenceAt(2), false);
673     mappedGroup = MappingUtils.mapSequenceGroup(sg, dnaView, proteinView);
674     assertEquals(0, mappedGroup.getStartRes());
675     assertEquals(4, mappedGroup.getEndRes());
676   }
677
678   @Test(groups = { "Functional" })
679   public void testFindMappingsForSequence()
680   {
681     SequenceI seq1 = new Sequence("Seq1", "ABC");
682     SequenceI seq2 = new Sequence("Seq2", "ABC");
683     SequenceI seq3 = new Sequence("Seq3", "ABC");
684     SequenceI seq4 = new Sequence("Seq4", "ABC");
685     seq1.createDatasetSequence();
686     seq2.createDatasetSequence();
687     seq3.createDatasetSequence();
688     seq4.createDatasetSequence();
689
690     /*
691      * Create mappings from seq1 to seq2, seq2 to seq1, seq3 to seq1
692      */
693     AlignedCodonFrame acf1 = new AlignedCodonFrame();
694     MapList map = new MapList(new int[] { 1, 3 }, new int[] { 1, 3 }, 1, 1);
695     acf1.addMap(seq1.getDatasetSequence(), seq2.getDatasetSequence(), map);
696     AlignedCodonFrame acf2 = new AlignedCodonFrame();
697     acf2.addMap(seq2.getDatasetSequence(), seq1.getDatasetSequence(), map);
698     AlignedCodonFrame acf3 = new AlignedCodonFrame();
699     acf3.addMap(seq3.getDatasetSequence(), seq1.getDatasetSequence(), map);
700
701     List<AlignedCodonFrame> mappings = new ArrayList<>();
702     mappings.add(acf1);
703     mappings.add(acf2);
704     mappings.add(acf3);
705
706     /*
707      * Seq1 has three mappings
708      */
709     List<AlignedCodonFrame> result = MappingUtils.findMappingsForSequence(
710             seq1, mappings);
711     assertEquals(3, result.size());
712     assertTrue(result.contains(acf1));
713     assertTrue(result.contains(acf2));
714     assertTrue(result.contains(acf3));
715
716     /*
717      * Seq2 has two mappings
718      */
719     result = MappingUtils.findMappingsForSequence(seq2, mappings);
720     assertEquals(2, result.size());
721     assertTrue(result.contains(acf1));
722     assertTrue(result.contains(acf2));
723
724     /*
725      * Seq3 has one mapping
726      */
727     result = MappingUtils.findMappingsForSequence(seq3, mappings);
728     assertEquals(1, result.size());
729     assertTrue(result.contains(acf3));
730
731     /*
732      * Seq4 has no mappings
733      */
734     result = MappingUtils.findMappingsForSequence(seq4, mappings);
735     assertEquals(0, result.size());
736
737     result = MappingUtils.findMappingsForSequence(null, mappings);
738     assertEquals(0, result.size());
739
740     result = MappingUtils.findMappingsForSequence(seq1, null);
741     assertEquals(0, result.size());
742
743     result = MappingUtils.findMappingsForSequence(null, null);
744     assertEquals(0, result.size());
745   }
746
747   /**
748    * just like the one above, but this time, we provide a set of sequences to
749    * subselect the mapping search
750    */
751   @Test(groups = { "Functional" })
752   public void testFindMappingsForSequenceAndOthers()
753   {
754     SequenceI seq1 = new Sequence("Seq1", "ABC");
755     SequenceI seq2 = new Sequence("Seq2", "ABC");
756     SequenceI seq3 = new Sequence("Seq3", "ABC");
757     SequenceI seq4 = new Sequence("Seq4", "ABC");
758     seq1.createDatasetSequence();
759     seq2.createDatasetSequence();
760     seq3.createDatasetSequence();
761     seq4.createDatasetSequence();
762
763     /*
764      * Create mappings from seq1 to seq2, seq2 to seq1, seq3 to seq1, seq3 to seq4
765      */
766     AlignedCodonFrame acf1 = new AlignedCodonFrame();
767     MapList map = new MapList(new int[] { 1, 3 }, new int[] { 1, 3 }, 1, 1);
768     acf1.addMap(seq1.getDatasetSequence(), seq2.getDatasetSequence(), map);
769     AlignedCodonFrame acf2 = new AlignedCodonFrame();
770     acf2.addMap(seq2.getDatasetSequence(), seq1.getDatasetSequence(), map);
771     AlignedCodonFrame acf3 = new AlignedCodonFrame();
772     acf3.addMap(seq3.getDatasetSequence(), seq1.getDatasetSequence(), map);
773     AlignedCodonFrame acf4 = new AlignedCodonFrame();
774     acf4.addMap(seq3.getDatasetSequence(), seq4.getDatasetSequence(), map);
775
776     List<AlignedCodonFrame> mappings = new ArrayList<>();
777     mappings.add(acf1);
778     mappings.add(acf2);
779     mappings.add(acf3);
780     mappings.add(acf4);
781
782     /*
783      * test for null args
784      */
785     List<AlignedCodonFrame> result = MappingUtils
786             .findMappingsForSequenceAndOthers(null, mappings,
787                     Arrays.asList(new SequenceI[] { seq1, seq2 }));
788     assertTrue(result.isEmpty());
789
790     result = MappingUtils.findMappingsForSequenceAndOthers(seq1, null,
791             Arrays.asList(new SequenceI[] { seq1, seq2 }));
792     assertTrue(result.isEmpty());
793
794     /*
795      * Seq1 has three mappings, but filter argument will only accept
796      * those to seq2
797      */
798     result = MappingUtils.findMappingsForSequenceAndOthers(
799             seq1,
800             mappings,
801             Arrays.asList(new SequenceI[] { seq1, seq2,
802                 seq1.getDatasetSequence() }));
803     assertEquals(2, result.size());
804     assertTrue(result.contains(acf1));
805     assertTrue(result.contains(acf2));
806     assertFalse("Did not expect to find mapping acf3 - subselect failed",
807             result.contains(acf3));
808     assertFalse(
809             "Did not expect to find mapping acf4 - doesn't involve sequence",
810             result.contains(acf4));
811
812     /*
813      * and verify the no filter case
814      */
815     result = MappingUtils.findMappingsForSequenceAndOthers(seq1, mappings,
816             null);
817     assertEquals(3, result.size());
818     assertTrue(result.contains(acf1));
819     assertTrue(result.contains(acf2));
820     assertTrue(result.contains(acf3));
821   }
822
823   @Test(groups = { "Functional" })
824   public void testMapEditCommand()
825   {
826     SequenceI dna = new Sequence("Seq1", "---ACG---GCATCA", 8, 16);
827     SequenceI protein = new Sequence("Seq2", "-T-AS", 5, 7);
828     dna.createDatasetSequence();
829     protein.createDatasetSequence();
830     AlignedCodonFrame acf = new AlignedCodonFrame();
831     MapList map = new MapList(new int[] { 8, 16 }, new int[] { 5, 7 }, 3, 1);
832     acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
833     List<AlignedCodonFrame> mappings = new ArrayList<>();
834     mappings.add(acf);
835
836     AlignmentI prot = new Alignment(new SequenceI[] { protein });
837     prot.setCodonFrames(mappings);
838     AlignmentI nuc = new Alignment(new SequenceI[] { dna });
839
840     /*
841      * construct and perform the edit command to turn "-T-AS" in to "-T-A--S"
842      * i.e. insert two gaps at column 4
843      */
844     EditCommand ec = new EditCommand();
845     final Edit edit = ec.new Edit(Action.INSERT_GAP,
846             new SequenceI[] { protein }, 4, 2, '-');
847     ec.appendEdit(edit, prot, true, null);
848
849     /*
850      * the mapped edit command should be to insert 6 gaps before base 4 in the
851      * nucleotide sequence, which corresponds to aligned column 12 in the dna
852      */
853     EditCommand mappedEdit = MappingUtils.mapEditCommand(ec, false, nuc,
854             '-', mappings);
855     assertEquals(1, mappedEdit.getEdits().size());
856     Edit e = mappedEdit.getEdits().get(0);
857     assertEquals(1, e.getSequences().length);
858     assertEquals(dna, e.getSequences()[0]);
859     assertEquals(12, e.getPosition());
860     assertEquals(6, e.getNumber());
861   }
862
863   /**
864    * Tests for the method that converts a series of [start, end] ranges to
865    * single positions, where the mapping is to a reverse strand i.e. start is
866    * greater than end point mapped to
867    */
868   @Test(groups = { "Functional" })
869   public void testFlattenRanges_reverseStrand()
870   {
871     assertEquals("[4, 3, 2, 1]",
872             Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 1 })));
873     assertEquals(
874             "[4, 3, 2, 1]",
875             Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 3, 2,
876                 1 })));
877     assertEquals(
878             "[4, 3, 2, 1]",
879             Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 4, 3,
880                 3, 2, 2, 1, 1 })));
881     assertEquals(
882             "[12, 9, 8, 7, 4, 3, 2, 1]",
883             Arrays.toString(MappingUtils.flattenRanges(new int[] { 12, 12,
884                 9, 7, 4, 1 })));
885     // forwards and backwards anyone?
886     assertEquals(
887             "[4, 5, 6, 3, 2, 1]",
888             Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 6, 3,
889                 1 })));
890     // backwards and forwards
891     assertEquals(
892             "[3, 2, 1, 4, 5, 6]",
893             Arrays.toString(MappingUtils.flattenRanges(new int[] { 3, 1, 4,
894                 6 })));
895     // trailing unpaired start position is ignored:
896     assertEquals(
897             "[12, 9, 8, 7, 4, 3, 2]",
898             Arrays.toString(MappingUtils.flattenRanges(new int[] { 12, 12,
899                 9, 7, 4, 2, 1 })));
900   }
901
902   /**
903    * Test mapping a column selection including hidden columns
904    * 
905    * @throws IOException
906    */
907   @Test(groups = { "Functional" })
908   public void testMapColumnSelection_hiddenColumns() throws IOException
909   {
910     setupMappedAlignments();
911
912     ColumnSelection proteinSelection = new ColumnSelection();
913     HiddenColumns hiddenCols = new HiddenColumns();
914
915     /*
916      * Column 0 in protein picks up Seq2/L, Seq3/G which map to cols 0-4 and 0-3
917      * in dna respectively, overall 0-4
918      */
919     proteinSelection.hideSelectedColumns(0, hiddenCols);
920     ColumnSelection dnaSelection = new ColumnSelection();
921     HiddenColumns dnaHidden = new HiddenColumns();
922     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
923             proteinView, dnaView, dnaSelection, dnaHidden);
924     assertEquals("[]", dnaSelection.getSelected().toString());
925     Iterator<int[]> regions = dnaHidden.iterator();
926     assertEquals(1, dnaHidden.getNumberOfRegions());
927     assertEquals("[0, 4]", Arrays.toString(regions.next()));
928
929     /*
930      * Column 1 in protein picks up Seq1/K which maps to cols 0-3 in dna
931      */
932     dnaSelection = new ColumnSelection();
933     dnaHidden = new HiddenColumns();
934     hiddenCols.revealAllHiddenColumns(proteinSelection);
935     // the unhidden columns are now marked selected!
936     assertEquals("[0]", proteinSelection.getSelected().toString());
937     // deselect these or hideColumns will be expanded to include 0
938     proteinSelection.clear();
939     proteinSelection.hideSelectedColumns(1, hiddenCols);
940     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
941             proteinView, dnaView, dnaSelection, dnaHidden);
942     regions = dnaHidden.iterator();
943     assertEquals(1, dnaHidden.getNumberOfRegions());
944     assertEquals("[0, 3]", Arrays.toString(regions.next()));
945
946     /*
947      * Column 2 in protein picks up gaps only - no mapping
948      */
949     dnaSelection = new ColumnSelection();
950     dnaHidden = new HiddenColumns();
951     hiddenCols.revealAllHiddenColumns(proteinSelection);
952     proteinSelection.clear();
953     proteinSelection.hideSelectedColumns(2, hiddenCols);
954     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
955             proteinView, dnaView, dnaSelection, dnaHidden);
956     assertEquals(0, dnaHidden.getNumberOfRegions());
957
958     /*
959      * Column 3 in protein picks up Seq1/P, Seq2/Q, Seq3/S which map to columns
960      * 6-9, 6-10, 5-8 respectively, overall to 5-10
961      */
962     dnaSelection = new ColumnSelection();
963     dnaHidden = new HiddenColumns();
964     hiddenCols.revealAllHiddenColumns(proteinSelection);
965     proteinSelection.clear();
966     proteinSelection.hideSelectedColumns(3, hiddenCols); // 5-10 hidden in dna
967     proteinSelection.addElement(1); // 0-3 selected in dna
968     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
969             proteinView, dnaView, dnaSelection, dnaHidden);
970     assertEquals("[0, 1, 2, 3]", dnaSelection.getSelected().toString());
971     regions = dnaHidden.iterator();
972     assertEquals(1, dnaHidden.getNumberOfRegions());
973     assertEquals("[5, 10]", Arrays.toString(regions.next()));
974
975     /*
976      * Combine hiding columns 1 and 3 to get discontiguous hidden columns
977      */
978     dnaSelection = new ColumnSelection();
979     dnaHidden = new HiddenColumns();
980     hiddenCols.revealAllHiddenColumns(proteinSelection);
981     proteinSelection.clear();
982     proteinSelection.hideSelectedColumns(1, hiddenCols);
983     proteinSelection.hideSelectedColumns(3, hiddenCols);
984     MappingUtils.mapColumnSelection(proteinSelection, hiddenCols,
985             proteinView, dnaView, dnaSelection, dnaHidden);
986     regions = dnaHidden.iterator();
987     assertEquals(2, dnaHidden.getNumberOfRegions());
988     assertEquals("[0, 3]", Arrays.toString(regions.next()));
989     assertEquals("[5, 10]", Arrays.toString(regions.next()));
990   }
991
992   @Test(groups = { "Functional" })
993   public void testGetLength()
994   {
995     assertEquals(0, MappingUtils.getLength(null));
996
997     /*
998      * [start, end] ranges
999      */
1000     List<int[]> ranges = new ArrayList<>();
1001     assertEquals(0, MappingUtils.getLength(ranges));
1002     ranges.add(new int[] { 1, 1 });
1003     assertEquals(1, MappingUtils.getLength(ranges));
1004     ranges.add(new int[] { 2, 10 });
1005     assertEquals(10, MappingUtils.getLength(ranges));
1006     ranges.add(new int[] { 20, 10 });
1007     assertEquals(21, MappingUtils.getLength(ranges));
1008
1009     /*
1010      * [start, end, start, end...] ranges
1011      */
1012     ranges.clear();
1013     ranges.add(new int[] { 1, 5, 8, 4 });
1014     ranges.add(new int[] { 8, 2 });
1015     ranges.add(new int[] { 12, 12 });
1016     assertEquals(18, MappingUtils.getLength(ranges));
1017   }
1018
1019   @Test(groups = { "Functional" })
1020   public void testContains()
1021   {
1022     assertFalse(MappingUtils.contains(null, 1));
1023     List<int[]> ranges = new ArrayList<>();
1024     assertFalse(MappingUtils.contains(ranges, 1));
1025
1026     ranges.add(new int[] { 1, 4 });
1027     ranges.add(new int[] { 6, 6 });
1028     ranges.add(new int[] { 8, 10 });
1029     ranges.add(new int[] { 30, 20 });
1030     ranges.add(new int[] { -16, -44 });
1031
1032     assertFalse(MappingUtils.contains(ranges, 0));
1033     assertTrue(MappingUtils.contains(ranges, 1));
1034     assertTrue(MappingUtils.contains(ranges, 2));
1035     assertTrue(MappingUtils.contains(ranges, 3));
1036     assertTrue(MappingUtils.contains(ranges, 4));
1037     assertFalse(MappingUtils.contains(ranges, 5));
1038
1039     assertTrue(MappingUtils.contains(ranges, 6));
1040     assertFalse(MappingUtils.contains(ranges, 7));
1041
1042     assertTrue(MappingUtils.contains(ranges, 8));
1043     assertTrue(MappingUtils.contains(ranges, 9));
1044     assertTrue(MappingUtils.contains(ranges, 10));
1045
1046     assertFalse(MappingUtils.contains(ranges, 31));
1047     assertTrue(MappingUtils.contains(ranges, 30));
1048     assertTrue(MappingUtils.contains(ranges, 29));
1049     assertTrue(MappingUtils.contains(ranges, 20));
1050     assertFalse(MappingUtils.contains(ranges, 19));
1051
1052     assertFalse(MappingUtils.contains(ranges, -15));
1053     assertTrue(MappingUtils.contains(ranges, -16));
1054     assertTrue(MappingUtils.contains(ranges, -44));
1055     assertFalse(MappingUtils.contains(ranges, -45));
1056   }
1057
1058   /**
1059    * Test the method that drops positions from the start of a mapped range
1060    */
1061   @Test(groups = "Functional")
1062   public void testRemoveStartPositions()
1063   {
1064     int[] ranges = new int[] { 1, 10 };
1065     int[] adjusted = MappingUtils.removeStartPositions(0, ranges);
1066     assertEquals("[1, 10]", Arrays.toString(adjusted));
1067
1068     adjusted = MappingUtils.removeStartPositions(1, ranges);
1069     assertEquals("[2, 10]", Arrays.toString(adjusted));
1070     assertEquals("[1, 10]", Arrays.toString(ranges));
1071
1072     ranges = adjusted;
1073     adjusted = MappingUtils.removeStartPositions(1, ranges);
1074     assertEquals("[3, 10]", Arrays.toString(adjusted));
1075     assertEquals("[2, 10]", Arrays.toString(ranges));
1076
1077     ranges = new int[] { 2, 3, 10, 12 };
1078     adjusted = MappingUtils.removeStartPositions(1, ranges);
1079     assertEquals("[3, 3, 10, 12]", Arrays.toString(adjusted));
1080     assertEquals("[2, 3, 10, 12]", Arrays.toString(ranges));
1081
1082     ranges = new int[] { 2, 2, 8, 12 };
1083     adjusted = MappingUtils.removeStartPositions(1, ranges);
1084     assertEquals("[8, 12]", Arrays.toString(adjusted));
1085     assertEquals("[2, 2, 8, 12]", Arrays.toString(ranges));
1086
1087     ranges = new int[] { 2, 2, 8, 12 };
1088     adjusted = MappingUtils.removeStartPositions(2, ranges);
1089     assertEquals("[9, 12]", Arrays.toString(adjusted));
1090     assertEquals("[2, 2, 8, 12]", Arrays.toString(ranges));
1091
1092     ranges = new int[] { 2, 2, 4, 4, 9, 12 };
1093     adjusted = MappingUtils.removeStartPositions(1, ranges);
1094     assertEquals("[4, 4, 9, 12]", Arrays.toString(adjusted));
1095     assertEquals("[2, 2, 4, 4, 9, 12]", Arrays.toString(ranges));
1096
1097     ranges = new int[] { 2, 2, 4, 4, 9, 12 };
1098     adjusted = MappingUtils.removeStartPositions(2, ranges);
1099     assertEquals("[9, 12]", Arrays.toString(adjusted));
1100     assertEquals("[2, 2, 4, 4, 9, 12]", Arrays.toString(ranges));
1101
1102     ranges = new int[] { 2, 3, 9, 12 };
1103     adjusted = MappingUtils.removeStartPositions(3, ranges);
1104     assertEquals("[10, 12]", Arrays.toString(adjusted));
1105     assertEquals("[2, 3, 9, 12]", Arrays.toString(ranges));
1106   }
1107
1108   /**
1109    * Test the method that drops positions from the start of a mapped range, on
1110    * the reverse strand
1111    */
1112   @Test(groups = "Functional")
1113   public void testRemoveStartPositions_reverseStrand()
1114   {
1115     int[] ranges = new int[] { 10, 1 };
1116     int[] adjusted = MappingUtils.removeStartPositions(0, ranges);
1117     assertEquals("[10, 1]", Arrays.toString(adjusted));
1118     assertEquals("[10, 1]", Arrays.toString(ranges));
1119
1120     ranges = adjusted;
1121     adjusted = MappingUtils.removeStartPositions(1, ranges);
1122     assertEquals("[9, 1]", Arrays.toString(adjusted));
1123     assertEquals("[10, 1]", Arrays.toString(ranges));
1124
1125     ranges = adjusted;
1126     adjusted = MappingUtils.removeStartPositions(1, ranges);
1127     assertEquals("[8, 1]", Arrays.toString(adjusted));
1128     assertEquals("[9, 1]", Arrays.toString(ranges));
1129
1130     ranges = new int[] { 12, 11, 9, 6 };
1131     adjusted = MappingUtils.removeStartPositions(1, ranges);
1132     assertEquals("[11, 11, 9, 6]", Arrays.toString(adjusted));
1133     assertEquals("[12, 11, 9, 6]", Arrays.toString(ranges));
1134
1135     ranges = new int[] { 12, 12, 8, 4 };
1136     adjusted = MappingUtils.removeStartPositions(1, ranges);
1137     assertEquals("[8, 4]", Arrays.toString(adjusted));
1138     assertEquals("[12, 12, 8, 4]", Arrays.toString(ranges));
1139
1140     ranges = new int[] { 12, 12, 8, 4 };
1141     adjusted = MappingUtils.removeStartPositions(2, ranges);
1142     assertEquals("[7, 4]", Arrays.toString(adjusted));
1143     assertEquals("[12, 12, 8, 4]", Arrays.toString(ranges));
1144
1145     ranges = new int[] { 12, 12, 10, 10, 8, 4 };
1146     adjusted = MappingUtils.removeStartPositions(1, ranges);
1147     assertEquals("[10, 10, 8, 4]", Arrays.toString(adjusted));
1148     assertEquals("[12, 12, 10, 10, 8, 4]", Arrays.toString(ranges));
1149
1150     ranges = new int[] { 12, 12, 10, 10, 8, 4 };
1151     adjusted = MappingUtils.removeStartPositions(2, ranges);
1152     assertEquals("[8, 4]", Arrays.toString(adjusted));
1153     assertEquals("[12, 12, 10, 10, 8, 4]", Arrays.toString(ranges));
1154
1155     ranges = new int[] { 12, 11, 8, 4 };
1156     adjusted = MappingUtils.removeStartPositions(3, ranges);
1157     assertEquals("[7, 4]", Arrays.toString(adjusted));
1158     assertEquals("[12, 11, 8, 4]", Arrays.toString(ranges));
1159   }
1160
1161   @Test(groups = { "Functional" })
1162   public void testRangeContains()
1163   {
1164     /*
1165      * both forward ranges
1166      */
1167     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1168         1, 10 }));
1169     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1170         2, 10 }));
1171     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1172         1, 9 }));
1173     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1174         4, 5 }));
1175     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1176         0, 9 }));
1177     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1178         -10, -9 }));
1179     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1180         1, 11 }));
1181     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1182         11, 12 }));
1183
1184     /*
1185      * forward range, reverse query
1186      */
1187     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1188         10, 1 }));
1189     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1190         9, 1 }));
1191     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1192         10, 2 }));
1193     assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1194         5, 5 }));
1195     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1196         11, 1 }));
1197     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
1198         10, 0 }));
1199
1200     /*
1201      * reverse range, forward query
1202      */
1203     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1204         1, 10 }));
1205     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1206         1, 9 }));
1207     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1208         2, 10 }));
1209     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1210         6, 6 }));
1211     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1212         6, 11 }));
1213     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1214         11, 20 }));
1215     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1216         -3, -2 }));
1217
1218     /*
1219      * both reverse
1220      */
1221     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1222         10, 1 }));
1223     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1224         9, 1 }));
1225     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1226         10, 2 }));
1227     assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1228         3, 3 }));
1229     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1230         11, 1 }));
1231     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1232         10, 0 }));
1233     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1234         12, 11 }));
1235     assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
1236         -5, -8 }));
1237
1238     /*
1239      * bad arguments
1240      */
1241     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10, 12 },
1242             new int[] {
1243         1, 10 }));
1244     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 },
1245             new int[] { 1 }));
1246     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, null));
1247     assertFalse(MappingUtils.rangeContains(null, new int[] { 1, 10 }));
1248   }
1249
1250   @Test(groups = "Functional")
1251   public void testRemoveEndPositions()
1252   {
1253     List<int[]> ranges = new ArrayList<>();
1254
1255     /*
1256      * case 1: truncate last range
1257      */
1258     ranges.add(new int[] { 1, 10 });
1259     ranges.add(new int[] { 20, 30 });
1260     MappingUtils.removeEndPositions(5, ranges);
1261     assertEquals(2, ranges.size());
1262     assertEquals(25, ranges.get(1)[1]);
1263
1264     /*
1265      * case 2: remove last range
1266      */
1267     ranges.clear();
1268     ranges.add(new int[] { 1, 10 });
1269     ranges.add(new int[] { 20, 22 });
1270     MappingUtils.removeEndPositions(3, ranges);
1271     assertEquals(1, ranges.size());
1272     assertEquals(10, ranges.get(0)[1]);
1273
1274     /*
1275      * case 3: truncate penultimate range
1276      */
1277     ranges.clear();
1278     ranges.add(new int[] { 1, 10 });
1279     ranges.add(new int[] { 20, 21 });
1280     MappingUtils.removeEndPositions(3, ranges);
1281     assertEquals(1, ranges.size());
1282     assertEquals(9, ranges.get(0)[1]);
1283
1284     /*
1285      * case 4: remove last two ranges
1286      */
1287     ranges.clear();
1288     ranges.add(new int[] { 1, 10 });
1289     ranges.add(new int[] { 20, 20 });
1290     ranges.add(new int[] { 30, 30 });
1291     MappingUtils.removeEndPositions(3, ranges);
1292     assertEquals(1, ranges.size());
1293     assertEquals(9, ranges.get(0)[1]);
1294   }
1295   
1296   @Test(groups = "Functional")
1297   public void testListToArray()
1298   {
1299     List<int[]> ranges = new ArrayList<>();
1300     
1301     int[] result = MappingUtils.listToArray(ranges);
1302     assertEquals(result.length, 0);
1303     ranges.add(new int[] {24, 12});
1304     result = MappingUtils.listToArray(ranges);
1305     assertEquals(result.length, 2);
1306     assertEquals(result[0], 24);
1307     assertEquals(result[1], 12);
1308     ranges.add(new int[] {-7, 30});
1309     result = MappingUtils.listToArray(ranges);
1310     assertEquals(result.length, 4);
1311     assertEquals(result[0], 24);
1312     assertEquals(result[1], 12);
1313     assertEquals(result[2], -7);
1314     assertEquals(result[3], 30);
1315     try
1316     {
1317       MappingUtils.listToArray(null);
1318       fail("Expected exception");
1319     } catch (NullPointerException e)
1320     {
1321       // expected
1322     }
1323   }
1324 }