JAL-192 patch for off-by one and pathological case (hide inserted regions + set refer...
[jalview.git] / test / jalview / datamodel / ColumnSelectionTest.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.assertSame;
26 import static org.testng.AssertJUnit.assertTrue;
27
28 import java.util.Arrays;
29 import java.util.List;
30
31 import org.testng.annotations.Test;
32
33 public class ColumnSelectionTest
34 {
35
36   @Test(groups = { "Functional" })
37   public void testAddElement()
38   {
39     ColumnSelection cs = new ColumnSelection();
40     cs.addElement(2);
41     cs.addElement(5);
42     cs.addElement(3);
43     cs.addElement(5); // ignored
44     List<Integer> sel = cs.getSelected();
45     assertEquals("[2, 5, 3]", sel.toString());
46   }
47
48   /**
49    * Test the remove method - in particular to verify that remove(int i) removes
50    * the element whose value is i, _NOT_ the i'th element.
51    */
52   @Test(groups = { "Functional" })
53   public void testRemoveElement()
54   {
55     ColumnSelection cs = new ColumnSelection();
56     cs.addElement(2);
57     cs.addElement(5);
58
59     // removing elements not in the list has no effect
60     cs.removeElement(0);
61     cs.removeElement(1);
62     List<Integer> sel = cs.getSelected();
63     assertEquals(2, sel.size());
64     assertEquals(new Integer(2), sel.get(0));
65     assertEquals(new Integer(5), sel.get(1));
66
67     // removing an element in the list removes it
68     cs.removeElement(2);
69     assertEquals(1, sel.size());
70     assertEquals(new Integer(5), sel.get(0));
71   }
72
73   /**
74    * Test the method that finds the visible column position of an alignment
75    * column, allowing for hidden columns.
76    */
77   @Test(groups = { "Functional" })
78   public void testFindColumnPosition()
79   {
80     ColumnSelection cs = new ColumnSelection();
81     assertEquals(5, cs.findColumnPosition(5));
82
83     // hiding column 6 makes no difference
84     cs.hideColumns(6, 6);
85     assertEquals(5, cs.findColumnPosition(5));
86
87     // hiding column 4 moves column 5 to column 4
88     cs.hideColumns(4, 4);
89     assertEquals(4, cs.findColumnPosition(5));
90
91     // hiding columns 1 and 2 moves column 5 to column 2
92     cs.hideColumns(1, 2);
93     assertEquals(2, cs.findColumnPosition(5));
94   }
95
96   /**
97    * Test the code used to locate the reference sequence ruler origin
98    */
99   @Test(groups = { "Functional" })
100   public void testLocateVisibleBoundsofSequence()
101   {
102     ColumnSelection cs = new ColumnSelection();
103     SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---");
104     assertEquals(2, seq.findIndex(seq.getStart()));
105
106     // no hidden columns
107     assertEquals(
108             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
109                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
110                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
111                 seq.findIndex(seq.getEnd()) - 1 }),
112             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
113
114     // hidden column on gap after end of sequence - should not affect bounds
115     cs.hideColumns(13);
116     assertEquals(
117             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
118                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
119                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
120                 seq.findIndex(seq.getEnd()) - 1 }),
121             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
122
123     cs.revealAllHiddenColumns();
124     // hidden column on gap before beginning of sequence - should vis bounds by
125     // one
126     cs.hideColumns(0);
127     assertEquals(
128             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 2,
129                 seq.findIndex(seq.getEnd()) - 2, seq.getStart(),
130                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
131                 seq.findIndex(seq.getEnd()) - 1 }),
132             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
133
134     cs.revealAllHiddenColumns();
135     // hide columns around most of sequence - leave one residue remaining
136     cs.hideColumns(1, 3);
137     cs.hideColumns(6, 11);
138     assertEquals("-D",
139             cs.getVisibleSequenceStrings(0, 5, new SequenceI[] { seq })[0]);
140     assertEquals(
141             Arrays.toString(new int[] { 1, 1, 3, 3,
142                 seq.findIndex(seq.getStart()) - 1,
143                 seq.findIndex(seq.getEnd()) - 1 }),
144             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
145     cs.revealAllHiddenColumns();
146
147     // hide whole sequence - should just get location of hidden region
148     // containing sequence
149     cs.hideColumns(1, 11);
150     assertEquals(
151             Arrays.toString(new int[] { 0, 1, 0, 0,
152                 seq.findIndex(seq.getStart()) - 1,
153                 seq.findIndex(seq.getEnd()) - 1 }),
154             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
155
156   }
157
158   @Test(groups={"Functional"})
159   public void testLocateVisibleBoundsPathologicals()
160   {
161     // test some pathological cases we missed
162     AlignmentI al = new Alignment(new SequenceI[] { new Sequence("refseqGaptest","KTDVTI----------NFI-----G----L")});
163     ColumnSelection cs = new ColumnSelection();
164     cs.hideInsertionsFor(al.getSequenceAt(0));
165     assertEquals(
166             "G",
167             ""
168                     + al.getSequenceAt(0).getCharAt(
169                             cs.adjustForHiddenColumns(9)));
170
171
172   }
173   @Test(groups = { "Functional" })
174   public void testHideColumns()
175   {
176     ColumnSelection cs = new ColumnSelection();
177     cs.hideColumns(5);
178     List<int[]> hidden = cs.getHiddenColumns();
179     assertEquals(1, hidden.size());
180     assertEquals("[5, 5]", Arrays.toString(hidden.get(0)));
181
182     cs.hideColumns(3);
183     assertEquals(2, hidden.size());
184     // two hidden ranges, in order:
185     assertSame(hidden, cs.getHiddenColumns());
186     assertEquals("[3, 3]", Arrays.toString(hidden.get(0)));
187     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
188
189     // hiding column 4 expands [3, 3] to [3, 4]
190     // not fancy enough to coalesce this into [3, 5] though
191     cs.hideColumns(4);
192     hidden = cs.getHiddenColumns();
193     assertEquals(2, hidden.size());
194     assertEquals("[3, 4]", Arrays.toString(hidden.get(0)));
195     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
196
197     // clear hidden columns (note they are added to selected)
198     cs.revealAllHiddenColumns();
199     // it is now actually null but getter returns an empty list
200     assertTrue(cs.getHiddenColumns().isEmpty());
201
202     cs.hideColumns(3, 6);
203     hidden = cs.getHiddenColumns();
204     int[] firstHiddenRange = hidden.get(0);
205     assertEquals("[3, 6]", Arrays.toString(firstHiddenRange));
206
207     // adding a subrange of already hidden should do nothing
208     cs.hideColumns(4, 5);
209     assertEquals(1, hidden.size());
210     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
211     cs.hideColumns(3, 5);
212     assertEquals(1, hidden.size());
213     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
214     cs.hideColumns(4, 6);
215     assertEquals(1, hidden.size());
216     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
217     cs.hideColumns(3, 6);
218     assertEquals(1, hidden.size());
219     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
220
221     cs.revealAllHiddenColumns();
222     cs.hideColumns(2, 4);
223     hidden = cs.getHiddenColumns();
224     assertEquals(1, hidden.size());
225     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
226
227     // extend contiguous with 2 positions overlap
228     cs.hideColumns(3, 5);
229     assertEquals(1, hidden.size());
230     assertEquals("[2, 5]", Arrays.toString(hidden.get(0)));
231
232     // extend contiguous with 1 position overlap
233     cs.hideColumns(5, 6);
234     assertEquals(1, hidden.size());
235     assertEquals("[2, 6]", Arrays.toString(hidden.get(0)));
236
237     // extend contiguous with overlap both ends:
238     cs.hideColumns(1, 7);
239     assertEquals(1, hidden.size());
240     assertEquals("[1, 7]", Arrays.toString(hidden.get(0)));
241   }
242
243   /**
244    * Test the method that hides a specified column including any adjacent
245    * selected columns. This is a convenience method for the case where multiple
246    * column regions are selected and then hidden using menu option View | Hide |
247    * Selected Columns.
248    */
249   @Test(groups = { "Functional" })
250   public void testHideColumns_withSelection()
251   {
252     ColumnSelection cs = new ColumnSelection();
253     // select columns 4-6
254     cs.addElement(4);
255     cs.addElement(5);
256     cs.addElement(6);
257     // hide column 5 (and adjacent):
258     cs.hideColumns(5);
259     // 4,5,6 now hidden:
260     List<int[]> hidden = cs.getHiddenColumns();
261     assertEquals(1, hidden.size());
262     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
263     // none now selected:
264     assertTrue(cs.getSelected().isEmpty());
265
266     // repeat, hiding column 4 (5 and 6)
267     cs = new ColumnSelection();
268     cs.addElement(4);
269     cs.addElement(5);
270     cs.addElement(6);
271     cs.hideColumns(4);
272     hidden = cs.getHiddenColumns();
273     assertEquals(1, hidden.size());
274     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
275     assertTrue(cs.getSelected().isEmpty());
276
277     // repeat, hiding column (4, 5 and) 6
278     cs = new ColumnSelection();
279     cs.addElement(4);
280     cs.addElement(5);
281     cs.addElement(6);
282     cs.hideColumns(6);
283     hidden = cs.getHiddenColumns();
284     assertEquals(1, hidden.size());
285     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
286     assertTrue(cs.getSelected().isEmpty());
287
288     // repeat, with _only_ adjacent columns selected
289     cs = new ColumnSelection();
290     cs.addElement(4);
291     cs.addElement(6);
292     cs.hideColumns(5);
293     hidden = cs.getHiddenColumns();
294     assertEquals(1, hidden.size());
295     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
296     assertTrue(cs.getSelected().isEmpty());
297   }
298
299   /**
300    * Test the method that hides all (possibly disjoint) selected column ranges
301    */
302   @Test(groups = { "Functional" })
303   public void testHideSelectedColumns()
304   {
305     ColumnSelection cs = new ColumnSelection();
306     int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
307     for (int col : sel)
308     {
309       cs.addElement(col);
310     }
311     cs.hideColumns(15, 18);
312
313     cs.hideSelectedColumns();
314     assertTrue(cs.getSelected().isEmpty());
315     List<int[]> hidden = cs.getHiddenColumns();
316     assertEquals(4, hidden.size());
317     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
318     assertEquals("[7, 9]", Arrays.toString(hidden.get(1)));
319     assertEquals("[15, 18]", Arrays.toString(hidden.get(2)));
320     assertEquals("[20, 22]", Arrays.toString(hidden.get(3)));
321   }
322
323   /**
324    * Test the method that gets runs of selected columns ordered by column. If
325    * this fails, HideSelectedColumns may also fail
326    */
327   @Test(groups = { "Functional" })
328   public void testgetSelectedRanges()
329   {
330     ColumnSelection cs = new ColumnSelection();
331     int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
332     for (int col : sel)
333     {
334       cs.addElement(col);
335     }
336     List<int[]> range;
337     range = cs.getSelectedRanges();
338     assertEquals(3, range.size());
339     assertEquals("[2, 4]", Arrays.toString(range.get(0)));
340     assertEquals("[7, 9]", Arrays.toString(range.get(1)));
341     assertEquals("[20, 22]", Arrays.toString(range.get(2)));
342     cs.addElement(0);
343     cs.addElement(1);
344     range = cs.getSelectedRanges();
345     assertEquals(3, range.size());
346     assertEquals("[0, 4]", Arrays.toString(range.get(0)));
347   }
348
349   /**
350    * Test the method that reveals a range of hidden columns given the start
351    * column of the range
352    */
353   @Test(groups = { "Functional" })
354   public void testRevealHiddenColumns()
355   {
356     ColumnSelection cs = new ColumnSelection();
357     cs.hideColumns(5, 8);
358     cs.addElement(10);
359     cs.revealHiddenColumns(5);
360     // hidden columns list now null but getter returns empty list:
361     assertTrue(cs.getHiddenColumns().isEmpty());
362     // revealed columns are marked as selected (added to selection):
363     assertEquals("[10, 5, 6, 7, 8]", cs.getSelected().toString());
364
365     // calling with a column other than the range start does nothing:
366     cs = new ColumnSelection();
367     cs.hideColumns(5, 8);
368     List<int[]> hidden = cs.getHiddenColumns();
369     cs.revealHiddenColumns(6);
370     assertSame(hidden, cs.getHiddenColumns());
371     assertTrue(cs.getSelected().isEmpty());
372   }
373
374   @Test(groups = { "Functional" })
375   public void testRevealAllHiddenColumns()
376   {
377     ColumnSelection cs = new ColumnSelection();
378     cs.hideColumns(5, 8);
379     cs.hideColumns(2, 3);
380     cs.addElement(11);
381     cs.addElement(1);
382     cs.revealAllHiddenColumns();
383
384     /*
385      * revealing hidden columns adds them (in order) to the (unordered)
386      * selection list
387      */
388     assertTrue(cs.getHiddenColumns().isEmpty());
389     assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", cs.getSelected().toString());
390   }
391
392   @Test(groups = { "Functional" })
393   public void testIsVisible()
394   {
395     ColumnSelection cs = new ColumnSelection();
396     cs.hideColumns(2, 4);
397     cs.hideColumns(6, 7);
398     assertTrue(cs.isVisible(0));
399     assertTrue(cs.isVisible(-99));
400     assertTrue(cs.isVisible(1));
401     assertFalse(cs.isVisible(2));
402     assertFalse(cs.isVisible(3));
403     assertFalse(cs.isVisible(4));
404     assertTrue(cs.isVisible(5));
405     assertFalse(cs.isVisible(6));
406     assertFalse(cs.isVisible(7));
407   }
408
409   @Test(groups = { "Functional" })
410   public void testGetVisibleContigs()
411   {
412     ColumnSelection cs = new ColumnSelection();
413     cs.hideColumns(3, 6);
414     cs.hideColumns(8, 9);
415     cs.hideColumns(12, 12);
416
417     // start position is inclusive, end position exclusive:
418     int[] visible = cs.getVisibleContigs(1, 13);
419     assertEquals("[1, 2, 7, 7, 10, 11]", Arrays.toString(visible));
420
421     visible = cs.getVisibleContigs(4, 14);
422     assertEquals("[7, 7, 10, 11, 13, 13]", Arrays.toString(visible));
423
424     visible = cs.getVisibleContigs(3, 10);
425     assertEquals("[7, 7]", Arrays.toString(visible));
426
427     visible = cs.getVisibleContigs(4, 6);
428     assertEquals("[]", Arrays.toString(visible));
429   }
430
431   @Test(groups = { "Functional" })
432   public void testInvertColumnSelection()
433   {
434     ColumnSelection cs = new ColumnSelection();
435     cs.addElement(4);
436     cs.addElement(6);
437     cs.addElement(8);
438     cs.hideColumns(3, 3);
439     cs.hideColumns(6, 6);
440
441     // invert selection from start (inclusive) to end (exclusive)
442     // hidden columns are _not_ changed
443     cs.invertColumnSelection(2, 9);
444     assertEquals("[2, 5, 7]", cs.getSelected().toString());
445
446     cs.invertColumnSelection(1, 9);
447     assertEquals("[1, 4, 8]", cs.getSelected().toString());
448   }
449
450   @Test(groups = { "Functional" })
451   public void testMaxColumnSelection()
452   {
453     ColumnSelection cs = new ColumnSelection();
454     cs.addElement(0);
455     cs.addElement(513);
456     cs.addElement(1);
457     assertEquals(513, cs.getMax());
458     cs.removeElement(513);
459     assertEquals(1, cs.getMax());
460     cs.removeElement(1);
461     assertEquals(0, cs.getMax());
462     cs.addElement(512);
463     cs.addElement(513);
464     assertEquals(513, cs.getMax());
465
466   }
467
468   @Test(groups = { "Functional" })
469   public void testMinColumnSelection()
470   {
471     ColumnSelection cs = new ColumnSelection();
472     cs.addElement(0);
473     cs.addElement(513);
474     cs.addElement(1);
475     assertEquals(0, cs.getMin());
476     cs.removeElement(0);
477     assertEquals(1, cs.getMin());
478     cs.addElement(0);
479     assertEquals(0, cs.getMin());
480   }
481 }