/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.datamodel; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertSame; import static org.testng.AssertJUnit.assertTrue; import jalview.analysis.AlignmentGenerator; import jalview.gui.JvOptionPane; import java.util.Arrays; import java.util.BitSet; import java.util.List; import java.util.Random; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; public class HiddenColumnsTest { @BeforeClass(alwaysRun = true) public void setUpJvOptionPane() { JvOptionPane.setInteractiveMode(false); JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); } /** * Test the method which counts the number of hidden columns */ @Test(groups = { "Functional" }) public void testGetSize() { HiddenColumns hidden = new HiddenColumns(); assertEquals(0, hidden.getSize()); hidden.hideColumns(3, 5); assertEquals(3, hidden.getSize()); hidden.hideColumns(8, 8); assertEquals(4, hidden.getSize()); hidden.hideColumns(9, 14); assertEquals(10, hidden.getSize()); ColumnSelection cs = new ColumnSelection(); hidden.revealAllHiddenColumns(cs); assertEquals(0, hidden.getSize()); } /** * Test the method that finds the visible column position of an alignment * column, allowing for hidden columns. */ @Test(groups = { "Functional" }) public void testFindColumnPosition() { HiddenColumns cs = new HiddenColumns(); assertEquals(5, cs.findColumnPosition(5)); // hiding column 6 makes no difference cs.hideColumns(6, 6); assertEquals(5, cs.findColumnPosition(5)); // hiding column 4 moves column 5 to column 4 cs.hideColumns(4, 4); assertEquals(4, cs.findColumnPosition(5)); // hiding column 4 moves column 4 to position 3 assertEquals(3, cs.findColumnPosition(4)); // hiding columns 1 and 2 moves column 5 to column 2 cs.hideColumns(1, 2); assertEquals(2, cs.findColumnPosition(5)); // check with > 1 hidden column regions // where some columns are in the hidden regions HiddenColumns cs2 = new HiddenColumns(); cs2.hideColumns(5, 10); cs2.hideColumns(20, 27); cs2.hideColumns(40, 44); // hiding columns 5-10 and 20-27 moves column 8 to column 4 assertEquals(4, cs2.findColumnPosition(8)); // and moves column 24 to 13 assertEquals(13, cs2.findColumnPosition(24)); // and moves column 28 to 14 assertEquals(14, cs2.findColumnPosition(28)); // and moves column 40 to 25 assertEquals(25, cs2.findColumnPosition(40)); // check when hidden columns start at 0 that the visible column // is returned as 0 HiddenColumns cs3 = new HiddenColumns(); cs3.hideColumns(0, 4); assertEquals(0, cs3.findColumnPosition(2)); } /** * Test the method that finds the visible column position a given distance * before another column */ @Test(groups = { "Functional" }) public void testFindColumnNToLeft() { HiddenColumns cs = new HiddenColumns(); // test that without hidden columns, findColumnNToLeft returns // position n to left of provided position int pos = cs.subtractVisibleColumns(3, 10); assertEquals(7, pos); // 0 returns same position pos = cs.subtractVisibleColumns(0, 10); assertEquals(10, pos); // overflow to left returns negative number pos = cs.subtractVisibleColumns(3, 0); assertEquals(-3, pos); // test that with hidden columns to left of result column // behaviour is the same as above cs.hideColumns(1, 3); // position n to left of provided position pos = cs.subtractVisibleColumns(3, 10); assertEquals(7, pos); // 0 returns same position pos = cs.subtractVisibleColumns(0, 10); assertEquals(10, pos); // test with one set of hidden columns between start and required position cs.hideColumns(12, 15); pos = cs.subtractVisibleColumns(8, 17); assertEquals(5, pos); // test with two sets of hidden columns between start and required position cs.hideColumns(20, 21); pos = cs.subtractVisibleColumns(8, 23); assertEquals(9, pos); // repeat last 2 tests with no hidden columns to left of required position ColumnSelection colsel = new ColumnSelection(); cs.revealAllHiddenColumns(colsel); // test with one set of hidden columns between start and required position cs.hideColumns(12, 15); pos = cs.subtractVisibleColumns(8, 17); assertEquals(5, pos); // test with two sets of hidden columns between start and required position cs.hideColumns(20, 21); pos = cs.subtractVisibleColumns(8, 23); assertEquals(9, pos); } @Test(groups = { "Functional" }) public void testGetVisibleContigs() { HiddenColumns cs = new HiddenColumns(); cs.hideColumns(3, 6); cs.hideColumns(8, 9); cs.hideColumns(12, 12); // start position is inclusive, end position exclusive: int[] visible = cs.getVisibleContigs(1, 13); assertEquals("[1, 2, 7, 7, 10, 11]", Arrays.toString(visible)); visible = cs.getVisibleContigs(4, 14); assertEquals("[7, 7, 10, 11, 13, 13]", Arrays.toString(visible)); visible = cs.getVisibleContigs(3, 10); assertEquals("[7, 7]", Arrays.toString(visible)); visible = cs.getVisibleContigs(4, 6); assertEquals("[]", Arrays.toString(visible)); } @Test(groups = { "Functional" }) public void testEquals() { HiddenColumns cs = new HiddenColumns(); cs.hideColumns(5, 9); // a different set of hidden columns HiddenColumns cs2 = new HiddenColumns(); // with no hidden columns assertFalse(cs.equals(cs2)); assertFalse(cs2.equals(cs)); // with hidden columns added in a different order cs2.hideColumns(6, 9); cs2.hideColumns(5, 8); assertTrue(cs.equals(cs2)); assertTrue(cs.equals(cs)); assertTrue(cs2.equals(cs)); assertTrue(cs2.equals(cs2)); } @Test(groups = "Functional") public void testCopyConstructor() { HiddenColumns cs = new HiddenColumns(); cs.hideColumns(10, 11); cs.hideColumns(5, 7); assertEquals("[5, 7]", Arrays.toString(cs.getHiddenRegions().get(0))); HiddenColumns cs2 = new HiddenColumns(cs); assertTrue(cs2.hasHiddenColumns()); assertEquals(2, cs2.getHiddenRegions().size()); // hidden columns are held in column order assertEquals("[5, 7]", Arrays.toString(cs2.getHiddenRegions().get(0))); assertEquals("[10, 11]", Arrays.toString(cs2.getHiddenRegions().get(1))); } /** * Test the code used to locate the reference sequence ruler origin */ @Test(groups = { "Functional" }) public void testLocateVisibleBoundsofSequence() { // create random alignment AlignmentGenerator gen = new AlignmentGenerator(false); AlignmentI al = gen.generate(50, 20, 123, 5, 5); HiddenColumns cs = al.getHiddenColumns(); ColumnSelection colsel = new ColumnSelection(); SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---"); assertEquals(2, seq.findIndex(seq.getStart())); // no hidden columns assertEquals( Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1, seq.findIndex(seq.getEnd()) - 1, seq.getStart(), seq.getEnd(), seq.findIndex(seq.getStart()) - 1, seq.findIndex(seq.getEnd()) - 1 }), Arrays.toString(cs.locateVisibleBoundsOfSequence(seq))); // hidden column on gap after end of sequence - should not affect bounds colsel.hideSelectedColumns(13, al.getHiddenColumns()); assertEquals( Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1, seq.findIndex(seq.getEnd()) - 1, seq.getStart(), seq.getEnd(), seq.findIndex(seq.getStart()) - 1, seq.findIndex(seq.getEnd()) - 1 }), Arrays.toString(cs.locateVisibleBoundsOfSequence(seq))); cs.revealAllHiddenColumns(colsel); // hidden column on gap before beginning of sequence - should vis bounds by // one colsel.hideSelectedColumns(0, al.getHiddenColumns()); assertEquals( Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 2, seq.findIndex(seq.getEnd()) - 2, seq.getStart(), seq.getEnd(), seq.findIndex(seq.getStart()) - 1, seq.findIndex(seq.getEnd()) - 1 }), Arrays.toString(cs.locateVisibleBoundsOfSequence(seq))); cs.revealAllHiddenColumns(colsel); // hide columns around most of sequence - leave one residue remaining cs.hideColumns(1, 3); cs.hideColumns(6, 11); assertEquals("-D", cs.getVisibleSequenceStrings(0, 5, new SequenceI[] { seq })[0]); assertEquals( Arrays.toString(new int[] { 1, 1, 3, 3, seq.findIndex(seq.getStart()) - 1, seq.findIndex(seq.getEnd()) - 1 }), Arrays.toString(cs.locateVisibleBoundsOfSequence(seq))); cs.revealAllHiddenColumns(colsel); // hide whole sequence - should just get location of hidden region // containing sequence cs.hideColumns(1, 11); assertEquals( Arrays.toString(new int[] { 0, 1, 0, 0, seq.findIndex(seq.getStart()) - 1, seq.findIndex(seq.getEnd()) - 1 }), Arrays.toString(cs.locateVisibleBoundsOfSequence(seq))); } @Test(groups = { "Functional" }) public void testLocateVisibleBoundsPathologicals() { // test some pathological cases we missed AlignmentI al = new Alignment(new SequenceI[] { new Sequence( "refseqGaptest", "KTDVTI----------NFI-----G----L") }); HiddenColumns cs = new HiddenColumns(); cs.hideInsertionsFor(al.getSequenceAt(0)); assertEquals( "G", "" + al.getSequenceAt(0).getCharAt( cs.adjustForHiddenColumns(9))); } @Test(groups = { "Functional" }) public void testHideColumns() { // create random alignment AlignmentGenerator gen = new AlignmentGenerator(false); AlignmentI al = gen.generate(50, 20, 123, 5, 5); ColumnSelection colsel = new ColumnSelection(); HiddenColumns cs = al.getHiddenColumns(); colsel.hideSelectedColumns(5, al.getHiddenColumns()); List hidden = cs.getHiddenRegions(); assertEquals(1, hidden.size()); assertEquals("[5, 5]", Arrays.toString(hidden.get(0))); colsel.hideSelectedColumns(3, al.getHiddenColumns()); assertEquals(2, hidden.size()); // two hidden ranges, in order: assertSame(hidden, cs.getHiddenRegions()); assertEquals("[3, 3]", Arrays.toString(hidden.get(0))); assertEquals("[5, 5]", Arrays.toString(hidden.get(1))); // hiding column 4 expands [3, 3] to [3, 4] // and merges to [5, 5] to make [3, 5] colsel.hideSelectedColumns(4, al.getHiddenColumns()); hidden = cs.getHiddenRegions(); assertEquals(1, hidden.size()); assertEquals("[3, 5]", Arrays.toString(hidden.get(0))); // clear hidden columns (note they are added to selected) cs.revealAllHiddenColumns(colsel); // it is now actually null but getter returns an empty list assertTrue(cs.getHiddenRegions().isEmpty()); cs.hideColumns(3, 6); hidden = cs.getHiddenRegions(); int[] firstHiddenRange = hidden.get(0); assertEquals("[3, 6]", Arrays.toString(firstHiddenRange)); // adding a subrange of already hidden should do nothing cs.hideColumns(4, 5); assertEquals(1, hidden.size()); assertSame(firstHiddenRange, cs.getHiddenRegions().get(0)); cs.hideColumns(3, 5); assertEquals(1, hidden.size()); assertSame(firstHiddenRange, cs.getHiddenRegions().get(0)); cs.hideColumns(4, 6); assertEquals(1, hidden.size()); assertSame(firstHiddenRange, cs.getHiddenRegions().get(0)); cs.hideColumns(3, 6); assertEquals(1, hidden.size()); assertSame(firstHiddenRange, cs.getHiddenRegions().get(0)); cs.revealAllHiddenColumns(colsel); cs.hideColumns(2, 4); hidden = cs.getHiddenRegions(); assertEquals(1, hidden.size()); assertEquals("[2, 4]", Arrays.toString(hidden.get(0))); // extend contiguous with 2 positions overlap cs.hideColumns(3, 5); assertEquals(1, hidden.size()); assertEquals("[2, 5]", Arrays.toString(hidden.get(0))); // extend contiguous with 1 position overlap cs.hideColumns(5, 6); assertEquals(1, hidden.size()); assertEquals("[2, 6]", Arrays.toString(hidden.get(0))); // extend contiguous with overlap both ends: cs.hideColumns(1, 7); assertEquals(1, hidden.size()); assertEquals("[1, 7]", Arrays.toString(hidden.get(0))); } /** * Test the method that reveals a range of hidden columns given the start * column of the range */ @Test(groups = { "Functional" }) public void testRevealHiddenColumns() { ColumnSelection colsel = new ColumnSelection(); HiddenColumns cs = new HiddenColumns(); cs.hideColumns(5, 8); colsel.addElement(10); cs.revealHiddenColumns(5, colsel); // hidden columns list now null but getter returns empty list: assertTrue(cs.getHiddenRegions().isEmpty()); // revealed columns are marked as selected (added to selection): assertEquals("[10, 5, 6, 7, 8]", colsel.getSelected().toString()); // calling with a column other than the range start does nothing: colsel = new ColumnSelection(); cs = new HiddenColumns(); cs.hideColumns(5, 8); List hidden = cs.getHiddenRegions(); cs.revealHiddenColumns(6, colsel); assertSame(hidden, cs.getHiddenRegions()); assertTrue(colsel.getSelected().isEmpty()); } @Test(groups = { "Functional" }) public void testRevealAllHiddenColumns() { HiddenColumns cs = new HiddenColumns(); ColumnSelection colsel = new ColumnSelection(); cs.hideColumns(5, 8); cs.hideColumns(2, 3); colsel.addElement(11); colsel.addElement(1); cs.revealAllHiddenColumns(colsel); /* * revealing hidden columns adds them (in order) to the (unordered) * selection list */ assertTrue(cs.getHiddenRegions().isEmpty()); assertEquals("[11, 1, 2, 3, 5, 6, 7, 8]", colsel.getSelected() .toString()); } @Test(groups = { "Functional" }) public void testIsVisible() { HiddenColumns cs = new HiddenColumns(); cs.hideColumns(2, 4); cs.hideColumns(6, 7); assertTrue(cs.isVisible(0)); assertTrue(cs.isVisible(-99)); assertTrue(cs.isVisible(1)); assertFalse(cs.isVisible(2)); assertFalse(cs.isVisible(3)); assertFalse(cs.isVisible(4)); assertTrue(cs.isVisible(5)); assertFalse(cs.isVisible(6)); assertFalse(cs.isVisible(7)); } /** * Test for the case when a hidden range encloses more one already hidden * range */ @Test(groups = { "Functional" }) public void testHideColumns_subsumingHidden() { /* * JAL-2370 bug scenario: * two hidden ranges subsumed by a third */ HiddenColumns cs = new HiddenColumns(); cs.hideColumns(49, 59); cs.hideColumns(69, 79); List hidden = cs.getHiddenRegions(); assertEquals(2, hidden.size()); assertEquals("[49, 59]", Arrays.toString(hidden.get(0))); assertEquals("[69, 79]", Arrays.toString(hidden.get(1))); cs.hideColumns(48, 80); hidden = cs.getHiddenRegions(); assertEquals(1, hidden.size()); assertEquals("[48, 80]", Arrays.toString(hidden.get(0))); /* * another...joining hidden ranges */ cs = new HiddenColumns(); cs.hideColumns(10, 20); cs.hideColumns(30, 40); cs.hideColumns(50, 60); // hiding 21-49 should merge to one range cs.hideColumns(21, 49); hidden = cs.getHiddenRegions(); assertEquals(1, hidden.size()); assertEquals("[10, 60]", Arrays.toString(hidden.get(0))); /* * another...left overlap, subsumption, right overlap, * no overlap of existing hidden ranges */ cs = new HiddenColumns(); cs.hideColumns(10, 20); cs.hideColumns(10, 20); cs.hideColumns(30, 35); cs.hideColumns(40, 50); cs.hideColumns(60, 70); cs.hideColumns(15, 45); hidden = cs.getHiddenRegions(); assertEquals(2, hidden.size()); assertEquals("[10, 50]", Arrays.toString(hidden.get(0))); assertEquals("[60, 70]", Arrays.toString(hidden.get(1))); } @Test(groups = { "Functional" }) public void testHideBitset() { HiddenColumns cs; BitSet one = new BitSet(); // one hidden range one.set(1); cs = new HiddenColumns(); cs.hideMarkedBits(one); assertEquals(1, cs.getHiddenRegions().size()); one.set(2); cs = new HiddenColumns(); cs.hideMarkedBits(one); assertEquals(1, cs.getHiddenRegions().size()); one.set(3); cs = new HiddenColumns(); cs.hideMarkedBits(one); assertEquals(1, cs.getHiddenRegions().size()); // split one.clear(2); cs = new HiddenColumns(); cs.hideMarkedBits(one); assertEquals(2, cs.getHiddenRegions().size()); assertEquals(0, cs.adjustForHiddenColumns(0)); assertEquals(2, cs.adjustForHiddenColumns(1)); assertEquals(4, cs.adjustForHiddenColumns(2)); // one again one.clear(1); cs = new HiddenColumns(); cs.hideMarkedBits(one); assertEquals(1, cs.getHiddenRegions().size()); assertEquals(0, cs.adjustForHiddenColumns(0)); assertEquals(1, cs.adjustForHiddenColumns(1)); assertEquals(2, cs.adjustForHiddenColumns(2)); assertEquals(4, cs.adjustForHiddenColumns(3)); } @Test(groups = { "Functional" }) public void testGetBitset() { BitSet toMark, fromMark; long seed = -3241532; Random number = new Random(seed); for (int n = 0; n < 1000; n++) { // create a random bitfield toMark = BitSet.valueOf(new long[] { number.nextLong(), number.nextLong(), number.nextLong() }); toMark.set(n * number.nextInt(10), n * (25 + number.nextInt(25))); HiddenColumns hc = new HiddenColumns(); hc.hideMarkedBits(toMark); // see if we can recover bitfield hc.markHiddenRegions(fromMark = new BitSet()); assertEquals(toMark, fromMark); } } }