JAL-192 ‘efficiently’ locate visible bounds for a sequence (needs scalability testing)
[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     assertEquals(Arrays.toString(new int[] { seq.findIndex(seq.getStart()),
106         seq.findIndex(seq.getEnd()), seq.getStart(), seq.getEnd() }),
107             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
108     cs.hideColumns(1, 3);
109     cs.hideColumns(6, 11);
110     assertEquals("-D",
111             cs.getVisibleSequenceStrings(0, 5, new SequenceI[] { seq })[0]);
112     assertEquals(Arrays.toString(new int[] { 1, 1, 3, 3 }),
113             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
114   }
115
116   @Test(groups = { "Functional" })
117   public void testHideColumns()
118   {
119     ColumnSelection cs = new ColumnSelection();
120     cs.hideColumns(5);
121     List<int[]> hidden = cs.getHiddenColumns();
122     assertEquals(1, hidden.size());
123     assertEquals("[5, 5]", Arrays.toString(hidden.get(0)));
124
125     cs.hideColumns(3);
126     assertEquals(2, hidden.size());
127     // two hidden ranges, in order:
128     assertSame(hidden, cs.getHiddenColumns());
129     assertEquals("[3, 3]", Arrays.toString(hidden.get(0)));
130     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
131
132     // hiding column 4 expands [3, 3] to [3, 4]
133     // not fancy enough to coalesce this into [3, 5] though
134     cs.hideColumns(4);
135     hidden = cs.getHiddenColumns();
136     assertEquals(2, hidden.size());
137     assertEquals("[3, 4]", Arrays.toString(hidden.get(0)));
138     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
139
140     // clear hidden columns (note they are added to selected)
141     cs.revealAllHiddenColumns();
142     // it is now actually null but getter returns an empty list
143     assertTrue(cs.getHiddenColumns().isEmpty());
144
145     cs.hideColumns(3, 6);
146     hidden = cs.getHiddenColumns();
147     int[] firstHiddenRange = hidden.get(0);
148     assertEquals("[3, 6]", Arrays.toString(firstHiddenRange));
149
150     // adding a subrange of already hidden should do nothing
151     cs.hideColumns(4, 5);
152     assertEquals(1, hidden.size());
153     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
154     cs.hideColumns(3, 5);
155     assertEquals(1, hidden.size());
156     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
157     cs.hideColumns(4, 6);
158     assertEquals(1, hidden.size());
159     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
160     cs.hideColumns(3, 6);
161     assertEquals(1, hidden.size());
162     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
163
164     cs.revealAllHiddenColumns();
165     cs.hideColumns(2, 4);
166     hidden = cs.getHiddenColumns();
167     assertEquals(1, hidden.size());
168     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
169
170     // extend contiguous with 2 positions overlap
171     cs.hideColumns(3, 5);
172     assertEquals(1, hidden.size());
173     assertEquals("[2, 5]", Arrays.toString(hidden.get(0)));
174
175     // extend contiguous with 1 position overlap
176     cs.hideColumns(5, 6);
177     assertEquals(1, hidden.size());
178     assertEquals("[2, 6]", Arrays.toString(hidden.get(0)));
179
180     // extend contiguous with overlap both ends:
181     cs.hideColumns(1, 7);
182     assertEquals(1, hidden.size());
183     assertEquals("[1, 7]", Arrays.toString(hidden.get(0)));
184   }
185
186   /**
187    * Test the method that hides a specified column including any adjacent
188    * selected columns. This is a convenience method for the case where multiple
189    * column regions are selected and then hidden using menu option View | Hide |
190    * Selected Columns.
191    */
192   @Test(groups = { "Functional" })
193   public void testHideColumns_withSelection()
194   {
195     ColumnSelection cs = new ColumnSelection();
196     // select columns 4-6
197     cs.addElement(4);
198     cs.addElement(5);
199     cs.addElement(6);
200     // hide column 5 (and adjacent):
201     cs.hideColumns(5);
202     // 4,5,6 now hidden:
203     List<int[]> hidden = cs.getHiddenColumns();
204     assertEquals(1, hidden.size());
205     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
206     // none now selected:
207     assertTrue(cs.getSelected().isEmpty());
208
209     // repeat, hiding column 4 (5 and 6)
210     cs = new ColumnSelection();
211     cs.addElement(4);
212     cs.addElement(5);
213     cs.addElement(6);
214     cs.hideColumns(4);
215     hidden = cs.getHiddenColumns();
216     assertEquals(1, hidden.size());
217     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
218     assertTrue(cs.getSelected().isEmpty());
219
220     // repeat, hiding column (4, 5 and) 6
221     cs = new ColumnSelection();
222     cs.addElement(4);
223     cs.addElement(5);
224     cs.addElement(6);
225     cs.hideColumns(6);
226     hidden = cs.getHiddenColumns();
227     assertEquals(1, hidden.size());
228     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
229     assertTrue(cs.getSelected().isEmpty());
230
231     // repeat, with _only_ adjacent columns selected
232     cs = new ColumnSelection();
233     cs.addElement(4);
234     cs.addElement(6);
235     cs.hideColumns(5);
236     hidden = cs.getHiddenColumns();
237     assertEquals(1, hidden.size());
238     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
239     assertTrue(cs.getSelected().isEmpty());
240   }
241
242   /**
243    * Test the method that hides all (possibly disjoint) selected column ranges
244    */
245   @Test(groups = { "Functional" })
246   public void testHideSelectedColumns()
247   {
248     ColumnSelection cs = new ColumnSelection();
249     int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
250     for (int col : sel)
251     {
252       cs.addElement(col);
253     }
254     cs.hideColumns(15, 18);
255
256     cs.hideSelectedColumns();
257     assertTrue(cs.getSelected().isEmpty());
258     List<int[]> hidden = cs.getHiddenColumns();
259     assertEquals(4, hidden.size());
260     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
261     assertEquals("[7, 9]", Arrays.toString(hidden.get(1)));
262     assertEquals("[15, 18]", Arrays.toString(hidden.get(2)));
263     assertEquals("[20, 22]", Arrays.toString(hidden.get(3)));
264   }
265
266   /**
267    * Test the method that gets runs of selected columns ordered by column. If
268    * this fails, HideSelectedColumns may also fail
269    */
270   @Test(groups = { "Functional" })
271   public void testgetSelectedRanges()
272   {
273     ColumnSelection cs = new ColumnSelection();
274     int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
275     for (int col : sel)
276     {
277       cs.addElement(col);
278     }
279     List<int[]> range;
280     range = cs.getSelectedRanges();
281     assertEquals(3, range.size());
282     assertEquals("[2, 4]", Arrays.toString(range.get(0)));
283     assertEquals("[7, 9]", Arrays.toString(range.get(1)));
284     assertEquals("[20, 22]", Arrays.toString(range.get(2)));
285     cs.addElement(0);
286     cs.addElement(1);
287     range = cs.getSelectedRanges();
288     assertEquals(3, range.size());
289     assertEquals("[0, 4]", Arrays.toString(range.get(0)));
290   }
291
292   /**
293    * Test the method that reveals a range of hidden columns given the start
294    * column of the range
295    */
296   @Test(groups = { "Functional" })
297   public void testRevealHiddenColumns()
298   {
299     ColumnSelection cs = new ColumnSelection();
300     cs.hideColumns(5, 8);
301     cs.addElement(10);
302     cs.revealHiddenColumns(5);
303     // hidden columns list now null but getter returns empty list:
304     assertTrue(cs.getHiddenColumns().isEmpty());
305     // revealed columns are marked as selected (added to selection):
306     assertEquals("[10, 5, 6, 7, 8]", cs.getSelected().toString());
307
308     // calling with a column other than the range start does nothing:
309     cs = new ColumnSelection();
310     cs.hideColumns(5, 8);
311     List<int[]> hidden = cs.getHiddenColumns();
312     cs.revealHiddenColumns(6);
313     assertSame(hidden, cs.getHiddenColumns());
314     assertTrue(cs.getSelected().isEmpty());
315   }
316
317   @Test(groups = { "Functional" })
318   public void testRevealAllHiddenColumns()
319   {
320     ColumnSelection cs = new ColumnSelection();
321     cs.hideColumns(5, 8);
322     cs.hideColumns(2, 3);
323     cs.addElement(11);
324     cs.addElement(1);
325     cs.revealAllHiddenColumns();
326
327     /*
328      * revealing hidden columns adds them (in order) to the (unordered)
329      * selection list
330      */
331     assertTrue(cs.getHiddenColumns().isEmpty());
332     assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", cs.getSelected().toString());
333   }
334
335   @Test(groups = { "Functional" })
336   public void testIsVisible()
337   {
338     ColumnSelection cs = new ColumnSelection();
339     cs.hideColumns(2, 4);
340     cs.hideColumns(6, 7);
341     assertTrue(cs.isVisible(0));
342     assertTrue(cs.isVisible(-99));
343     assertTrue(cs.isVisible(1));
344     assertFalse(cs.isVisible(2));
345     assertFalse(cs.isVisible(3));
346     assertFalse(cs.isVisible(4));
347     assertTrue(cs.isVisible(5));
348     assertFalse(cs.isVisible(6));
349     assertFalse(cs.isVisible(7));
350   }
351
352   @Test(groups = { "Functional" })
353   public void testGetVisibleContigs()
354   {
355     ColumnSelection cs = new ColumnSelection();
356     cs.hideColumns(3, 6);
357     cs.hideColumns(8, 9);
358     cs.hideColumns(12, 12);
359
360     // start position is inclusive, end position exclusive:
361     int[] visible = cs.getVisibleContigs(1, 13);
362     assertEquals("[1, 2, 7, 7, 10, 11]", Arrays.toString(visible));
363
364     visible = cs.getVisibleContigs(4, 14);
365     assertEquals("[7, 7, 10, 11, 13, 13]", Arrays.toString(visible));
366
367     visible = cs.getVisibleContigs(3, 10);
368     assertEquals("[7, 7]", Arrays.toString(visible));
369
370     visible = cs.getVisibleContigs(4, 6);
371     assertEquals("[]", Arrays.toString(visible));
372   }
373
374   @Test(groups = { "Functional" })
375   public void testInvertColumnSelection()
376   {
377     ColumnSelection cs = new ColumnSelection();
378     cs.addElement(4);
379     cs.addElement(6);
380     cs.addElement(8);
381     cs.hideColumns(3, 3);
382     cs.hideColumns(6, 6);
383
384     // invert selection from start (inclusive) to end (exclusive)
385     // hidden columns are _not_ changed
386     cs.invertColumnSelection(2, 9);
387     assertEquals("[2, 5, 7]", cs.getSelected().toString());
388
389     cs.invertColumnSelection(1, 9);
390     assertEquals("[1, 4, 8]", cs.getSelected().toString());
391   }
392
393   @Test(groups = { "Functional" })
394   public void testMaxColumnSelection()
395   {
396     ColumnSelection cs = new ColumnSelection();
397     cs.addElement(0);
398     cs.addElement(513);
399     cs.addElement(1);
400     assertEquals(513, cs.getMax());
401     cs.removeElement(513);
402     assertEquals(1, cs.getMax());
403     cs.removeElement(1);
404     assertEquals(0, cs.getMax());
405     cs.addElement(512);
406     cs.addElement(513);
407     assertEquals(513, cs.getMax());
408
409   }
410
411   @Test(groups = { "Functional" })
412   public void testMinColumnSelection()
413   {
414     ColumnSelection cs = new ColumnSelection();
415     cs.addElement(0);
416     cs.addElement(513);
417     cs.addElement(1);
418     assertEquals(0, cs.getMin());
419     cs.removeElement(0);
420     assertEquals(1, cs.getMin());
421     cs.addElement(0);
422     assertEquals(0, cs.getMin());
423   }
424 }