JAL-2089 patch broken merge to master for Release 2.10.0b1
[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 import static org.testng.AssertJUnit.fail;
28
29 import java.util.Arrays;
30 import java.util.BitSet;
31 import java.util.Collections;
32 import java.util.ConcurrentModificationException;
33 import java.util.List;
34
35 import org.testng.annotations.Test;
36
37 public class ColumnSelectionTest
38 {
39
40   @Test(groups = { "Functional" })
41   public void testAddElement()
42   {
43     ColumnSelection cs = new ColumnSelection();
44     cs.addElement(2);
45     cs.addElement(5);
46     cs.addElement(3);
47     cs.addElement(5); // ignored
48     List<Integer> sel = cs.getSelected();
49     assertEquals("[2, 5, 3]", sel.toString());
50   }
51
52   /**
53    * Test the remove method - in particular to verify that remove(int i) removes
54    * the element whose value is i, _NOT_ the i'th element.
55    */
56   @Test(groups = { "Functional" })
57   public void testRemoveElement()
58   {
59     ColumnSelection cs = new ColumnSelection();
60     cs.addElement(2);
61     cs.addElement(5);
62
63     // removing elements not in the list has no effect
64     cs.removeElement(0);
65     cs.removeElement(1);
66     List<Integer> sel = cs.getSelected();
67     assertEquals(2, sel.size());
68     assertEquals(new Integer(2), sel.get(0));
69     assertEquals(new Integer(5), sel.get(1));
70
71     // removing an element in the list removes it
72     cs.removeElement(2);
73     // ...and also from the read-only view
74     assertEquals(1, sel.size());
75     sel = cs.getSelected();
76     assertEquals(1, sel.size());
77     assertEquals(new Integer(5), sel.get(0));
78   }
79
80   /**
81    * Test the method that finds the visible column position of an alignment
82    * column, allowing for hidden columns.
83    */
84   @Test(groups = { "Functional" })
85   public void testFindColumnPosition()
86   {
87     ColumnSelection cs = new ColumnSelection();
88     assertEquals(5, cs.findColumnPosition(5));
89
90     // hiding column 6 makes no difference
91     cs.hideColumns(6, 6);
92     assertEquals(5, cs.findColumnPosition(5));
93
94     // hiding column 4 moves column 5 to column 4
95     cs.hideColumns(4, 4);
96     assertEquals(4, cs.findColumnPosition(5));
97
98     // hiding columns 1 and 2 moves column 5 to column 2
99     cs.hideColumns(1, 2);
100     assertEquals(2, cs.findColumnPosition(5));
101   }
102
103   /**
104    * Test the code used to locate the reference sequence ruler origin
105    */
106   @Test(groups = { "Functional" })
107   public void testLocateVisibleBoundsofSequence()
108   {
109     ColumnSelection cs = new ColumnSelection();
110     SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---");
111     assertEquals(2, seq.findIndex(seq.getStart()));
112
113     // no hidden columns
114     assertEquals(
115             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
116                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
117                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
118                 seq.findIndex(seq.getEnd()) - 1 }),
119             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
120
121     // hidden column on gap after end of sequence - should not affect bounds
122     cs.hideColumns(13);
123     assertEquals(
124             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
125                 seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
126                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
127                 seq.findIndex(seq.getEnd()) - 1 }),
128             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
129
130     cs.revealAllHiddenColumns();
131     // hidden column on gap before beginning of sequence - should vis bounds by
132     // one
133     cs.hideColumns(0);
134     assertEquals(
135             Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 2,
136                 seq.findIndex(seq.getEnd()) - 2, seq.getStart(),
137                 seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
138                 seq.findIndex(seq.getEnd()) - 1 }),
139             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
140
141     cs.revealAllHiddenColumns();
142     // hide columns around most of sequence - leave one residue remaining
143     cs.hideColumns(1, 3);
144     cs.hideColumns(6, 11);
145     assertEquals("-D",
146             cs.getVisibleSequenceStrings(0, 5, new SequenceI[] { seq })[0]);
147     assertEquals(
148             Arrays.toString(new int[] { 1, 1, 3, 3,
149                 seq.findIndex(seq.getStart()) - 1,
150                 seq.findIndex(seq.getEnd()) - 1 }),
151             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
152     cs.revealAllHiddenColumns();
153
154     // hide whole sequence - should just get location of hidden region
155     // containing sequence
156     cs.hideColumns(1, 11);
157     assertEquals(
158             Arrays.toString(new int[] { 0, 1, 0, 0,
159                 seq.findIndex(seq.getStart()) - 1,
160                 seq.findIndex(seq.getEnd()) - 1 }),
161             Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
162
163   }
164
165   @Test(groups = { "Functional" })
166   public void testLocateVisibleBoundsPathologicals()
167   {
168     // test some pathological cases we missed
169     AlignmentI al = new Alignment(new SequenceI[] { new Sequence(
170             "refseqGaptest", "KTDVTI----------NFI-----G----L") });
171     ColumnSelection cs = new ColumnSelection();
172     cs.hideInsertionsFor(al.getSequenceAt(0));
173     assertEquals(
174             "G",
175             ""
176                     + al.getSequenceAt(0).getCharAt(
177                             cs.adjustForHiddenColumns(9)));
178
179   }
180
181   @Test(groups = { "Functional" })
182   public void testHideColumns()
183   {
184     ColumnSelection cs = new ColumnSelection();
185     cs.hideColumns(5);
186     List<int[]> hidden = cs.getHiddenColumns();
187     assertEquals(1, hidden.size());
188     assertEquals("[5, 5]", Arrays.toString(hidden.get(0)));
189
190     cs.hideColumns(3);
191     assertEquals(2, hidden.size());
192     // two hidden ranges, in order:
193     assertSame(hidden, cs.getHiddenColumns());
194     assertEquals("[3, 3]", Arrays.toString(hidden.get(0)));
195     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
196
197     // hiding column 4 expands [3, 3] to [3, 4]
198     // not fancy enough to coalesce this into [3, 5] though
199     cs.hideColumns(4);
200     hidden = cs.getHiddenColumns();
201     assertEquals(2, hidden.size());
202     assertEquals("[3, 4]", Arrays.toString(hidden.get(0)));
203     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
204
205     // clear hidden columns (note they are added to selected)
206     cs.revealAllHiddenColumns();
207     // it is now actually null but getter returns an empty list
208     assertTrue(cs.getHiddenColumns().isEmpty());
209
210     cs.hideColumns(3, 6);
211     hidden = cs.getHiddenColumns();
212     int[] firstHiddenRange = hidden.get(0);
213     assertEquals("[3, 6]", Arrays.toString(firstHiddenRange));
214
215     // adding a subrange of already hidden should do nothing
216     cs.hideColumns(4, 5);
217     assertEquals(1, hidden.size());
218     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
219     cs.hideColumns(3, 5);
220     assertEquals(1, hidden.size());
221     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
222     cs.hideColumns(4, 6);
223     assertEquals(1, hidden.size());
224     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
225     cs.hideColumns(3, 6);
226     assertEquals(1, hidden.size());
227     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
228
229     cs.revealAllHiddenColumns();
230     cs.hideColumns(2, 4);
231     hidden = cs.getHiddenColumns();
232     assertEquals(1, hidden.size());
233     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
234
235     // extend contiguous with 2 positions overlap
236     cs.hideColumns(3, 5);
237     assertEquals(1, hidden.size());
238     assertEquals("[2, 5]", Arrays.toString(hidden.get(0)));
239
240     // extend contiguous with 1 position overlap
241     cs.hideColumns(5, 6);
242     assertEquals(1, hidden.size());
243     assertEquals("[2, 6]", Arrays.toString(hidden.get(0)));
244
245     // extend contiguous with overlap both ends:
246     cs.hideColumns(1, 7);
247     assertEquals(1, hidden.size());
248     assertEquals("[1, 7]", Arrays.toString(hidden.get(0)));
249   }
250
251   /**
252    * Test the method that hides a specified column including any adjacent
253    * selected columns. This is a convenience method for the case where multiple
254    * column regions are selected and then hidden using menu option View | Hide |
255    * Selected Columns.
256    */
257   @Test(groups = { "Functional" })
258   public void testHideColumns_withSelection()
259   {
260     ColumnSelection cs = new ColumnSelection();
261     // select columns 4-6
262     cs.addElement(4);
263     cs.addElement(5);
264     cs.addElement(6);
265     // hide column 5 (and adjacent):
266     cs.hideColumns(5);
267     // 4,5,6 now hidden:
268     List<int[]> hidden = cs.getHiddenColumns();
269     assertEquals(1, hidden.size());
270     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
271     // none now selected:
272     assertTrue(cs.getSelected().isEmpty());
273
274     // repeat, hiding column 4 (5 and 6)
275     cs = new ColumnSelection();
276     cs.addElement(4);
277     cs.addElement(5);
278     cs.addElement(6);
279     cs.hideColumns(4);
280     hidden = cs.getHiddenColumns();
281     assertEquals(1, hidden.size());
282     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
283     assertTrue(cs.getSelected().isEmpty());
284
285     // repeat, hiding column (4, 5 and) 6
286     cs = new ColumnSelection();
287     cs.addElement(4);
288     cs.addElement(5);
289     cs.addElement(6);
290     cs.hideColumns(6);
291     hidden = cs.getHiddenColumns();
292     assertEquals(1, hidden.size());
293     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
294     assertTrue(cs.getSelected().isEmpty());
295
296     // repeat, with _only_ adjacent columns selected
297     cs = new ColumnSelection();
298     cs.addElement(4);
299     cs.addElement(6);
300     cs.hideColumns(5);
301     hidden = cs.getHiddenColumns();
302     assertEquals(1, hidden.size());
303     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
304     assertTrue(cs.getSelected().isEmpty());
305   }
306
307   /**
308    * Test the method that hides all (possibly disjoint) selected column ranges
309    */
310   @Test(groups = { "Functional" })
311   public void testHideSelectedColumns()
312   {
313     ColumnSelection cs = new ColumnSelection();
314     int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
315     for (int col : sel)
316     {
317       cs.addElement(col);
318     }
319     cs.hideColumns(15, 18);
320
321     cs.hideSelectedColumns();
322     assertTrue(cs.getSelected().isEmpty());
323     List<int[]> hidden = cs.getHiddenColumns();
324     assertEquals(4, hidden.size());
325     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
326     assertEquals("[7, 9]", Arrays.toString(hidden.get(1)));
327     assertEquals("[15, 18]", Arrays.toString(hidden.get(2)));
328     assertEquals("[20, 22]", Arrays.toString(hidden.get(3)));
329   }
330
331   /**
332    * Test the method that gets runs of selected columns ordered by column. If
333    * this fails, HideSelectedColumns may also fail
334    */
335   @Test(groups = { "Functional" })
336   public void testGetSelectedRanges()
337   {
338     /*
339      * getSelectedRanges returns ordered columns regardless
340      * of the order in which they are added
341      */
342     ColumnSelection cs = new ColumnSelection();
343     int[] sel = { 4, 3, 7, 21, 9, 20, 8, 22, 2 };
344     for (int col : sel)
345     {
346       cs.addElement(col);
347     }
348     List<int[]> range;
349     range = cs.getSelectedRanges();
350     assertEquals(3, range.size());
351     assertEquals("[2, 4]", Arrays.toString(range.get(0)));
352     assertEquals("[7, 9]", Arrays.toString(range.get(1)));
353     assertEquals("[20, 22]", Arrays.toString(range.get(2)));
354     cs.addElement(0);
355     cs.addElement(1);
356     range = cs.getSelectedRanges();
357     assertEquals(3, range.size());
358     assertEquals("[0, 4]", Arrays.toString(range.get(0)));
359   }
360
361   /**
362    * Test the method that reveals a range of hidden columns given the start
363    * column of the range
364    */
365   @Test(groups = { "Functional" })
366   public void testRevealHiddenColumns()
367   {
368     ColumnSelection cs = new ColumnSelection();
369     cs.hideColumns(5, 8);
370     cs.addElement(10);
371     cs.revealHiddenColumns(5);
372     // hidden columns list now null but getter returns empty list:
373     assertTrue(cs.getHiddenColumns().isEmpty());
374     // revealed columns are marked as selected (added to selection):
375     assertEquals("[10, 5, 6, 7, 8]", cs.getSelected().toString());
376
377     // calling with a column other than the range start does nothing:
378     cs = new ColumnSelection();
379     cs.hideColumns(5, 8);
380     List<int[]> hidden = cs.getHiddenColumns();
381     cs.revealHiddenColumns(6);
382     assertSame(hidden, cs.getHiddenColumns());
383     assertTrue(cs.getSelected().isEmpty());
384   }
385
386   @Test(groups = { "Functional" })
387   public void testRevealAllHiddenColumns()
388   {
389     ColumnSelection cs = new ColumnSelection();
390     cs.hideColumns(5, 8);
391     cs.hideColumns(2, 3);
392     cs.addElement(11);
393     cs.addElement(1);
394     cs.revealAllHiddenColumns();
395
396     /*
397      * revealing hidden columns adds them (in order) to the (unordered)
398      * selection list
399      */
400     assertTrue(cs.getHiddenColumns().isEmpty());
401     assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", cs.getSelected().toString());
402   }
403
404   @Test(groups = { "Functional" })
405   public void testIsVisible()
406   {
407     ColumnSelection cs = new ColumnSelection();
408     cs.hideColumns(2, 4);
409     cs.hideColumns(6, 7);
410     assertTrue(cs.isVisible(0));
411     assertTrue(cs.isVisible(-99));
412     assertTrue(cs.isVisible(1));
413     assertFalse(cs.isVisible(2));
414     assertFalse(cs.isVisible(3));
415     assertFalse(cs.isVisible(4));
416     assertTrue(cs.isVisible(5));
417     assertFalse(cs.isVisible(6));
418     assertFalse(cs.isVisible(7));
419   }
420
421   @Test(groups = { "Functional" })
422   public void testGetVisibleContigs()
423   {
424     ColumnSelection cs = new ColumnSelection();
425     cs.hideColumns(3, 6);
426     cs.hideColumns(8, 9);
427     cs.hideColumns(12, 12);
428
429     // start position is inclusive, end position exclusive:
430     int[] visible = cs.getVisibleContigs(1, 13);
431     assertEquals("[1, 2, 7, 7, 10, 11]", Arrays.toString(visible));
432
433     visible = cs.getVisibleContigs(4, 14);
434     assertEquals("[7, 7, 10, 11, 13, 13]", Arrays.toString(visible));
435
436     visible = cs.getVisibleContigs(3, 10);
437     assertEquals("[7, 7]", Arrays.toString(visible));
438
439     visible = cs.getVisibleContigs(4, 6);
440     assertEquals("[]", Arrays.toString(visible));
441   }
442
443   @Test(groups = { "Functional" })
444   public void testInvertColumnSelection()
445   {
446     ColumnSelection cs = new ColumnSelection();
447     cs.addElement(4);
448     cs.addElement(6);
449     cs.addElement(8);
450     cs.hideColumns(3, 3);
451     cs.hideColumns(6, 6);
452
453     // invert selection from start (inclusive) to end (exclusive)
454     // hidden columns are _not_ changed
455     cs.invertColumnSelection(2, 9);
456     assertEquals("[2, 5, 7]", cs.getSelected().toString());
457
458     cs.invertColumnSelection(1, 9);
459     assertEquals("[1, 4, 8]", cs.getSelected().toString());
460   }
461
462   @Test(groups = { "Functional" })
463   public void testMaxColumnSelection()
464   {
465     ColumnSelection cs = new ColumnSelection();
466     cs.addElement(0);
467     cs.addElement(513);
468     cs.addElement(1);
469     assertEquals(513, cs.getMax());
470     cs.removeElement(513);
471     assertEquals(1, cs.getMax());
472     cs.removeElement(1);
473     assertEquals(0, cs.getMax());
474     cs.addElement(512);
475     cs.addElement(513);
476     assertEquals(513, cs.getMax());
477
478   }
479
480   @Test(groups = { "Functional" })
481   public void testMinColumnSelection()
482   {
483     ColumnSelection cs = new ColumnSelection();
484     cs.addElement(0);
485     cs.addElement(513);
486     cs.addElement(1);
487     assertEquals(0, cs.getMin());
488     cs.removeElement(0);
489     assertEquals(1, cs.getMin());
490     cs.addElement(0);
491     assertEquals(0, cs.getMin());
492   }
493
494   @Test(groups = { "Functional" })
495   public void testEquals()
496   {
497     ColumnSelection cs = new ColumnSelection();
498     cs.addElement(0);
499     cs.addElement(513);
500     cs.addElement(1);
501     cs.hideColumns(3);
502     cs.hideColumns(7);
503     cs.hideColumns(5, 9);
504
505     // same selections added in a different order
506     ColumnSelection cs2 = new ColumnSelection();
507     cs2.addElement(1);
508     cs2.addElement(513);
509     cs2.addElement(0);
510
511     // with no hidden columns
512     assertFalse(cs.equals(cs2));
513     assertFalse(cs2.equals(cs));
514
515     // with hidden columns added in a different order
516     cs2.hideColumns(6, 9);
517     cs2.hideColumns(5, 8);
518     cs2.hideColumns(3);
519
520     assertTrue(cs.equals(cs2));
521     assertTrue(cs.equals(cs));
522     assertTrue(cs2.equals(cs));
523     assertTrue(cs2.equals(cs2));
524
525     cs2.addElement(12);
526     assertFalse(cs.equals(cs2));
527     assertFalse(cs2.equals(cs));
528
529     cs2.removeElement(12);
530     assertTrue(cs.equals(cs2));
531
532     cs2.hideColumns(88);
533     assertFalse(cs.equals(cs2));
534     /*
535      * unhiding a column adds it to selection!
536      */
537     cs2.revealHiddenColumns(88);
538     assertFalse(cs.equals(cs2));
539     cs.addElement(88);
540     assertTrue(cs.equals(cs2));
541   }
542
543   /**
544    * Test the method that returns selected columns, in the order in which they
545    * were added
546    */
547   @Test(groups = { "Functional" })
548   public void testGetSelected()
549   {
550     ColumnSelection cs = new ColumnSelection();
551     int[] sel = { 4, 3, 7, 21 };
552     for (int col : sel)
553     {
554       cs.addElement(col);
555     }
556
557     List<Integer> selected = cs.getSelected();
558     assertEquals(4, selected.size());
559     assertEquals("[4, 3, 7, 21]", selected.toString());
560
561     /*
562      * getSelected returns a read-only view of the list
563      * verify the view follows any changes in it
564      */
565     cs.removeElement(7);
566     cs.addElement(1);
567     cs.removeElement(4);
568     assertEquals("[3, 21, 1]", selected.toString());
569   }
570
571   /**
572    * Test to verify that the list returned by getSelection cannot be modified
573    */
574   @Test(groups = { "Functional" })
575   public void testGetSelected_isReadOnly()
576   {
577     ColumnSelection cs = new ColumnSelection();
578     cs.addElement(3);
579
580     List<Integer> selected = cs.getSelected();
581     try
582     {
583       selected.clear();
584       fail("expected exception");
585     } catch (UnsupportedOperationException e)
586     {
587       // expected
588     }
589     try
590     {
591       selected.add(1);
592       fail("expected exception");
593     } catch (UnsupportedOperationException e)
594     {
595       // expected
596     }
597     try
598     {
599       selected.remove(3);
600       fail("expected exception");
601     } catch (UnsupportedOperationException e)
602     {
603       // expected
604     }
605     try
606     {
607       Collections.sort(selected);
608       fail("expected exception");
609     } catch (UnsupportedOperationException e)
610     {
611       // expected
612     }
613   }
614
615   /**
616    * Test that demonstrates a ConcurrentModificationException is thrown if you
617    * change the selection while iterating over it
618    */
619   @Test(
620     groups = "Functional",
621     expectedExceptions = { ConcurrentModificationException.class })
622   public void testGetSelected_concurrentModification()
623   {
624     ColumnSelection cs = new ColumnSelection();
625     cs.addElement(0);
626     cs.addElement(1);
627     cs.addElement(2);
628
629     /*
630      * simulate changing the list under us (e.g. in a separate
631      * thread) while iterating over it -> ConcurrentModificationException
632      */
633     List<Integer> selected = cs.getSelected();
634     for (Integer col : selected)
635     {
636       if (col.intValue() == 0)
637       {
638         cs.removeElement(1);
639       }
640     }
641   }
642
643   @Test(groups = "Functional")
644   public void testMarkColumns()
645   {
646     ColumnSelection cs = new ColumnSelection();
647     cs.addElement(5); // this will be cleared
648     BitSet toMark = new BitSet();
649     toMark.set(1);
650     toMark.set(3);
651     toMark.set(6);
652     toMark.set(9);
653
654     assertTrue(cs.markColumns(toMark, 3, 8, false, false, false));
655     List<Integer> selected = cs.getSelected();
656     assertEquals(2, selected.size());
657     assertTrue(selected.contains(3));
658     assertTrue(selected.contains(6));
659   }
660
661   @Test(groups = "Functional")
662   public void testMarkColumns_extend()
663   {
664     ColumnSelection cs = new ColumnSelection();
665     cs.addElement(1);
666     cs.addElement(5);
667     BitSet toMark = new BitSet();
668     toMark.set(1);
669     toMark.set(3);
670     toMark.set(6);
671     toMark.set(9);
672
673     /*
674      * extending selection of {3, 6} should leave {1, 3, 5, 6} selected
675      */
676     assertTrue(cs.markColumns(toMark, 3, 8, false, true, false));
677     List<Integer> selected = cs.getSelected();
678     assertEquals(4, selected.size());
679     assertTrue(selected.contains(1));
680     assertTrue(selected.contains(3));
681     assertTrue(selected.contains(5));
682     assertTrue(selected.contains(6));
683   }
684
685   @Test(groups = "Functional")
686   public void testMarkColumns_invert()
687   {
688     ColumnSelection cs = new ColumnSelection();
689     cs.addElement(5); // this will be cleared
690     BitSet toMark = new BitSet();
691     toMark.set(1);
692     toMark.set(3);
693     toMark.set(6);
694     toMark.set(9);
695
696     /*
697      * inverted selection of {3, 6} should select {4, 5, 7, 8}
698      */
699     assertTrue(cs.markColumns(toMark, 3, 8, true, false, false));
700     List<Integer> selected = cs.getSelected();
701     assertEquals(4, selected.size());
702     assertTrue(selected.contains(4));
703     assertTrue(selected.contains(5));
704     assertTrue(selected.contains(7));
705     assertTrue(selected.contains(8));
706   }
707
708   @Test(groups = "Functional")
709   public void testMarkColumns_toggle()
710   {
711     ColumnSelection cs = new ColumnSelection();
712     cs.addElement(1); // outside change range
713     cs.addElement(3);
714     cs.addElement(4);
715     cs.addElement(10); // outside change range
716     BitSet toMark = new BitSet();
717     toMark.set(1);
718     toMark.set(3);
719     toMark.set(6);
720     toMark.set(9);
721
722     /*
723      * toggling state of {3, 6} should leave {1, 4, 6, 10} selected
724      */
725     assertTrue(cs.markColumns(toMark, 3, 8, false, false, true));
726     List<Integer> selected = cs.getSelected();
727     assertEquals(4, selected.size());
728     assertTrue(selected.contains(1));
729     assertTrue(selected.contains(4));
730     assertTrue(selected.contains(6));
731     assertTrue(selected.contains(10));
732   }
733
734   @Test(groups = "Functional")
735   public void testCopyConstructor()
736   {
737     ColumnSelection cs = new ColumnSelection();
738     cs.addElement(3);
739     cs.addElement(1);
740     cs.hideColumns(10, 11);
741     cs.hideColumns(5, 7);
742     assertEquals("[5, 7]", Arrays.toString(cs.getHiddenColumns().get(0)));
743
744     ColumnSelection cs2 = new ColumnSelection(cs);
745     assertTrue(cs2.hasSelectedColumns());
746     assertTrue(cs2.hasHiddenColumns());
747     // order of column selection is preserved
748     assertEquals("[3, 1]", cs2.getSelected().toString());
749     assertEquals(2, cs2.getHiddenColumns().size());
750     // hidden columns are held in column order
751     assertEquals("[5, 7]", Arrays.toString(cs2.getHiddenColumns().get(0)));
752     assertEquals("[10, 11]", Arrays.toString(cs2.getHiddenColumns().get(1)));
753   }
754 }