JAL-2291 method taking a BitSet for hiding columns
[jalview.git] / test / jalview / datamodel / HiddenColumnsTest.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 jalview.analysis.AlignmentGenerator;
29 import jalview.gui.JvOptionPane;
30
31 import java.util.Arrays;
32 import java.util.BitSet;
33 import java.util.List;
34
35 import org.testng.annotations.BeforeClass;
36 import org.testng.annotations.Test;
37
38 public class HiddenColumnsTest
39 {
40
41   @BeforeClass(alwaysRun = true)
42   public void setUpJvOptionPane()
43   {
44     JvOptionPane.setInteractiveMode(false);
45     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
46   }
47
48   /**
49    * Test the method which counts the number of hidden columns
50    */
51   @Test(groups = { "Functional" })
52   public void testGetSize()
53   {
54     HiddenColumns hidden = new HiddenColumns();
55     assertEquals(0, hidden.getSize());
56
57     hidden.hideColumns(3, 5);
58     assertEquals(3, hidden.getSize());
59
60     hidden.hideColumns(8, 8);
61     assertEquals(4, hidden.getSize());
62
63     hidden.hideColumns(9, 14);
64     assertEquals(10, hidden.getSize());
65
66     ColumnSelection cs = new ColumnSelection();
67     hidden.revealAllHiddenColumns(cs);
68     assertEquals(0, hidden.getSize());
69   }
70
71   /**
72    * Test the method that finds the visible column position of an alignment
73    * column, allowing for hidden columns.
74    */
75   @Test(groups = { "Functional" })
76   public void testFindColumnPosition()
77   {
78     HiddenColumns cs = new HiddenColumns();
79     assertEquals(5, cs.findColumnPosition(5));
80
81     // hiding column 6 makes no difference
82     cs.hideColumns(6, 6);
83     assertEquals(5, cs.findColumnPosition(5));
84
85     // hiding column 4 moves column 5 to column 4
86     cs.hideColumns(4, 4);
87     assertEquals(4, cs.findColumnPosition(5));
88
89     // hiding column 4 moves column 4 to position 3
90     assertEquals(3, cs.findColumnPosition(4));
91
92     // hiding columns 1 and 2 moves column 5 to column 2
93     cs.hideColumns(1, 2);
94     assertEquals(2, cs.findColumnPosition(5));
95
96     // check with > 1 hidden column regions
97     // where some columns are in the hidden regions
98     HiddenColumns cs2 = new HiddenColumns();
99     cs2.hideColumns(5, 10);
100     cs2.hideColumns(20, 27);
101     cs2.hideColumns(40, 44);
102
103     // hiding columns 5-10 and 20-27 moves column 8 to column 4
104     assertEquals(4, cs2.findColumnPosition(8));
105
106     // and moves column 24 to 13
107     assertEquals(13, cs2.findColumnPosition(24));
108
109     // and moves column 28 to 14
110     assertEquals(14, cs2.findColumnPosition(28));
111
112     // and moves column 40 to 25
113     assertEquals(25, cs2.findColumnPosition(40));
114
115     // check when hidden columns start at 0 that the visible column
116     // is returned as 0
117     HiddenColumns cs3 = new HiddenColumns();
118     cs3.hideColumns(0, 4);
119     assertEquals(0, cs3.findColumnPosition(2));
120
121   }
122
123   /**
124    * Test the method that finds the visible column position a given distance
125    * before another column
126    */
127   @Test(groups = { "Functional" })
128   public void testFindColumnNToLeft()
129   {
130     HiddenColumns cs = new HiddenColumns();
131
132     // test that without hidden columns, findColumnNToLeft returns
133     // position n to left of provided position
134     int pos = cs.subtractVisibleColumns(3, 10);
135     assertEquals(7, pos);
136
137     // 0 returns same position
138     pos = cs.subtractVisibleColumns(0, 10);
139     assertEquals(10, pos);
140
141     // overflow to left returns negative number
142     pos = cs.subtractVisibleColumns(3, 0);
143     assertEquals(-3, pos);
144
145     // test that with hidden columns to left of result column
146     // behaviour is the same as above
147     cs.hideColumns(1, 3);
148
149     // position n to left of provided position
150     pos = cs.subtractVisibleColumns(3, 10);
151     assertEquals(7, pos);
152
153     // 0 returns same position
154     pos = cs.subtractVisibleColumns(0, 10);
155     assertEquals(10, pos);
156
157     // test with one set of hidden columns between start and required position
158     cs.hideColumns(12, 15);
159     pos = cs.subtractVisibleColumns(8, 17);
160     assertEquals(5, pos);
161
162     // test with two sets of hidden columns between start and required position
163     cs.hideColumns(20, 21);
164     pos = cs.subtractVisibleColumns(8, 23);
165     assertEquals(9, pos);
166
167     // repeat last 2 tests with no hidden columns to left of required position
168     ColumnSelection colsel = new ColumnSelection();
169     cs.revealAllHiddenColumns(colsel);
170
171     // test with one set of hidden columns between start and required position
172     cs.hideColumns(12, 15);
173     pos = cs.subtractVisibleColumns(8, 17);
174     assertEquals(5, pos);
175
176     // test with two sets of hidden columns between start and required position
177     cs.hideColumns(20, 21);
178     pos = cs.subtractVisibleColumns(8, 23);
179     assertEquals(9, pos);
180
181   }
182
183   @Test(groups = { "Functional" })
184   public void testGetVisibleContigs()
185   {
186     HiddenColumns cs = new HiddenColumns();
187     cs.hideColumns(3, 6);
188     cs.hideColumns(8, 9);
189     cs.hideColumns(12, 12);
190
191     // start position is inclusive, end position exclusive:
192     int[] visible = cs.getVisibleContigs(1, 13);
193     assertEquals("[1, 2, 7, 7, 10, 11]", Arrays.toString(visible));
194
195     visible = cs.getVisibleContigs(4, 14);
196     assertEquals("[7, 7, 10, 11, 13, 13]", Arrays.toString(visible));
197
198     visible = cs.getVisibleContigs(3, 10);
199     assertEquals("[7, 7]", Arrays.toString(visible));
200
201     visible = cs.getVisibleContigs(4, 6);
202     assertEquals("[]", Arrays.toString(visible));
203   }
204
205   @Test(groups = { "Functional" })
206   public void testEquals()
207   {
208     HiddenColumns cs = new HiddenColumns();
209     cs.hideColumns(5, 9);
210
211     // a different set of hidden columns
212     HiddenColumns cs2 = new HiddenColumns();
213
214     // with no hidden columns
215     assertFalse(cs.equals(cs2));
216     assertFalse(cs2.equals(cs));
217
218     // with hidden columns added in a different order
219     cs2.hideColumns(6, 9);
220     cs2.hideColumns(5, 8);
221
222     assertTrue(cs.equals(cs2));
223     assertTrue(cs.equals(cs));
224     assertTrue(cs2.equals(cs));
225     assertTrue(cs2.equals(cs2));
226   }
227
228   @Test(groups = "Functional")
229   public void testCopyConstructor()
230   {
231     HiddenColumns cs = new HiddenColumns();
232     cs.hideColumns(10, 11);
233     cs.hideColumns(5, 7);
234     assertEquals("[5, 7]", Arrays.toString(cs.getHiddenRegions().get(0)));
235
236     HiddenColumns cs2 = new HiddenColumns(cs);
237     assertTrue(cs2.hasHiddenColumns());
238     assertEquals(2, cs2.getHiddenRegions().size());
239     // hidden columns are held in column order
240     assertEquals("[5, 7]", Arrays.toString(cs2.getHiddenRegions().get(0)));
241     assertEquals("[10, 11]", Arrays.toString(cs2.getHiddenRegions().get(1)));
242   }
243
244   /**
245    * Test the code used to locate the reference sequence ruler origin
246    */
247   @Test(groups = { "Functional" })
248   public void testLocateVisibleBoundsofSequence()
249   {
250     // create random alignment
251     AlignmentGenerator gen = new AlignmentGenerator(false);
252     AlignmentI al = gen.generate(50, 20, 123, 5, 5);
253
254     HiddenColumns cs = al.getHiddenColumns();
255     ColumnSelection colsel = new ColumnSelection();
256
257     SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---");
258     assertEquals(2, seq.findIndex(seq.getStart()));
259
260     // no hidden columns
261     assertEquals(
262             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
263                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
264                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
265                 seq.findIndex(seq.getEnd()) - 1 }),
266             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
267
268     // hidden column on gap after end of sequence - should not affect bounds
269     colsel.hideSelectedColumns(13, al.getHiddenColumns());
270     assertEquals(
271             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
272                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
273                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
274                 seq.findIndex(seq.getEnd()) - 1 }),
275             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
276
277     cs.revealAllHiddenColumns(colsel);
278     // hidden column on gap before beginning of sequence - should vis bounds by
279     // one
280     colsel.hideSelectedColumns(0, al.getHiddenColumns());
281     assertEquals(
282             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 2,
283                 seq.findIndex(seq.getEnd()) - 2, seq.getStart(),
284                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
285                 seq.findIndex(seq.getEnd()) - 1 }),
286             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
287
288     cs.revealAllHiddenColumns(colsel);
289     // hide columns around most of sequence - leave one residue remaining
290     cs.hideColumns(1, 3);
291     cs.hideColumns(6, 11);
292     assertEquals("-D",
293             cs.getVisibleSequenceStrings(0, 5, new SequenceI[] { seq })[0]);
294     assertEquals(
295             Arrays.toString(new int[] { 1, 1, 3, 3,
296                 seq.findIndex(seq.getStart()) - 1,
297                 seq.findIndex(seq.getEnd()) - 1 }),
298             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
299     cs.revealAllHiddenColumns(colsel);
300
301     // hide whole sequence - should just get location of hidden region
302     // containing sequence
303     cs.hideColumns(1, 11);
304     assertEquals(
305             Arrays.toString(new int[] { 0, 1, 0, 0,
306                 seq.findIndex(seq.getStart()) - 1,
307                 seq.findIndex(seq.getEnd()) - 1 }),
308             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
309
310   }
311
312   @Test(groups = { "Functional" })
313   public void testLocateVisibleBoundsPathologicals()
314   {
315     // test some pathological cases we missed
316     AlignmentI al = new Alignment(new SequenceI[] { new Sequence(
317             "refseqGaptest", "KTDVTI----------NFI-----G----L") });
318     HiddenColumns cs = new HiddenColumns();
319     cs.hideInsertionsFor(al.getSequenceAt(0));
320     assertEquals(
321             "G",
322             ""
323                     + al.getSequenceAt(0).getCharAt(
324                             cs.adjustForHiddenColumns(9)));
325
326   }
327
328   @Test(groups = { "Functional" })
329   public void testHideColumns()
330   {
331     // create random alignment
332     AlignmentGenerator gen = new AlignmentGenerator(false);
333     AlignmentI al = gen.generate(50, 20, 123, 5, 5);
334
335     ColumnSelection colsel = new ColumnSelection();
336     HiddenColumns cs = al.getHiddenColumns();
337     colsel.hideSelectedColumns(5, al.getHiddenColumns());
338     List<int[]> hidden = cs.getHiddenRegions();
339     assertEquals(1, hidden.size());
340     assertEquals("[5, 5]", Arrays.toString(hidden.get(0)));
341
342     colsel.hideSelectedColumns(3, al.getHiddenColumns());
343     assertEquals(2, hidden.size());
344     // two hidden ranges, in order:
345     assertSame(hidden, cs.getHiddenRegions());
346     assertEquals("[3, 3]", Arrays.toString(hidden.get(0)));
347     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
348
349     // hiding column 4 expands [3, 3] to [3, 4]
350     // and merges to [5, 5] to make [3, 5]
351     colsel.hideSelectedColumns(4, al.getHiddenColumns());
352     hidden = cs.getHiddenRegions();
353     assertEquals(1, hidden.size());
354     assertEquals("[3, 5]", Arrays.toString(hidden.get(0)));
355
356     // clear hidden columns (note they are added to selected)
357     cs.revealAllHiddenColumns(colsel);
358     // it is now actually null but getter returns an empty list
359     assertTrue(cs.getHiddenRegions().isEmpty());
360
361     cs.hideColumns(3, 6);
362     hidden = cs.getHiddenRegions();
363     int[] firstHiddenRange = hidden.get(0);
364     assertEquals("[3, 6]", Arrays.toString(firstHiddenRange));
365
366     // adding a subrange of already hidden should do nothing
367     cs.hideColumns(4, 5);
368     assertEquals(1, hidden.size());
369     assertSame(firstHiddenRange, cs.getHiddenRegions().get(0));
370     cs.hideColumns(3, 5);
371     assertEquals(1, hidden.size());
372     assertSame(firstHiddenRange, cs.getHiddenRegions().get(0));
373     cs.hideColumns(4, 6);
374     assertEquals(1, hidden.size());
375     assertSame(firstHiddenRange, cs.getHiddenRegions().get(0));
376     cs.hideColumns(3, 6);
377     assertEquals(1, hidden.size());
378     assertSame(firstHiddenRange, cs.getHiddenRegions().get(0));
379
380     cs.revealAllHiddenColumns(colsel);
381     cs.hideColumns(2, 4);
382     hidden = cs.getHiddenRegions();
383     assertEquals(1, hidden.size());
384     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
385
386     // extend contiguous with 2 positions overlap
387     cs.hideColumns(3, 5);
388     assertEquals(1, hidden.size());
389     assertEquals("[2, 5]", Arrays.toString(hidden.get(0)));
390
391     // extend contiguous with 1 position overlap
392     cs.hideColumns(5, 6);
393     assertEquals(1, hidden.size());
394     assertEquals("[2, 6]", Arrays.toString(hidden.get(0)));
395
396     // extend contiguous with overlap both ends:
397     cs.hideColumns(1, 7);
398     assertEquals(1, hidden.size());
399     assertEquals("[1, 7]", Arrays.toString(hidden.get(0)));
400   }
401
402   /**
403    * Test the method that reveals a range of hidden columns given the start
404    * column of the range
405    */
406   @Test(groups = { "Functional" })
407   public void testRevealHiddenColumns()
408   {
409     ColumnSelection colsel = new ColumnSelection();
410     HiddenColumns cs = new HiddenColumns();
411     cs.hideColumns(5, 8);
412     colsel.addElement(10);
413     cs.revealHiddenColumns(5, colsel);
414     // hidden columns list now null but getter returns empty list:
415     assertTrue(cs.getHiddenRegions().isEmpty());
416     // revealed columns are marked as selected (added to selection):
417     assertEquals("[10, 5, 6, 7, 8]", colsel.getSelected().toString());
418
419     // calling with a column other than the range start does nothing:
420     colsel = new ColumnSelection();
421     cs = new HiddenColumns();
422     cs.hideColumns(5, 8);
423     List<int[]> hidden = cs.getHiddenRegions();
424     cs.revealHiddenColumns(6, colsel);
425     assertSame(hidden, cs.getHiddenRegions());
426     assertTrue(colsel.getSelected().isEmpty());
427   }
428
429   @Test(groups = { "Functional" })
430   public void testRevealAllHiddenColumns()
431   {
432     HiddenColumns cs = new HiddenColumns();
433     ColumnSelection colsel = new ColumnSelection();
434     cs.hideColumns(5, 8);
435     cs.hideColumns(2, 3);
436     colsel.addElement(11);
437     colsel.addElement(1);
438     cs.revealAllHiddenColumns(colsel);
439
440     /*
441      * revealing hidden columns adds them (in order) to the (unordered)
442      * selection list
443      */
444     assertTrue(cs.getHiddenRegions().isEmpty());
445     assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", colsel.getSelected()
446             .toString());
447   }
448
449   @Test(groups = { "Functional" })
450   public void testIsVisible()
451   {
452     HiddenColumns cs = new HiddenColumns();
453     cs.hideColumns(2, 4);
454     cs.hideColumns(6, 7);
455     assertTrue(cs.isVisible(0));
456     assertTrue(cs.isVisible(-99));
457     assertTrue(cs.isVisible(1));
458     assertFalse(cs.isVisible(2));
459     assertFalse(cs.isVisible(3));
460     assertFalse(cs.isVisible(4));
461     assertTrue(cs.isVisible(5));
462     assertFalse(cs.isVisible(6));
463     assertFalse(cs.isVisible(7));
464   }
465
466   /**
467    * Test for the case when a hidden range encloses more one already hidden
468    * range
469    */
470   @Test(groups = { "Functional" })
471   public void testHideColumns_subsumingHidden()
472   {
473     /*
474      * JAL-2370 bug scenario:
475      * two hidden ranges subsumed by a third
476      */
477     HiddenColumns cs = new HiddenColumns();
478     cs.hideColumns(49, 59);
479     cs.hideColumns(69, 79);
480     List<int[]> hidden = cs.getHiddenRegions();
481     assertEquals(2, hidden.size());
482     assertEquals("[49, 59]", Arrays.toString(hidden.get(0)));
483     assertEquals("[69, 79]", Arrays.toString(hidden.get(1)));
484
485     cs.hideColumns(48, 80);
486     hidden = cs.getHiddenRegions();
487     assertEquals(1, hidden.size());
488     assertEquals("[48, 80]", Arrays.toString(hidden.get(0)));
489
490     /*
491      * another...joining hidden ranges
492      */
493     cs = new HiddenColumns();
494     cs.hideColumns(10, 20);
495     cs.hideColumns(30, 40);
496     cs.hideColumns(50, 60);
497     // hiding 21-49 should merge to one range
498     cs.hideColumns(21, 49);
499     hidden = cs.getHiddenRegions();
500     assertEquals(1, hidden.size());
501     assertEquals("[10, 60]", Arrays.toString(hidden.get(0)));
502
503     /*
504      * another...left overlap, subsumption, right overlap,
505      * no overlap of existing hidden ranges
506      */
507     cs = new HiddenColumns();
508     cs.hideColumns(10, 20);
509     cs.hideColumns(10, 20);
510     cs.hideColumns(30, 35);
511     cs.hideColumns(40, 50);
512     cs.hideColumns(60, 70);
513
514     cs.hideColumns(15, 45);
515     hidden = cs.getHiddenRegions();
516     assertEquals(2, hidden.size());
517     assertEquals("[10, 50]", Arrays.toString(hidden.get(0)));
518     assertEquals("[60, 70]", Arrays.toString(hidden.get(1)));
519   }
520
521   @Test(groups = { "Functional" })
522   public void testHideBitset()
523   {
524     HiddenColumns cs;
525
526     BitSet one = new BitSet();
527
528     // one hidden range
529     one.set(1);
530     cs = new HiddenColumns();
531     cs.hideMarkedBits(one);
532     assertEquals(1, cs.getHiddenRegions().size());
533
534     one.set(2);
535     cs = new HiddenColumns();
536     cs.hideMarkedBits(one);
537     assertEquals(1, cs.getHiddenRegions().size());
538
539     one.set(3);
540     cs = new HiddenColumns();
541     cs.hideMarkedBits(one);
542     assertEquals(1, cs.getHiddenRegions().size());
543
544     // split
545     one.clear(2);
546     cs = new HiddenColumns();
547     cs.hideMarkedBits(one);
548     assertEquals(2, cs.getHiddenRegions().size());
549
550     assertEquals(0, cs.adjustForHiddenColumns(0));
551     assertEquals(2, cs.adjustForHiddenColumns(1));
552     assertEquals(4, cs.adjustForHiddenColumns(2));
553
554     // one again
555     one.clear(1);
556     cs = new HiddenColumns();
557     cs.hideMarkedBits(one);
558
559     assertEquals(1, cs.getHiddenRegions().size());
560
561     assertEquals(0, cs.adjustForHiddenColumns(0));
562     assertEquals(1, cs.adjustForHiddenColumns(1));
563     assertEquals(2, cs.adjustForHiddenColumns(2));
564     assertEquals(4, cs.adjustForHiddenColumns(3));
565   }
566 }