+
+ @Test(groups = "Functional")
+ public void testCopyConstructor() throws IOException
+ {
+ AlignmentI protein = loadAlignment(AA_SEQS_1, FileFormat.Fasta);
+ // create sequence and alignment datasets
+ protein.setDataset(null);
+ AlignedCodonFrame acf = new AlignedCodonFrame();
+ List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
+ { acf });
+ protein.getDataset().setCodonFrames(acfList);
+ AlignmentI copy = new Alignment(protein);
+
+ /*
+ * copy has different aligned sequences but the same dataset sequences
+ */
+ assertFalse(copy.getSequenceAt(0) == protein.getSequenceAt(0));
+ assertFalse(copy.getSequenceAt(1) == protein.getSequenceAt(1));
+ assertSame(copy.getSequenceAt(0).getDatasetSequence(), protein
+ .getSequenceAt(0).getDatasetSequence());
+ assertSame(copy.getSequenceAt(1).getDatasetSequence(), protein
+ .getSequenceAt(1).getDatasetSequence());
+
+ // TODO should the copy constructor copy the dataset?
+ // or make a new one referring to the same dataset sequences??
+ assertNull(copy.getDataset());
+ // TODO test metadata is copied when AlignmentI is a dataset
+
+ // assertArrayEquals(copy.getDataset().getSequencesArray(), protein
+ // .getDataset().getSequencesArray());
+ }
+
+ /**
+ * Test behaviour of createDataset
+ *
+ * @throws IOException
+ */
+ @Test(groups = "Functional")
+ public void testCreateDatasetAlignment() throws IOException
+ {
+ AlignmentI protein = new FormatAdapter().readFile(AA_SEQS_1,
+ DataSourceType.PASTE, FileFormat.Fasta);
+ /*
+ * create a dataset sequence on first sequence
+ * leave the second without one
+ */
+ protein.getSequenceAt(0).createDatasetSequence();
+ assertNotNull(protein.getSequenceAt(0).getDatasetSequence());
+ assertNull(protein.getSequenceAt(1).getDatasetSequence());
+
+ /*
+ * add a mapping to the alignment
+ */
+ AlignedCodonFrame acf = new AlignedCodonFrame();
+ protein.addCodonFrame(acf);
+ assertNull(protein.getDataset());
+ assertTrue(protein.getCodonFrames().contains(acf));
+
+ /*
+ * create the alignment dataset
+ * note this creates sequence datasets where missing
+ * as a side-effect (in this case, on seq2
+ */
+ // TODO promote this method to AlignmentI
+ ((Alignment) protein).createDatasetAlignment();
+
+ AlignmentI ds = protein.getDataset();
+
+ // side-effect: dataset created on second sequence
+ assertNotNull(protein.getSequenceAt(1).getDatasetSequence());
+ // dataset alignment has references to dataset sequences
+ assertEquals(ds.getSequenceAt(0), protein.getSequenceAt(0)
+ .getDatasetSequence());
+ assertEquals(ds.getSequenceAt(1), protein.getSequenceAt(1)
+ .getDatasetSequence());
+
+ // codon frames should have been moved to the dataset
+ // getCodonFrames() should delegate to the dataset:
+ assertTrue(protein.getCodonFrames().contains(acf));
+ // prove the codon frames are indeed on the dataset:
+ assertTrue(ds.getCodonFrames().contains(acf));
+ }
+
+ /**
+ * tests the addition of *all* sequences referred to by a sequence being added
+ * to the dataset
+ */
+ @Test(groups = "Functional")
+ public void testCreateDatasetAlignmentWithMappedToSeqs()
+ {
+ // Alignment with two sequences, gapped.
+ SequenceI sq1 = new Sequence("sq1", "A--SDF");
+ SequenceI sq2 = new Sequence("sq2", "G--TRQ");
+
+ // cross-references to two more sequences.
+ DBRefEntry dbr = new DBRefEntry("SQ1", "", "sq3");
+ SequenceI sq3 = new Sequence("sq3", "VWANG");
+ dbr.setMap(new Mapping(sq3, new MapList(new int[] { 1, 4 }, new int[] {
+ 2, 5 }, 1, 1)));
+ sq1.addDBRef(dbr);
+
+ SequenceI sq4 = new Sequence("sq4", "ERKWI");
+ DBRefEntry dbr2 = new DBRefEntry("SQ2", "", "sq4");
+ dbr2.setMap(new Mapping(sq4, new MapList(new int[] { 1, 4 }, new int[] {
+ 2, 5 }, 1, 1)));
+ sq2.addDBRef(dbr2);
+ // and a 1:1 codonframe mapping between them.
+ AlignedCodonFrame alc = new AlignedCodonFrame();
+ alc.addMap(sq1, sq2, new MapList(new int[] { 1, 4 },
+ new int[] { 1, 4 }, 1, 1));
+
+ AlignmentI protein = new Alignment(new SequenceI[] { sq1, sq2 });
+
+ /*
+ * create the alignment dataset
+ * note this creates sequence datasets where missing
+ * as a side-effect (in this case, on seq2
+ */
+
+ // TODO promote this method to AlignmentI
+ ((Alignment) protein).createDatasetAlignment();
+
+ AlignmentI ds = protein.getDataset();
+
+ // should be 4 sequences in dataset - two materialised, and two propagated
+ // from dbref
+ assertEquals(4, ds.getHeight());
+ assertTrue(ds.getSequences().contains(sq1.getDatasetSequence()));
+ assertTrue(ds.getSequences().contains(sq2.getDatasetSequence()));
+ assertTrue(ds.getSequences().contains(sq3));
+ assertTrue(ds.getSequences().contains(sq4));
+ // Should have one codon frame mapping between sq1 and sq2 via dataset
+ // sequences
+ assertEquals(ds.getCodonFrame(sq1.getDatasetSequence()),
+ ds.getCodonFrame(sq2.getDatasetSequence()));
+ }
+
+ @Test(groups = "Functional")
+ public void testAddCodonFrame()
+ {
+ AlignmentI align = new Alignment(new SequenceI[] {});
+ AlignedCodonFrame acf = new AlignedCodonFrame();
+ align.addCodonFrame(acf);
+ assertEquals(1, align.getCodonFrames().size());
+ assertTrue(align.getCodonFrames().contains(acf));
+ // can't add the same object twice:
+ align.addCodonFrame(acf);
+ assertEquals(1, align.getCodonFrames().size());
+
+ // create dataset alignment - mappings move to dataset
+ ((Alignment) align).createDatasetAlignment();
+ assertSame(align.getCodonFrames(), align.getDataset().getCodonFrames());
+ assertEquals(1, align.getCodonFrames().size());
+
+ AlignedCodonFrame acf2 = new AlignedCodonFrame();
+ align.addCodonFrame(acf2);
+ assertTrue(align.getDataset().getCodonFrames().contains(acf));
+ }
+
+ @Test(groups = "Functional")
+ public void testAddSequencePreserveDatasetIntegrity()
+ {
+ Sequence seq = new Sequence("testSeq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ Alignment align = new Alignment(new SequenceI[] { seq });
+ align.createDatasetAlignment();
+ AlignmentI ds = align.getDataset();
+ SequenceI copy = new Sequence(seq);
+ copy.insertCharAt(3, 5, '-');
+ align.addSequence(copy);
+ Assert.assertEquals(align.getDataset().getHeight(), 1,
+ "Dataset shouldn't have more than one sequence.");
+
+ Sequence seq2 = new Sequence("newtestSeq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ align.addSequence(seq2);
+ Assert.assertEquals(align.getDataset().getHeight(), 2,
+ "Dataset should now have two sequences.");
+
+ assertAlignmentDatasetRefs(align,
+ "addSequence broke dataset reference integrity");
+ }
+
+ /**
+ * Tests that dbrefs with mappings to sequence get updated if the sequence
+ * acquires a dataset sequence
+ */
+ @Test(groups = "Functional")
+ public void testCreateDataset_updateDbrefMappings()
+ {
+ SequenceI pep = new Sequence("pep", "ASD");
+ SequenceI dna = new Sequence("dna", "aaaGCCTCGGATggg");
+ SequenceI cds = new Sequence("cds", "GCCTCGGAT");
+
+ // add dbref from dna to peptide
+ DBRefEntry dbr = new DBRefEntry("UNIPROT", "", "pep");
+ dbr.setMap(new Mapping(pep, new MapList(new int[] { 4, 15 }, new int[] {
+ 1, 4 }, 3, 1)));
+ dna.addDBRef(dbr);
+
+ // add dbref from dna to peptide
+ DBRefEntry dbr2 = new DBRefEntry("UNIPROT", "", "pep");
+ dbr2.setMap(new Mapping(pep, new MapList(new int[] { 1, 12 }, new int[]
+ { 1, 4 }, 3, 1)));
+ cds.addDBRef(dbr2);
+
+ // add dbref from peptide to dna
+ DBRefEntry dbr3 = new DBRefEntry("EMBL", "", "dna");
+ dbr3.setMap(new Mapping(dna, new MapList(new int[] { 1, 4 }, new int[] {
+ 4, 15 }, 1, 3)));
+ pep.addDBRef(dbr3);
+
+ // add dbref from peptide to cds
+ DBRefEntry dbr4 = new DBRefEntry("EMBLCDS", "", "cds");
+ dbr4.setMap(new Mapping(cds, new MapList(new int[] { 1, 4 }, new int[] {
+ 1, 12 }, 1, 3)));
+ pep.addDBRef(dbr4);
+
+ AlignmentI protein = new Alignment(new SequenceI[] { pep });
+
+ /*
+ * create the alignment dataset
+ */
+ ((Alignment) protein).createDatasetAlignment();
+
+ AlignmentI ds = protein.getDataset();
+
+ // should be 3 sequences in dataset
+ assertEquals(3, ds.getHeight());
+ assertTrue(ds.getSequences().contains(pep.getDatasetSequence()));
+ assertTrue(ds.getSequences().contains(dna));
+ assertTrue(ds.getSequences().contains(cds));
+
+ /*
+ * verify peptide.cdsdbref.peptidedbref is now mapped to peptide dataset
+ */
+ List<DBRefEntry> dbRefs = pep.getDBRefs();
+ assertEquals(2, dbRefs.size());
+ assertSame(dna, dbRefs.get(0).map.to);
+ assertSame(cds, dbRefs.get(1).map.to);
+ assertEquals(1, dna.getDBRefs().size());
+ assertSame(pep.getDatasetSequence(), dna.getDBRefs().get(0).map.to);
+ assertEquals(1, cds.getDBRefs().size());
+ assertSame(pep.getDatasetSequence(), cds.getDBRefs().get(0).map.to);
+ }
+
+ @Test(groups = { "Functional" })
+ public void testFindGroup()
+ {
+ SequenceI seq1 = new Sequence("seq1", "ABCDEF---GHI");
+ SequenceI seq2 = new Sequence("seq2", "---JKLMNO---");
+ AlignmentI a = new Alignment(new SequenceI[] { seq1, seq2 });
+
+ assertNull(a.findGroup(null, 0));
+ assertNull(a.findGroup(seq1, 1));
+ assertNull(a.findGroup(seq1, -1));
+
+ /*
+ * add a group consisting of just "DEF"
+ */
+ SequenceGroup sg1 = new SequenceGroup();
+ sg1.addSequence(seq1, false);
+ sg1.setStartRes(3);
+ sg1.setEndRes(5);
+ a.addGroup(sg1);
+
+ assertNull(a.findGroup(seq1, 2)); // position not in group
+ assertNull(a.findGroup(seq1, 6)); // position not in group
+ assertNull(a.findGroup(seq2, 5)); // sequence not in group
+ assertSame(a.findGroup(seq1, 3), sg1); // yes
+ assertSame(a.findGroup(seq1, 4), sg1);
+ assertSame(a.findGroup(seq1, 5), sg1);
+
+ /*
+ * add a group consisting of
+ * EF--
+ * KLMN
+ */
+ SequenceGroup sg2 = new SequenceGroup();
+ sg2.addSequence(seq1, false);
+ sg2.addSequence(seq2, false);
+ sg2.setStartRes(4);
+ sg2.setEndRes(7);
+ a.addGroup(sg2);
+
+ assertNull(a.findGroup(seq1, 2)); // unchanged
+ assertSame(a.findGroup(seq1, 3), sg1); // unchanged
+ /*
+ * if a residue is in more than one group, method returns
+ * the first found (in order groups were added)
+ */
+ assertSame(a.findGroup(seq1, 4), sg1);
+ assertSame(a.findGroup(seq1, 5), sg1);
+
+ /*
+ * seq2 only belongs to the second group
+ */
+ assertSame(a.findGroup(seq2, 4), sg2);
+ assertSame(a.findGroup(seq2, 5), sg2);
+ assertSame(a.findGroup(seq2, 6), sg2);
+ assertSame(a.findGroup(seq2, 7), sg2);
+ assertNull(a.findGroup(seq2, 3));
+ assertNull(a.findGroup(seq2, 8));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testDeleteSequenceByIndex()
+ {
+ // create random alignment
+ AlignmentGenerator gen = new AlignmentGenerator(false);
+ AlignmentI a = gen.generate(20, 15, 123, 5, 5);
+
+ // delete sequence 10, alignment reduced by 1
+ int height = a.getAbsoluteHeight();
+ a.deleteSequence(10);
+ assertEquals(a.getAbsoluteHeight(), height - 1);
+
+ // try to delete -ve index, nothing happens
+ a.deleteSequence(-1);
+ assertEquals(a.getAbsoluteHeight(), height - 1);
+
+ // try to delete beyond end of alignment, nothing happens
+ a.deleteSequence(14);
+ assertEquals(a.getAbsoluteHeight(), height - 1);
+ }
+
+ @Test(groups = { "Functional" })
+ public void testDeleteSequenceBySeq()
+ {
+ // create random alignment
+ AlignmentGenerator gen = new AlignmentGenerator(false);
+ AlignmentI a = gen.generate(20, 15, 123, 5, 5);
+
+ // delete sequence 10, alignment reduced by 1
+ int height = a.getAbsoluteHeight();
+ SequenceI seq = a.getSequenceAt(10);
+ a.deleteSequence(seq);
+ assertEquals(a.getAbsoluteHeight(), height - 1);
+
+ // try to delete non-existent sequence, nothing happens
+ seq = new Sequence("cds", "GCCTCGGAT");
+ assertEquals(a.getAbsoluteHeight(), height - 1);
+ }
+
+ @Test(groups = { "Functional" })
+ public void testDeleteHiddenSequence()
+ {
+ // create random alignment
+ AlignmentGenerator gen = new AlignmentGenerator(false);
+ AlignmentI a = gen.generate(20, 15, 123, 5, 5);
+
+ // delete a sequence which is hidden, check it is NOT removed from hidden
+ // sequences
+ int height = a.getAbsoluteHeight();
+ SequenceI seq = a.getSequenceAt(2);
+ a.getHiddenSequences().hideSequence(seq);
+ assertEquals(a.getHiddenSequences().getSize(), 1);
+ a.deleteSequence(2);
+ assertEquals(a.getAbsoluteHeight(), height - 1);
+ assertEquals(a.getHiddenSequences().getSize(), 1);
+
+ // delete a sequence which is not hidden, check hiddenSequences are not
+ // affected
+ a.deleteSequence(10);
+ assertEquals(a.getAbsoluteHeight(), height - 2);
+ assertEquals(a.getHiddenSequences().getSize(), 1);
+ }
+
+ @Test(
+ groups = "Functional",
+ expectedExceptions = { IllegalArgumentException.class })
+ public void testSetDataset_selfReference()
+ {
+ SequenceI seq = new Sequence("a", "a");
+ AlignmentI alignment = new Alignment(new SequenceI[] { seq });
+ alignment.setDataset(alignment);
+ }
+
+ @Test(groups = "Functional")
+ public void testAppend()
+ {
+ SequenceI seq = new Sequence("seq1", "FRMLPSRT-A--L-");
+ AlignmentI alignment = new Alignment(new SequenceI[] { seq });
+ alignment.setGapCharacter('-');
+ SequenceI seq2 = new Sequence("seq1", "KP..L.FQII.");
+ AlignmentI alignment2 = new Alignment(new SequenceI[] { seq2 });
+ alignment2.setGapCharacter('.');
+
+ alignment.append(alignment2);
+
+ assertEquals('-', alignment.getGapCharacter());
+ assertSame(seq, alignment.getSequenceAt(0));
+ assertEquals("KP--L-FQII-", alignment.getSequenceAt(1)
+ .getSequenceAsString());
+
+ // todo test coverage for annotations, mappings, groups,
+ // hidden sequences, properties
+ }
+
+ /**
+ * test that calcId == null on findOrCreate doesn't raise an NPE, and yields
+ * an annotation with a null calcId
+ *
+ */
+ @Test(groups = "Functional")
+ public void testFindOrCreateForNullCalcId()
+ {
+ SequenceI seq = new Sequence("seq1", "FRMLPSRT-A--L-");
+ AlignmentI alignment = new Alignment(new SequenceI[] { seq });
+
+ AlignmentAnnotation ala = alignment.findOrCreateAnnotation(
+ "Temperature Factor", null, false, seq, null);
+ assertNotNull(ala);
+ assertEquals(seq, ala.sequenceRef);
+ assertEquals("", ala.calcId);
+ }
+
+ @Test(groups = "Functional")
+ public void testPropagateInsertions()
+ {
+ // create an alignment with no gaps - this will be the profile seq and other
+ // JPRED seqs
+ AlignmentGenerator gen = new AlignmentGenerator(false);
+ AlignmentI al = gen.generate(25, 10, 1234, 0, 0);
+
+ // get the profileseq
+ SequenceI profileseq = al.getSequenceAt(0);
+ SequenceI gappedseq = new Sequence(profileseq);
+ gappedseq.insertCharAt(5, al.getGapCharacter());
+ gappedseq.insertCharAt(6, al.getGapCharacter());
+ gappedseq.insertCharAt(7, al.getGapCharacter());
+ gappedseq.insertCharAt(8, al.getGapCharacter());
+
+ // force different kinds of padding
+ al.getSequenceAt(3).deleteChars(2, 23);
+ al.getSequenceAt(4).deleteChars(2, 27);
+ al.getSequenceAt(5).deleteChars(10, 27);
+
+ // create an alignment view with the gapped sequence
+ SequenceI[] seqs = new SequenceI[1];
+ seqs[0] = gappedseq;
+ AlignmentI newal = new Alignment(seqs);
+ HiddenColumns hidden = new HiddenColumns();
+ hidden.hideColumns(15, 17);
+
+ AlignmentView view = new AlignmentView(newal, hidden, null, true, false,
+ false);
+
+ // confirm that original contigs are as expected
+ Iterator<int[]> visible = hidden.getVisContigsIterator(0, 25, false);
+ int[] region = visible.next();
+ assertEquals("[0, 14]", Arrays.toString(region));
+ region = visible.next();
+ assertEquals("[18, 24]", Arrays.toString(region));
+
+ // propagate insertions
+ HiddenColumns result = al.propagateInsertions(profileseq, view);
+
+ // confirm that the contigs have changed to account for the gaps
+ visible = result.getVisContigsIterator(0, 25, false);
+ region = visible.next();
+ assertEquals("[0, 10]", Arrays.toString(region));
+ region = visible.next();
+ assertEquals("[14, 24]", Arrays.toString(region));
+
+ // confirm the alignment has been changed so that the other sequences have
+ // gaps inserted where the columns are hidden
+ assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[10]));
+ assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[11]));
+ assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[12]));
+ assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[13]));
+ assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[14]));
+
+ }
+
+ @Test(groups = "Functional")
+ public void testPropagateInsertionsOverlap()
+ {
+ // test propagateInsertions where gaps and hiddenColumns overlap
+
+ // create an alignment with no gaps - this will be the profile seq and other
+ // JPRED seqs
+ AlignmentGenerator gen = new AlignmentGenerator(false);
+ AlignmentI al = gen.generate(20, 10, 1234, 0, 0);
+
+ // get the profileseq
+ SequenceI profileseq = al.getSequenceAt(0);
+ SequenceI gappedseq = new Sequence(profileseq);
+ gappedseq.insertCharAt(5, al.getGapCharacter());
+ gappedseq.insertCharAt(6, al.getGapCharacter());
+ gappedseq.insertCharAt(7, al.getGapCharacter());
+ gappedseq.insertCharAt(8, al.getGapCharacter());
+
+ // create an alignment view with the gapped sequence
+ SequenceI[] seqs = new SequenceI[1];
+ seqs[0] = gappedseq;
+ AlignmentI newal = new Alignment(seqs);
+
+ // hide columns so that some overlap with the gaps
+ HiddenColumns hidden = new HiddenColumns();
+ hidden.hideColumns(7, 10);
+
+ AlignmentView view = new AlignmentView(newal, hidden, null, true, false,
+ false);
+
+ // confirm that original contigs are as expected
+ Iterator<int[]> visible = hidden.getVisContigsIterator(0, 20, false);
+ int[] region = visible.next();
+ assertEquals("[0, 6]", Arrays.toString(region));
+ region = visible.next();
+ assertEquals("[11, 19]", Arrays.toString(region));
+ assertFalse(visible.hasNext());
+
+ // propagate insertions
+ HiddenColumns result = al.propagateInsertions(profileseq, view);
+
+ // confirm that the contigs have changed to account for the gaps
+ visible = result.getVisContigsIterator(0, 20, false);
+ region = visible.next();
+ assertEquals("[0, 4]", Arrays.toString(region));
+ region = visible.next();
+ assertEquals("[7, 19]", Arrays.toString(region));
+ assertFalse(visible.hasNext());
+
+ // confirm the alignment has been changed so that the other sequences have
+ // gaps inserted where the columns are hidden
+ assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[4]));
+ assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[5]));
+ assertTrue(Comparison.isGap(al.getSequenceAt(1).getSequence()[6]));
+ assertFalse(Comparison.isGap(al.getSequenceAt(1).getSequence()[7]));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testPadGaps()
+ {
+ SequenceI seq1 = new Sequence("seq1", "ABCDEF--");
+ SequenceI seq2 = new Sequence("seq2", "-JKLMNO--");
+ SequenceI seq3 = new Sequence("seq2", "-PQR");
+ AlignmentI a = new Alignment(new SequenceI[] { seq1, seq2, seq3 });
+ a.setGapCharacter('.'); // this replaces existing gaps
+ assertEquals("ABCDEF..", seq1.getSequenceAsString());
+ a.padGaps();
+ // trailing gaps are pruned, short sequences padded with gap character
+ assertEquals("ABCDEF.", seq1.getSequenceAsString());
+ assertEquals(".JKLMNO", seq2.getSequenceAsString());
+ assertEquals(".PQR...", seq3.getSequenceAsString());
+ }