+ cs.removeElement(7);
+ cs.addElement(1);
+ cs.removeElement(4);
+ assertEquals("[3, 21, 1]", selected.toString());
+ }
+
+ /**
+ * Test to verify that the list returned by getSelection cannot be modified
+ */
+ @Test(groups = { "Functional" })
+ public void testGetSelected_isReadOnly()
+ {
+ ColumnSelection cs = new ColumnSelection();
+ cs.addElement(3);
+
+ List<Integer> selected = cs.getSelected();
+ try
+ {
+ selected.clear();
+ fail("expected exception");
+ } catch (UnsupportedOperationException e)
+ {
+ // expected
+ }
+ try
+ {
+ selected.add(1);
+ fail("expected exception");
+ } catch (UnsupportedOperationException e)
+ {
+ // expected
+ }
+ try
+ {
+ selected.remove(3);
+ fail("expected exception");
+ } catch (UnsupportedOperationException e)
+ {
+ // expected
+ }
+ try
+ {
+ Collections.sort(selected);
+ fail("expected exception");
+ } catch (UnsupportedOperationException e)
+ {
+ // expected
+ }
+ }
+
+ /**
+ * Test that demonstrates a ConcurrentModificationException is thrown if you
+ * change the selection while iterating over it
+ */
+ @Test(
+ groups = "Functional",
+ expectedExceptions =
+ { ConcurrentModificationException.class })
+ public void testGetSelected_concurrentModification()
+ {
+ ColumnSelection cs = new ColumnSelection();
+ cs.addElement(0);
+ cs.addElement(1);
+ cs.addElement(2);
+
+ /*
+ * simulate changing the list under us (e.g. in a separate
+ * thread) while iterating over it -> ConcurrentModificationException
+ */
+ List<Integer> selected = cs.getSelected();
+ for (Integer col : selected)
+ {
+ if (col.intValue() == 0)
+ {
+ cs.removeElement(1);
+ }
+ }
+ }
+
+ @Test(groups = "Functional")
+ public void testMarkColumns()
+ {
+ ColumnSelection cs = new ColumnSelection();
+ cs.addElement(5); // this will be cleared
+ BitSet toMark = new BitSet();
+ toMark.set(1);
+ toMark.set(3);
+ toMark.set(6);
+ toMark.set(9);
+
+ assertTrue(cs.markColumns(toMark, 3, 8, false, false, false));
+ List<Integer> selected = cs.getSelected();
+ assertEquals(2, selected.size());
+ assertTrue(selected.contains(3));
+ assertTrue(selected.contains(6));
+ }
+
+ @Test(groups = "Functional")
+ public void testMarkColumns_extend()
+ {
+ ColumnSelection cs = new ColumnSelection();
+ cs.addElement(1);
+ cs.addElement(5);
+ BitSet toMark = new BitSet();
+ toMark.set(1);
+ toMark.set(3);
+ toMark.set(6);
+ toMark.set(9);
+
+ /*
+ * extending selection of {3, 6} should leave {1, 3, 5, 6} selected
+ */
+ assertTrue(cs.markColumns(toMark, 3, 8, false, true, false));
+ List<Integer> selected = cs.getSelected();
+ assertEquals(4, selected.size());
+ assertTrue(selected.contains(1));
+ assertTrue(selected.contains(3));
+ assertTrue(selected.contains(5));
+ assertTrue(selected.contains(6));
+ }
+
+ @Test(groups = "Functional")
+ public void testMarkColumns_invert()
+ {
+ ColumnSelection cs = new ColumnSelection();
+ cs.addElement(5); // this will be cleared
+ BitSet toMark = new BitSet();
+ toMark.set(1);
+ toMark.set(3);
+ toMark.set(6);
+ toMark.set(9);
+
+ /*
+ * inverted selection of {3, 6} should select {4, 5, 7, 8}
+ */
+ assertTrue(cs.markColumns(toMark, 3, 8, true, false, false));
+ List<Integer> selected = cs.getSelected();
+ assertEquals(4, selected.size());
+ assertTrue(selected.contains(4));
+ assertTrue(selected.contains(5));
+ assertTrue(selected.contains(7));
+ assertTrue(selected.contains(8));
+ }
+
+ @Test(groups = "Functional")
+ public void testMarkColumns_toggle()
+ {
+ ColumnSelection cs = new ColumnSelection();
+ cs.addElement(1); // outside change range
+ cs.addElement(3);
+ cs.addElement(4);
+ cs.addElement(10); // outside change range
+ BitSet toMark = new BitSet();
+ toMark.set(1);
+ toMark.set(3);
+ toMark.set(6);
+ toMark.set(9);
+
+ /*
+ * toggling state of {3, 6} should leave {1, 4, 6, 10} selected
+ */
+ assertTrue(cs.markColumns(toMark, 3, 8, false, false, true));
+ List<Integer> selected = cs.getSelected();
+ assertEquals(4, selected.size());
+ assertTrue(selected.contains(1));
+ assertTrue(selected.contains(4));
+ assertTrue(selected.contains(6));
+ assertTrue(selected.contains(10));
+ }
+
+ @Test(groups = "Functional")
+ public void testCopyConstructor()
+ {
+ ColumnSelection cs = new ColumnSelection();
+ cs.addElement(3);
+ cs.addElement(1);
+
+ ColumnSelection cs2 = new ColumnSelection(cs);
+ assertTrue(cs2.hasSelectedColumns());
+
+ // order of column selection is preserved
+ assertEquals("[3, 1]", cs2.getSelected().toString());
+ }
+
+ @Test(groups = { "Functional" })
+ public void testStretchGroup_expand()
+ {
+ /*
+ * test that emulates clicking column 4 (selected)
+ * and dragging right to column 5 (all base 0)
+ */
+ ColumnSelection cs = new ColumnSelection();
+ cs.addElement(4);
+ SequenceGroup sg = new SequenceGroup();
+ sg.setStartRes(4);
+ sg.setEndRes(4);
+ cs.stretchGroup(5, sg, 4, 4);
+ assertEquals(cs.getSelected().size(), 2);
+ assertTrue(cs.contains(4));
+ assertTrue(cs.contains(5));
+ assertEquals(sg.getStartRes(), 4);
+ assertEquals(sg.getEndRes(), 5);
+
+ /*
+ * emulate drag right with columns 10-20 already selected
+ */
+ cs.clear();
+ for (int i = 10; i <= 20; i++)
+ {
+ cs.addElement(i);
+ }
+ assertEquals(cs.getSelected().size(), 11);
+ sg = new SequenceGroup();
+ sg.setStartRes(10);
+ sg.setEndRes(20);
+ cs.stretchGroup(21, sg, 10, 20);
+ assertEquals(cs.getSelected().size(), 12);
+ assertTrue(cs.contains(10));
+ assertTrue(cs.contains(21));
+ assertEquals(sg.getStartRes(), 10);
+ assertEquals(sg.getEndRes(), 21);
+ }
+
+ @Test(groups = { "Functional" })
+ public void testStretchGroup_shrink()
+ {
+ /*
+ * emulate drag left to 19 with columns 10-20 already selected
+ */
+ ColumnSelection cs = new ColumnSelection();
+ for (int i = 10; i <= 20; i++)
+ {
+ cs.addElement(i);
+ }
+ assertEquals(cs.getSelected().size(), 11);
+ SequenceGroup sg = new SequenceGroup();
+ sg.setStartRes(10);
+ sg.setEndRes(20);
+ cs.stretchGroup(19, sg, 10, 20);
+ assertEquals(cs.getSelected().size(), 10);
+ assertTrue(cs.contains(10));
+ assertTrue(cs.contains(19));
+ assertFalse(cs.contains(20));
+ assertEquals(sg.getStartRes(), 10);
+ assertEquals(sg.getEndRes(), 19);
+ }
+
+ @Test(groups = { "Functional" })
+ public void testFilterAnnotations()
+ {
+ ColumnSelection cs = new ColumnSelection();
+ AlignmentAnnotation alann = new AlignmentAnnotation("dummy",
+ "dummyDesc", null);
+
+ /*
+ * filter with no conditions clears the selection
+ */
+ Annotation[] anns = new Annotation[] { null };
+ AnnotationFilterParameter filter = new AnnotationFilterParameter();
+ cs.addElement(3);
+ alann.annotations = anns;
+ int added = cs.filterAnnotations(alann, filter);
+ assertEquals(0, added);
+ assertTrue(cs.isEmpty());
+
+ /*
+ * select on description (regex)
+ */
+ filter.setRegexString("w.rld");
+ filter.addRegexSearchField(SearchableAnnotationField.DESCRIPTION);
+ Annotation helix = new Annotation("(", "hello", '<', 2f);
+ Annotation sheet = new Annotation("(", "world", '<', 2f);
+ alann.annotations = new Annotation[] { null, helix, sheet };
+ added = cs.filterAnnotations(alann, filter);
+ assertEquals(1, added);
+ assertTrue(cs.contains(2));
+
+ /*
+ * select on label (invalid regex, exact match)
+ */
+ filter = new AnnotationFilterParameter();
+ filter.setRegexString("(");
+ filter.addRegexSearchField(SearchableAnnotationField.DISPLAY_STRING);
+ alann.annotations = new Annotation[] { null, helix, sheet };
+ added = cs.filterAnnotations(alann, filter);
+ assertEquals(2, added);
+ assertTrue(cs.contains(1));
+ assertTrue(cs.contains(2));
+
+ /*
+ * select Helix (secondary structure symbol H)
+ */
+ filter = new AnnotationFilterParameter();
+ filter.setFilterAlphaHelix(true);
+ helix = new Annotation("x", "desc", 'H', 0f);
+ sheet = new Annotation("x", "desc", 'E', 1f);
+ Annotation turn = new Annotation("x", "desc", 'S', 2f);
+ Annotation ann4 = new Annotation("x", "desc", 'Y', 3f);
+ alann.annotations = new Annotation[] { null, helix, sheet, turn, ann4 };
+ added = cs.filterAnnotations(alann, filter);
+ assertEquals(1, added);
+ assertTrue(cs.contains(1));
+
+ /*
+ * select Helix and Sheet (E)
+ */
+ filter.setFilterBetaSheet(true);
+ alann.annotations = new Annotation[] { null, helix, sheet, turn, ann4 };
+ added = cs.filterAnnotations(alann, filter);
+ assertEquals(2, added);
+ assertTrue(cs.contains(1));
+ assertTrue(cs.contains(2));
+
+ /*
+ * select Sheet and Turn (S)
+ */
+ filter.setFilterAlphaHelix(false);
+ filter.setFilterTurn(true);
+ alann.annotations = new Annotation[] { null, helix, sheet, turn, ann4 };
+ added = cs.filterAnnotations(alann, filter);
+ assertEquals(2, added);
+ assertTrue(cs.contains(2));
+ assertTrue(cs.contains(3));
+
+ /*
+ * select value < 2f (ann1, ann2)
+ */
+ filter = new AnnotationFilterParameter();
+ filter.setThresholdType(ThresholdType.BELOW_THRESHOLD);
+ filter.setThresholdValue(2f);
+ alann.annotations = new Annotation[] { null, helix, sheet, turn, ann4 };
+ added = cs.filterAnnotations(alann, filter);
+ assertEquals(2, added);
+ assertTrue(cs.contains(1));
+ assertTrue(cs.contains(2));
+
+ /*
+ * select value > 2f (ann4 only)
+ */
+ filter.setThresholdType(ThresholdType.ABOVE_THRESHOLD);
+ alann.annotations = new Annotation[] { null, helix, sheet, turn, ann4 };
+ added = cs.filterAnnotations(alann, filter);
+ assertEquals(1, added);
+ assertTrue(cs.contains(4));
+
+ /*
+ * select >2f or Helix
+ */
+ filter.setFilterAlphaHelix(true);
+ alann.annotations = new Annotation[] { null, helix, sheet, turn, ann4 };
+ added = cs.filterAnnotations(alann, filter);
+ assertEquals(2, added);
+ assertTrue(cs.contains(1));
+ assertTrue(cs.contains(4));
+
+ /*
+ * select < 1f or Helix; one annotation matches both
+ * return value should only count it once
+ */
+ filter.setThresholdType(ThresholdType.BELOW_THRESHOLD);
+ filter.setThresholdValue(1f);
+ alann.annotations = new Annotation[] { null, helix, sheet, turn, ann4 };
+ added = cs.filterAnnotations(alann, filter);
+ assertEquals(1, added);
+ assertTrue(cs.contains(1));