JAL-2172 test and code updates for readonly selection list
[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("refseqGaptest","KTDVTI----------NFI-----G----L")});
170     ColumnSelection cs = new ColumnSelection();
171     cs.hideInsertionsFor(al.getSequenceAt(0));
172     assertEquals(
173             "G",
174             ""
175                     + al.getSequenceAt(0).getCharAt(
176                             cs.adjustForHiddenColumns(9)));
177
178
179   }
180   @Test(groups = { "Functional" })
181   public void testHideColumns()
182   {
183     ColumnSelection cs = new ColumnSelection();
184     cs.hideColumns(5);
185     List<int[]> hidden = cs.getHiddenColumns();
186     assertEquals(1, hidden.size());
187     assertEquals("[5, 5]", Arrays.toString(hidden.get(0)));
188
189     cs.hideColumns(3);
190     assertEquals(2, hidden.size());
191     // two hidden ranges, in order:
192     assertSame(hidden, cs.getHiddenColumns());
193     assertEquals("[3, 3]", Arrays.toString(hidden.get(0)));
194     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
195
196     // hiding column 4 expands [3, 3] to [3, 4]
197     // not fancy enough to coalesce this into [3, 5] though
198     cs.hideColumns(4);
199     hidden = cs.getHiddenColumns();
200     assertEquals(2, hidden.size());
201     assertEquals("[3, 4]", Arrays.toString(hidden.get(0)));
202     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
203
204     // clear hidden columns (note they are added to selected)
205     cs.revealAllHiddenColumns();
206     // it is now actually null but getter returns an empty list
207     assertTrue(cs.getHiddenColumns().isEmpty());
208
209     cs.hideColumns(3, 6);
210     hidden = cs.getHiddenColumns();
211     int[] firstHiddenRange = hidden.get(0);
212     assertEquals("[3, 6]", Arrays.toString(firstHiddenRange));
213
214     // adding a subrange of already hidden should do nothing
215     cs.hideColumns(4, 5);
216     assertEquals(1, hidden.size());
217     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
218     cs.hideColumns(3, 5);
219     assertEquals(1, hidden.size());
220     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
221     cs.hideColumns(4, 6);
222     assertEquals(1, hidden.size());
223     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
224     cs.hideColumns(3, 6);
225     assertEquals(1, hidden.size());
226     assertSame(firstHiddenRange, cs.getHiddenColumns().get(0));
227
228     cs.revealAllHiddenColumns();
229     cs.hideColumns(2, 4);
230     hidden = cs.getHiddenColumns();
231     assertEquals(1, hidden.size());
232     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
233
234     // extend contiguous with 2 positions overlap
235     cs.hideColumns(3, 5);
236     assertEquals(1, hidden.size());
237     assertEquals("[2, 5]", Arrays.toString(hidden.get(0)));
238
239     // extend contiguous with 1 position overlap
240     cs.hideColumns(5, 6);
241     assertEquals(1, hidden.size());
242     assertEquals("[2, 6]", Arrays.toString(hidden.get(0)));
243
244     // extend contiguous with overlap both ends:
245     cs.hideColumns(1, 7);
246     assertEquals(1, hidden.size());
247     assertEquals("[1, 7]", Arrays.toString(hidden.get(0)));
248   }
249
250   /**
251    * Test the method that hides a specified column including any adjacent
252    * selected columns. This is a convenience method for the case where multiple
253    * column regions are selected and then hidden using menu option View | Hide |
254    * Selected Columns.
255    */
256   @Test(groups = { "Functional" })
257   public void testHideColumns_withSelection()
258   {
259     ColumnSelection cs = new ColumnSelection();
260     // select columns 4-6
261     cs.addElement(4);
262     cs.addElement(5);
263     cs.addElement(6);
264     // hide column 5 (and adjacent):
265     cs.hideColumns(5);
266     // 4,5,6 now hidden:
267     List<int[]> hidden = cs.getHiddenColumns();
268     assertEquals(1, hidden.size());
269     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
270     // none now selected:
271     assertTrue(cs.getSelected().isEmpty());
272
273     // repeat, hiding column 4 (5 and 6)
274     cs = new ColumnSelection();
275     cs.addElement(4);
276     cs.addElement(5);
277     cs.addElement(6);
278     cs.hideColumns(4);
279     hidden = cs.getHiddenColumns();
280     assertEquals(1, hidden.size());
281     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
282     assertTrue(cs.getSelected().isEmpty());
283
284     // repeat, hiding column (4, 5 and) 6
285     cs = new ColumnSelection();
286     cs.addElement(4);
287     cs.addElement(5);
288     cs.addElement(6);
289     cs.hideColumns(6);
290     hidden = cs.getHiddenColumns();
291     assertEquals(1, hidden.size());
292     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
293     assertTrue(cs.getSelected().isEmpty());
294
295     // repeat, with _only_ adjacent columns selected
296     cs = new ColumnSelection();
297     cs.addElement(4);
298     cs.addElement(6);
299     cs.hideColumns(5);
300     hidden = cs.getHiddenColumns();
301     assertEquals(1, hidden.size());
302     assertEquals("[4, 6]", Arrays.toString(hidden.get(0)));
303     assertTrue(cs.getSelected().isEmpty());
304   }
305
306   /**
307    * Test the method that hides all (possibly disjoint) selected column ranges
308    */
309   @Test(groups = { "Functional" })
310   public void testHideSelectedColumns()
311   {
312     ColumnSelection cs = new ColumnSelection();
313     int[] sel = { 2, 3, 4, 7, 8, 9, 20, 21, 22 };
314     for (int col : sel)
315     {
316       cs.addElement(col);
317     }
318     cs.hideColumns(15, 18);
319
320     cs.hideSelectedColumns();
321     assertTrue(cs.getSelected().isEmpty());
322     List<int[]> hidden = cs.getHiddenColumns();
323     assertEquals(4, hidden.size());
324     assertEquals("[2, 4]", Arrays.toString(hidden.get(0)));
325     assertEquals("[7, 9]", Arrays.toString(hidden.get(1)));
326     assertEquals("[15, 18]", Arrays.toString(hidden.get(2)));
327     assertEquals("[20, 22]", Arrays.toString(hidden.get(3)));
328   }
329
330   /**
331    * Test the method that gets runs of selected columns ordered by column. If
332    * this fails, HideSelectedColumns may also fail
333    */
334   @Test(groups = { "Functional" })
335   public void testGetSelectedRanges()
336   {
337     /*
338      * getSelectedRanges returns ordered columns regardless
339      * of the order in which they are added
340      */
341     ColumnSelection cs = new ColumnSelection();
342     int[] sel = { 4, 3, 7, 21, 9, 20, 8, 22, 2 };
343     for (int col : sel)
344     {
345       cs.addElement(col);
346     }
347     List<int[]> range;
348     range = cs.getSelectedRanges();
349     assertEquals(3, range.size());
350     assertEquals("[2, 4]", Arrays.toString(range.get(0)));
351     assertEquals("[7, 9]", Arrays.toString(range.get(1)));
352     assertEquals("[20, 22]", Arrays.toString(range.get(2)));
353     cs.addElement(0);
354     cs.addElement(1);
355     range = cs.getSelectedRanges();
356     assertEquals(3, range.size());
357     assertEquals("[0, 4]", Arrays.toString(range.get(0)));
358   }
359
360   /**
361    * Test the method that reveals a range of hidden columns given the start
362    * column of the range
363    */
364   @Test(groups = { "Functional" })
365   public void testRevealHiddenColumns()
366   {
367     ColumnSelection cs = new ColumnSelection();
368     cs.hideColumns(5, 8);
369     cs.addElement(10);
370     cs.revealHiddenColumns(5);
371     // hidden columns list now null but getter returns empty list:
372     assertTrue(cs.getHiddenColumns().isEmpty());
373     // revealed columns are marked as selected (added to selection):
374     assertEquals("[10, 5, 6, 7, 8]", cs.getSelected().toString());
375
376     // calling with a column other than the range start does nothing:
377     cs = new ColumnSelection();
378     cs.hideColumns(5, 8);
379     List<int[]> hidden = cs.getHiddenColumns();
380     cs.revealHiddenColumns(6);
381     assertSame(hidden, cs.getHiddenColumns());
382     assertTrue(cs.getSelected().isEmpty());
383   }
384
385   @Test(groups = { "Functional" })
386   public void testRevealAllHiddenColumns()
387   {
388     ColumnSelection cs = new ColumnSelection();
389     cs.hideColumns(5, 8);
390     cs.hideColumns(2, 3);
391     cs.addElement(11);
392     cs.addElement(1);
393     cs.revealAllHiddenColumns();
394
395     /*
396      * revealing hidden columns adds them (in order) to the (unordered)
397      * selection list
398      */
399     assertTrue(cs.getHiddenColumns().isEmpty());
400     assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", cs.getSelected().toString());
401   }
402
403   @Test(groups = { "Functional" })
404   public void testIsVisible()
405   {
406     ColumnSelection cs = new ColumnSelection();
407     cs.hideColumns(2, 4);
408     cs.hideColumns(6, 7);
409     assertTrue(cs.isVisible(0));
410     assertTrue(cs.isVisible(-99));
411     assertTrue(cs.isVisible(1));
412     assertFalse(cs.isVisible(2));
413     assertFalse(cs.isVisible(3));
414     assertFalse(cs.isVisible(4));
415     assertTrue(cs.isVisible(5));
416     assertFalse(cs.isVisible(6));
417     assertFalse(cs.isVisible(7));
418   }
419
420   @Test(groups = { "Functional" })
421   public void testGetVisibleContigs()
422   {
423     ColumnSelection cs = new ColumnSelection();
424     cs.hideColumns(3, 6);
425     cs.hideColumns(8, 9);
426     cs.hideColumns(12, 12);
427
428     // start position is inclusive, end position exclusive:
429     int[] visible = cs.getVisibleContigs(1, 13);
430     assertEquals("[1, 2, 7, 7, 10, 11]", Arrays.toString(visible));
431
432     visible = cs.getVisibleContigs(4, 14);
433     assertEquals("[7, 7, 10, 11, 13, 13]", Arrays.toString(visible));
434
435     visible = cs.getVisibleContigs(3, 10);
436     assertEquals("[7, 7]", Arrays.toString(visible));
437
438     visible = cs.getVisibleContigs(4, 6);
439     assertEquals("[]", Arrays.toString(visible));
440   }
441
442   @Test(groups = { "Functional" })
443   public void testInvertColumnSelection()
444   {
445     ColumnSelection cs = new ColumnSelection();
446     cs.addElement(4);
447     cs.addElement(6);
448     cs.addElement(8);
449     cs.hideColumns(3, 3);
450     cs.hideColumns(6, 6);
451
452     // invert selection from start (inclusive) to end (exclusive)
453     // hidden columns are _not_ changed
454     cs.invertColumnSelection(2, 9);
455     assertEquals("[2, 5, 7]", cs.getSelected().toString());
456
457     cs.invertColumnSelection(1, 9);
458     assertEquals("[1, 4, 8]", cs.getSelected().toString());
459   }
460
461   @Test(groups = { "Functional" })
462   public void testMaxColumnSelection()
463   {
464     ColumnSelection cs = new ColumnSelection();
465     cs.addElement(0);
466     cs.addElement(513);
467     cs.addElement(1);
468     assertEquals(513, cs.getMax());
469     cs.removeElement(513);
470     assertEquals(1, cs.getMax());
471     cs.removeElement(1);
472     assertEquals(0, cs.getMax());
473     cs.addElement(512);
474     cs.addElement(513);
475     assertEquals(513, cs.getMax());
476
477   }
478
479   @Test(groups = { "Functional" })
480   public void testMinColumnSelection()
481   {
482     ColumnSelection cs = new ColumnSelection();
483     cs.addElement(0);
484     cs.addElement(513);
485     cs.addElement(1);
486     assertEquals(0, cs.getMin());
487     cs.removeElement(0);
488     assertEquals(1, cs.getMin());
489     cs.addElement(0);
490     assertEquals(0, cs.getMin());
491   }
492
493   @Test(groups = { "Functional" })
494   public void testEquals()
495   {
496     ColumnSelection cs = new ColumnSelection();
497     cs.addElement(0);
498     cs.addElement(513);
499     cs.addElement(1);
500     cs.hideColumns(3);
501     cs.hideColumns(7);
502     cs.hideColumns(5,9);
503
504     // same selections added in a different order
505     ColumnSelection cs2 = new ColumnSelection();
506     cs2.addElement(1);
507     cs2.addElement(513);
508     cs2.addElement(0);
509
510     // with no hidden columns
511     assertFalse(cs.equals(cs2));
512     assertFalse(cs2.equals(cs));
513
514     // with hidden columns added in a different order
515     cs2.hideColumns(6, 9);
516     cs2.hideColumns(5, 8);
517     cs2.hideColumns(3);
518     
519     assertTrue(cs.equals(cs2));
520     assertTrue(cs.equals(cs));
521     assertTrue(cs2.equals(cs));
522     assertTrue(cs2.equals(cs2));
523
524     cs2.addElement(12);
525     assertFalse(cs.equals(cs2));
526     assertFalse(cs2.equals(cs));
527
528     cs2.removeElement(12);
529     assertTrue(cs.equals(cs2));
530
531     cs2.hideColumns(88);
532     assertFalse(cs.equals(cs2));
533     /*
534      * unhiding a column adds it to selection!
535      */
536     cs2.revealHiddenColumns(88);
537     assertFalse(cs.equals(cs2));
538     cs.addElement(88);
539     assertTrue(cs.equals(cs2));
540   }
541
542   /**
543    * Test the method that returns selected columns, in the order in which they
544    * were added
545    */
546   @Test(groups = { "Functional" })
547   public void testGetSelected()
548   {
549     ColumnSelection cs = new ColumnSelection();
550     int[] sel = { 4, 3, 7, 21 };
551     for (int col : sel)
552     {
553       cs.addElement(col);
554     }
555
556     List<Integer> selected = cs.getSelected();
557     assertEquals(4, selected.size());
558     assertEquals("[4, 3, 7, 21]", selected.toString());
559
560     /*
561      * getSelected returns a read-only view of the list
562      * verify the view follows any changes in it
563      */
564     cs.removeElement(7);
565     cs.addElement(1);
566     cs.removeElement(4);
567     assertEquals("[3, 21, 1]", selected.toString());
568   }
569
570   /**
571    * Test to verify that the list returned by getSelection cannot be modified
572    */
573   @Test(groups = { "Functional" })
574   public void testGetSelected_isReadOnly()
575   {
576     ColumnSelection cs = new ColumnSelection();
577     cs.addElement(3);
578
579     List<Integer> selected = cs.getSelected();
580     try
581     {
582       selected.clear();
583       fail("expected exception");
584     } catch (UnsupportedOperationException e)
585     {
586       // expected
587     }
588     try
589     {
590       selected.add(1);
591       fail("expected exception");
592     } catch (UnsupportedOperationException e)
593     {
594       // expected
595     }
596     try
597     {
598       selected.remove(3);
599       fail("expected exception");
600     } catch (UnsupportedOperationException e)
601     {
602       // expected
603     }
604     try
605     {
606       Collections.sort(selected);
607       fail("expected exception");
608     } catch (UnsupportedOperationException e)
609     {
610       // expected
611     }
612   }
613
614   /**
615    * Test that demonstrates a ConcurrentModificationException is thrown if you
616    * change the selection while iterating over it
617    */
618   @Test(
619     groups = "Functional",
620     expectedExceptions = { ConcurrentModificationException.class })
621   public void testGetSelected_concurrentModification()
622   {
623     ColumnSelection cs = new ColumnSelection();
624     cs.addElement(0);
625     cs.addElement(1);
626     cs.addElement(2);
627
628     /*
629      * simulate changing the list under us (e.g. in a separate
630      * thread) while iterating over it -> ConcurrentModificationException
631      */
632     List<Integer> selected = cs.getSelected();
633     for (Integer col : selected)
634     {
635       if (col.intValue() == 0)
636       {
637         cs.removeElement(1);
638       }
639     }
640   }
641
642   @Test(groups = "Functional")
643   public void testMarkColumns()
644   {
645     ColumnSelection cs = new ColumnSelection();
646     cs.addElement(5); // this will be cleared
647     BitSet toMark = new BitSet();
648     toMark.set(1);
649     toMark.set(3);
650     toMark.set(6);
651     toMark.set(9);
652
653     assertTrue(cs.markColumns(toMark, 3, 8, false, false, false));
654     List<Integer> selected = cs.getSelected();
655     assertEquals(2, selected.size());
656     assertTrue(selected.contains(3));
657     assertTrue(selected.contains(6));
658   }
659
660   @Test(groups = "Functional")
661   public void testMarkColumns_extend()
662   {
663     ColumnSelection cs = new ColumnSelection();
664     cs.addElement(1);
665     cs.addElement(5);
666     BitSet toMark = new BitSet();
667     toMark.set(1);
668     toMark.set(3);
669     toMark.set(6);
670     toMark.set(9);
671
672     /*
673      * extending selection of {3, 6} should leave {1, 3, 5, 6} selected
674      */
675     assertTrue(cs.markColumns(toMark, 3, 8, false, true, false));
676     List<Integer> selected = cs.getSelected();
677     assertEquals(4, selected.size());
678     assertTrue(selected.contains(1));
679     assertTrue(selected.contains(3));
680     assertTrue(selected.contains(5));
681     assertTrue(selected.contains(6));
682   }
683
684   @Test(groups = "Functional")
685   public void testMarkColumns_invert()
686   {
687     ColumnSelection cs = new ColumnSelection();
688     cs.addElement(5); // this will be cleared
689     BitSet toMark = new BitSet();
690     toMark.set(1);
691     toMark.set(3);
692     toMark.set(6);
693     toMark.set(9);
694
695     /*
696      * inverted selection of {3, 6} should select {4, 5, 7, 8}
697      */
698     assertTrue(cs.markColumns(toMark, 3, 8, true, false, false));
699     List<Integer> selected = cs.getSelected();
700     assertEquals(4, selected.size());
701     assertTrue(selected.contains(4));
702     assertTrue(selected.contains(5));
703     assertTrue(selected.contains(7));
704     assertTrue(selected.contains(8));
705   }
706
707   @Test(groups = "Functional")
708   public void testMarkColumns_toggle()
709   {
710     ColumnSelection cs = new ColumnSelection();
711     cs.addElement(1); // outside change range
712     cs.addElement(3);
713     cs.addElement(4);
714     cs.addElement(10); // outside change range
715     BitSet toMark = new BitSet();
716     toMark.set(1);
717     toMark.set(3);
718     toMark.set(6);
719     toMark.set(9);
720
721     /*
722      * toggling state of {3, 6} should leave {1, 4, 6, 10} selected
723      */
724     assertTrue(cs.markColumns(toMark, 3, 8, false, false, true));
725     List<Integer> selected = cs.getSelected();
726     assertEquals(4, selected.size());
727     assertTrue(selected.contains(1));
728     assertTrue(selected.contains(4));
729     assertTrue(selected.contains(6));
730     assertTrue(selected.contains(10));
731   }
732
733   @Test(groups = "Functional")
734   public void testCopyConstructor()
735   {
736     ColumnSelection cs = new ColumnSelection();
737     cs.addElement(3);
738     cs.addElement(1);
739     cs.hideColumns(10, 11);
740     cs.hideColumns(5, 7);
741     assertEquals("[5, 7]", Arrays.toString(cs.getHiddenColumns().get(0)));
742
743     ColumnSelection cs2 = new ColumnSelection(cs);
744     assertTrue(cs2.hasSelectedColumns());
745     assertTrue(cs2.hasHiddenColumns());
746     // order of column selection is preserved
747     assertEquals("[3, 1]", cs2.getSelected().toString());
748     assertEquals(2, cs2.getHiddenColumns().size());
749     // hidden columns are held in column order
750     assertEquals("[5, 7]", Arrays.toString(cs2.getHiddenColumns().get(0)));
751     assertEquals("[10, 11]", Arrays.toString(cs2.getHiddenColumns().get(1)));
752   }
753 }