/*
* 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.assertNotNull;
import static org.testng.AssertJUnit.assertNotSame;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertSame;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
import jalview.gui.AlignViewport;
import jalview.gui.JvOptionPane;
import java.util.List;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
@Test(singleThreaded = true)
public class HiddenSequencesTest
{
@BeforeClass(alwaysRun = true)
public void setUpJvOptionPane()
{
JvOptionPane.setInteractiveMode(false);
JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
}
static int SEQ_COUNT = 25;
SequenceI[] seqs;
/**
* Set up an alignment of 10 sequences
*/
@BeforeTest(alwaysRun = true)
public void setUp()
{
seqs = new SequenceI[SEQ_COUNT];
for (int i = 0; i < SEQ_COUNT; i++)
{
// sequence lengths are 1, 2, ... 25
seqs[i] = new Sequence("Seq" + i,
"abcdefghijklmnopqrstuvwxy".substring(0, i + 1));
}
}
/**
* Test the method that converts sequence alignment index to what it would be
* if all sequences were unhidden
*/
@Test(groups = "Functional")
public void testAdjustForHiddenSeqs()
{
AlignmentI al = new Alignment(seqs);
HiddenSequences hs = al.getHiddenSequences();
for (int i = 0; i < SEQ_COUNT; i++)
{
assertEquals(i, hs.adjustForHiddenSeqs(i));
}
// hide seq1 and seq5 and seq6
hs.hideSequence(seqs[1]);
hs.hideSequence(seqs[5]);
hs.hideSequence(seqs[6]);
/*
* alignment is now seq0/2/3/4/7/8/9
*/
assertEquals(SEQ_COUNT - 3, al.getHeight());
assertEquals(0, hs.adjustForHiddenSeqs(0));
assertEquals(2, hs.adjustForHiddenSeqs(1));
assertEquals(3, hs.adjustForHiddenSeqs(2));
assertEquals(4, hs.adjustForHiddenSeqs(3));
assertEquals(7, hs.adjustForHiddenSeqs(4));
assertEquals(8, hs.adjustForHiddenSeqs(5));
assertEquals(9, hs.adjustForHiddenSeqs(6));
}
/**
* Test the method that increments the internal array size if a sequence is
* added to the alignment (ugh this should not be exposed to the light of day)
*/
@Test(groups = "Functional")
public void testAdjustHeightSequenceAdded()
{
AlignmentI al = new Alignment(seqs);
assertEquals(SEQ_COUNT, al.getHeight());
HiddenSequences hs = al.getHiddenSequences();
// initially does nothing
hs.adjustHeightSequenceAdded();
assertNull(hs.hiddenSequences);
// hide one sequence
hs.hideSequence(seqs[3]);
assertEquals(1, hs.getSize());
assertEquals(SEQ_COUNT - 1, al.getHeight());
assertEquals(SEQ_COUNT, hs.hiddenSequences.length);
/*
* add a sequence to the alignment
* - the safe way to call hs.adjustHeightSequenceAdded!
* (implementation depends on alignment height having
* been already updated for the added sequence)
*/
al.addSequence(new Sequence("a", "b"));
assertEquals(1, hs.getSize());
assertEquals(SEQ_COUNT, al.getHeight());
assertEquals(SEQ_COUNT + 1, hs.hiddenSequences.length);
}
/**
* Test the method that decrements the internal array size if a sequence is
* deleted from the alignment (ugh this should not be exposed to the light of
* day)
*/
@Test(groups = "Functional")
public void testAdjustHeightSequenceDeleted()
{
AlignmentI al = new Alignment(seqs);
assertEquals(SEQ_COUNT, al.getHeight());
HiddenSequences hs = al.getHiddenSequences();
// initially does nothing
hs.adjustHeightSequenceAdded();
assertNull(hs.hiddenSequences);
// hide two sequences
hs.hideSequence(seqs[3]);
hs.hideSequence(seqs[5]);
assertEquals(2, hs.getSize());
assertTrue(hs.isHidden(seqs[3]));
assertTrue(hs.isHidden(seqs[5]));
assertEquals(SEQ_COUNT - 2, al.getHeight());
assertEquals(SEQ_COUNT, hs.hiddenSequences.length);
/*
* delete a visible sequence from the alignment
* - the safe way to call hs.adjustHeightSequenceDeleted!
* (implementation depends on alignment height having
* been already updated for the removed sequence)
*/
al.deleteSequence(seqs[2]);
assertEquals(2, hs.getSize());
// the visible alignment is unchanged:
assertEquals(SEQ_COUNT - 3, al.getHeight());
// sequences array size has decremented:
assertEquals(SEQ_COUNT - 1, hs.hiddenSequences.length);
}
/**
* Test the method that converts a 'full alignment' sequence index into the
* equivalent in the alignment with sequences hidden
*/
@Test(groups = "Functional")
public void testFindIndexWithoutHiddenSeqs()
{
AlignmentI al = new Alignment(seqs);
HiddenSequences hs = al.getHiddenSequences();
for (int i = 0; i < SEQ_COUNT; i++)
{
assertEquals(i, hs.findIndexWithoutHiddenSeqs(i));
}
// hide seq1 and seq5 and seq6
hs.hideSequence(seqs[1]);
hs.hideSequence(seqs[5]);
hs.hideSequence(seqs[6]);
/*
* alignment is now seq0/2/3/4/7/8/9
*/
assertEquals(SEQ_COUNT - 3, al.getHeight());
assertEquals(0, hs.findIndexWithoutHiddenSeqs(0));
assertEquals(0, hs.findIndexWithoutHiddenSeqs(1));
assertEquals(1, hs.findIndexWithoutHiddenSeqs(2));
assertEquals(2, hs.findIndexWithoutHiddenSeqs(3));
assertEquals(3, hs.findIndexWithoutHiddenSeqs(4));
assertEquals(3, hs.findIndexWithoutHiddenSeqs(5));
assertEquals(3, hs.findIndexWithoutHiddenSeqs(6));
assertEquals(4, hs.findIndexWithoutHiddenSeqs(7));
assertEquals(5, hs.findIndexWithoutHiddenSeqs(8));
assertEquals(6, hs.findIndexWithoutHiddenSeqs(9));
}
/**
* Test the method that finds the visible row position a given distance before
* another row
*/
@Test(groups = { "Functional" })
public void testFindIndexNFromRow()
{
AlignmentI al = new Alignment(seqs);
HiddenSequences hs = new HiddenSequences(al);
// test that without hidden rows, findIndexNFromRow returns
// position n above provided position
int pos = hs.subtractVisibleRows(3, 10);
assertEquals(7, pos);
// 0 returns same position
pos = hs.subtractVisibleRows(0, 10);
assertEquals(10, pos);
// overflow to top returns negative number
pos = hs.subtractVisibleRows(3, 0);
assertEquals(-3, pos);
// test that with hidden rows above result row
// behaviour is the same as above
hs.hideSequence(seqs[1]);
hs.hideSequence(seqs[2]);
hs.hideSequence(seqs[3]);
// position n above provided position
pos = hs.subtractVisibleRows(3, 10);
assertEquals(7, pos);
// 0 returns same position
pos = hs.subtractVisibleRows(0, 10);
assertEquals(10, pos);
// test with one set of hidden rows between start and required position
hs.hideSequence(seqs[12]);
hs.hideSequence(seqs[13]);
hs.hideSequence(seqs[14]);
hs.hideSequence(seqs[15]);
pos = hs.subtractVisibleRows(8, 17);
assertEquals(5, pos);
// test with two sets of hidden rows between start and required position
hs.hideSequence(seqs[20]);
hs.hideSequence(seqs[21]);
pos = hs.subtractVisibleRows(8, 23);
assertEquals(9, pos);
// repeat last 2 tests with no hidden columns to left of required position
hs.showAll(null);
// test with one set of hidden rows between start and required position
hs.hideSequence(seqs[12]);
hs.hideSequence(seqs[13]);
hs.hideSequence(seqs[14]);
hs.hideSequence(seqs[15]);
pos = hs.subtractVisibleRows(8, 17);
assertEquals(5, pos);
// test with two sets of hidden rows between start and required position
hs.hideSequence(seqs[20]);
hs.hideSequence(seqs[21]);
pos = hs.subtractVisibleRows(8, 23);
assertEquals(9, pos);
}
/**
* Test the method that reconstructs (sort of) the full alignment including
* hidden sequences
*/
@Test(groups = "Functional")
public void testGetFullAlignment()
{
AlignmentI al = new Alignment(seqs);
assertArrayEquals(seqs, al.getSequencesArray());
al.setProperty("a", "b");
al.addAnnotation(new AlignmentAnnotation("ann", "label", 12f));
al.setSeqrep(seqs[4]);
SequenceGroup sg = new SequenceGroup();
sg.addSequence(seqs[8], false);
al.addGroup(sg);
((Alignment) al).hasRNAStructure = true;
HiddenSequences hs = al.getHiddenSequences();
AlignmentI al2 = hs.getFullAlignment();
// new alignment but with original sequences
assertNotSame(al, al2);
assertArrayEquals(al.getSequencesArray(), al2.getSequencesArray());
hs.hideSequence(seqs[4]);
hs.hideSequence(seqs[9]);
al2 = hs.getFullAlignment();
assertNotSame(al, al2);
assertArrayEquals(seqs, al2.getSequencesArray());
assertNotNull(al2.getProperties());
assertSame(al.getProperties(), al2.getProperties());
assertNotNull(al2.getAlignmentAnnotation());
assertSame(al.getAlignmentAnnotation(), al2.getAlignmentAnnotation());
assertSame(seqs[4], al2.getSeqrep());
assertNotNull(al2.getGroups());
assertSame(al.getGroups(), al2.getGroups());
assertTrue(al2.hasRNAStructure());
}
/**
* Test the method that returns the hidden sequence at a given index in the
* full alignment
*
* @return either the sequence (if hidden) or null (if not hidden)
*/
@Test(groups = "Functional")
public void testGetHiddenSequence()
{
AlignmentI al = new Alignment(seqs);
HiddenSequences hs = al.getHiddenSequences();
assertNull(hs.getHiddenSequence(0));
hs.hideSequence(seqs[3]);
assertSame(seqs[3], hs.getHiddenSequence(3));
assertNull(hs.getHiddenSequence(2));
assertNull(hs.getHiddenSequence(4));
}
@Test(groups = "Functional")
public void testGetSize()
{
}
@Test(groups = "Functional")
public void testGetWidth()
{
AlignmentI al = new Alignment(seqs);
HiddenSequences hs = al.getHiddenSequences();
assertEquals(0, hs.getWidth());
hs.hideSequence(seqs[6]);
hs.hideSequence(seqs[8]);
assertEquals(9, hs.getWidth());
}
/**
* Test the method that adds a sequence to the hidden sequences and deletes it
* from the alignment, and its converse
*/
@Test(groups = "Functional")
public void testHideShowSequence()
{
AlignmentI al = new Alignment(seqs);
assertTrue(al.getSequences().contains(seqs[1]));
HiddenSequences hs = al.getHiddenSequences();
assertEquals(0, hs.getSize());
assertEquals(SEQ_COUNT, al.getHeight());
/*
* hide the second sequence in the alignment
*/
hs.hideSequence(seqs[1]);
assertFalse(hs.isHidden(seqs[0]));
assertTrue(hs.isHidden(seqs[1]));
assertFalse(al.getSequences().contains(seqs[1]));
assertEquals(1, hs.getSize());
assertEquals(SEQ_COUNT - 1, al.getHeight());
assertSame(seqs[2], al.getSequenceAt(1));
/*
* hide what is now the second sequence in the alignment
*/
hs.hideSequence(seqs[2]);
assertFalse(hs.isHidden(seqs[0]));
assertTrue(hs.isHidden(seqs[1]));
assertTrue(hs.isHidden(seqs[2]));
assertFalse(al.getSequences().contains(seqs[1]));
assertFalse(al.getSequences().contains(seqs[2]));
assertEquals(2, hs.getSize());
assertEquals(SEQ_COUNT - 2, al.getHeight());
/*
* perform 'reveal' on what is now the second sequence in the alignment
* this should unhide the two sequences that precede it
*/
List revealed = hs.showSequence(1, null);
assertEquals(2, revealed.size());
assertTrue(revealed.contains(seqs[1]));
assertTrue(revealed.contains(seqs[2]));
assertEquals(0, hs.getSize());
assertEquals(SEQ_COUNT, al.getHeight());
}
/**
* Test the method that adds a sequence to the hidden sequences and deletes it
* from the alignment, and its converse, where the first hidden sequences are
* at the bottom of the alignment (JAL-2437)
*/
@Test(groups = "Functional")
public void testHideShowLastSequences()
{
AlignmentI al = new Alignment(seqs);
assertTrue(al.getSequences().contains(seqs[1]));
HiddenSequences hs = al.getHiddenSequences();
assertEquals(0, hs.getSize());
assertEquals(SEQ_COUNT, al.getHeight());
/*
* hide the last sequence in the alignment
*/
hs.hideSequence(seqs[SEQ_COUNT - 1]);
assertFalse(hs.isHidden(seqs[SEQ_COUNT - 2]));
assertTrue(hs.isHidden(seqs[SEQ_COUNT - 1]));
assertFalse(al.getSequences().contains(seqs[SEQ_COUNT - 1]));
assertEquals(1, hs.getSize());
assertEquals(SEQ_COUNT - 1, al.getHeight());
/*
* hide the third last sequence in the alignment
*/
hs.hideSequence(seqs[SEQ_COUNT - 3]);
assertFalse(hs.isHidden(seqs[SEQ_COUNT - 2]));
assertTrue(hs.isHidden(seqs[SEQ_COUNT - 3]));
assertFalse(al.getSequences().contains(seqs[SEQ_COUNT - 3]));
assertEquals(2, hs.getSize());
assertEquals(SEQ_COUNT - 2, al.getHeight());
/*
* reveal all the sequences, which should be reinstated in the same order as they started in
*/
hs.showAll(null);
assertFalse(hs.isHidden(seqs[SEQ_COUNT - 3]));
assertFalse(hs.isHidden(seqs[SEQ_COUNT - 1]));
assertEquals(seqs[SEQ_COUNT - 3], al.getSequences().get(SEQ_COUNT - 3));
assertEquals(seqs[SEQ_COUNT - 2], al.getSequences().get(SEQ_COUNT - 2));
assertEquals(seqs[SEQ_COUNT - 1], al.getSequences().get(SEQ_COUNT - 1));
assertEquals(0, hs.getSize());
assertEquals(SEQ_COUNT, al.getHeight());
}
@Test(groups = "Functional")
public void testIsHidden()
{
AlignmentI al = new Alignment(seqs);
HiddenSequences hs = al.getHiddenSequences();
hs.hideSequence(seqs[7]);
hs.hideSequence(seqs[4]);
assertTrue(hs.isHidden(seqs[4]));
assertFalse(hs.isHidden(seqs[5]));
assertFalse(hs.isHidden(seqs[6]));
assertTrue(hs.isHidden(seqs[7]));
assertFalse(hs.isHidden(null));
assertFalse(hs.isHidden(new Sequence("", "")));
}
/**
* Test hiding and unhiding a group with a representative sequence. The
* representative should be left visible when the group is hidden, and
* included in the selected group when it is unhidden.
*/
@Test(groups = "Functional")
public void testHideShowSequence_withHiddenRepSequence()
{
AlignmentI al = new Alignment(seqs);
/*
* represent seqs 2-4 with seq3
* this hides seq2 and seq4 but not seq3
*/
AlignViewport av = new AlignViewport(al);
SequenceGroup sg = new SequenceGroup();
sg.addSequence(seqs[1], false);
sg.addSequence(seqs[2], false);
sg.addSequence(seqs[3], false);
av.setSelectionGroup(sg);
/*
* hiding group with reference sequence is done via AlignViewport
*/
av.hideSequences(seqs[2], true);
HiddenSequences hs = al.getHiddenSequences();
assertEquals(2, hs.getSize());
assertTrue(hs.isHidden(seqs[1]));
assertFalse(hs.isHidden(seqs[2]));
assertTrue(hs.isHidden(seqs[3]));
/*
* should now be no sequences selected in the alignment
*/
assertNull(av.getSelectionGroup());
/*
* visible alignment is now seq0/2/4/5/6/7/8/9
* 'reveal sequences' at the representative sequence (index = 1)
* this should unhide the one above i.e. seq1
* and return a selection list including seq2
*
* note have to call via AlignViewport to get the expected
* resulting sequence selection
*/
av.showSequence(1);
/*
* only seq3 is now hidden
*/
assertEquals(1, hs.getSize());
assertTrue(hs.isHidden(seqs[3]));
assertEquals(SEQ_COUNT - 1, al.getHeight());
sg = av.getSelectionGroup();
/*
* unhidden and representative sequence selected
* (this behaviour may change! JAL-2133)
*/
assertEquals(2, sg.getSize());
assertTrue(sg.getSequences().contains(seqs[1]));
assertTrue(sg.getSequences().contains(seqs[2]));
assertFalse(sg.getSequences().contains(seqs[3]));
}
}