package jalview.commands;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
import jalview.commands.EditCommand.Action;
import jalview.commands.EditCommand.Edit;
import jalview.datamodel.Alignment;
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceI;
+import java.util.Map;
+
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
testee = new EditCommand();
seqs = new SequenceI[4];
seqs[0] = new Sequence("seq0", "abcdefghjk");
+ seqs[0].setDatasetSequence(new Sequence("seq0ds", "abcdefghjk"));
seqs[1] = new Sequence("seq1", "fghjklmnopq");
+ seqs[1].setDatasetSequence(new Sequence("seq1ds", "fghjklmnopq"));
seqs[2] = new Sequence("seq2", "qrstuvwxyz");
+ seqs[2].setDatasetSequence(new Sequence("seq2ds", "qrstuvwxyz"));
seqs[3] = new Sequence("seq3", "1234567890");
+ seqs[3].setDatasetSequence(new Sequence("seq3ds", "1234567890"));
al = new Alignment(seqs);
al.setGapCharacter('?');
}
assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
assertEquals("1234567890", seqs[3].getSequenceAsString());
seqs[1] = new Sequence("seq1", "fghjZXYnopq");
+ }
+
+ /**
+ * Test that the addEdit command correctly merges insert gap commands when
+ * possible.
+ */
+ @Test
+ public void testAddEdit_multipleInsertGap()
+ {
+ /*
+ * 3 insert gap in a row (aka mouse drag right):
+ */
+ Edit e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { seqs[0] }, 1, 1, al);
+ testee.addEdit(e);
+ SequenceI edited = new Sequence("seq0", "a?bcdefghjk");
+ edited.setDatasetSequence(seqs[0].getDatasetSequence());
+ e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { edited }, 2, 1, al);
+ testee.addEdit(e);
+ edited = new Sequence("seq0", "a??bcdefghjk");
+ edited.setDatasetSequence(seqs[0].getDatasetSequence());
+ e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { edited }, 3, 1, al);
+ testee.addEdit(e);
+ assertEquals(1, testee.getSize());
+ assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
+ assertEquals(1, testee.getEdit(0).getPosition());
+ assertEquals(3, testee.getEdit(0).getNumber());
+
+ /*
+ * Add a non-contiguous edit - should not be merged.
+ */
+ e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { edited }, 5, 2, al);
+ testee.addEdit(e);
+ assertEquals(2, testee.getSize());
+ assertEquals(5, testee.getEdit(1).getPosition());
+ assertEquals(2, testee.getEdit(1).getNumber());
+
+ /*
+ * Add a Delete after the Insert - should not be merged.
+ */
+ e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { edited }, 6, 2, al);
+ testee.addEdit(e);
+ assertEquals(3, testee.getSize());
+ assertEquals(Action.DELETE_GAP, testee.getEdit(2).getAction());
+ assertEquals(6, testee.getEdit(2).getPosition());
+ assertEquals(2, testee.getEdit(2).getNumber());
+ }
+
+ /**
+ * Test that the addEdit command correctly merges delete gap commands when
+ * possible.
+ */
+ @Test
+ public void testAddEdit_multipleDeleteGap()
+ {
+ /*
+ * 3 delete gap in a row (aka mouse drag left):
+ */
+ seqs[0].setSequence("a???bcdefghjk");
+ Edit e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { seqs[0] }, 4, 1, al);
+ testee.addEdit(e);
+ assertEquals(1, testee.getSize());
+
+ SequenceI edited = new Sequence("seq0", "a??bcdefghjk");
+ edited.setDatasetSequence(seqs[0].getDatasetSequence());
+ e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { edited }, 3, 1, al);
+ testee.addEdit(e);
+ assertEquals(1, testee.getSize());
+
+ edited = new Sequence("seq0", "a?bcdefghjk");
+ edited.setDatasetSequence(seqs[0].getDatasetSequence());
+ e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { edited }, 2, 1, al);
+ testee.addEdit(e);
+ assertEquals(1, testee.getSize());
+ assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
+ assertEquals(2, testee.getEdit(0).getPosition());
+ assertEquals(3, testee.getEdit(0).getNumber());
+
+ /*
+ * Add a non-contiguous edit - should not be merged.
+ */
+ e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { edited }, 2, 1, al);
+ testee.addEdit(e);
+ assertEquals(2, testee.getSize());
+ assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
+ assertEquals(2, testee.getEdit(1).getPosition());
+ assertEquals(1, testee.getEdit(1).getNumber());
+
+ /*
+ * Add an Insert after the Delete - should not be merged.
+ */
+ e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { edited }, 1, 1, al);
+ testee.addEdit(e);
+ assertEquals(3, testee.getSize());
+ assertEquals(Action.INSERT_GAP, testee.getEdit(2).getAction());
+ assertEquals(1, testee.getEdit(2).getPosition());
+ assertEquals(1, testee.getEdit(2).getNumber());
+ }
+
+ /**
+ * Test that the addEdit command correctly handles 'remove gaps' edits for the
+ * case when they appear contiguous but are acting on different sequences.
+ * They should not be merged.
+ */
+ @Test
+ public void testAddEdit_removeAllGaps()
+ {
+ seqs[0].setSequence("a???bcdefghjk");
+ Edit e = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { seqs[0] }, 4, 1, al);
+ testee.addEdit(e);
+
+ seqs[1].setSequence("f??ghjklmnopq");
+ Edit e2 = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
+ { seqs[1] }, 3, 1, al);
+ testee.addEdit(e2);
+ assertEquals(2, testee.getSize());
+ assertSame(e, testee.getEdit(0));
+ assertSame(e2, testee.getEdit(1));
+ }
+
+ /**
+ * Test that the addEdit command correctly merges insert gap commands acting
+ * on a multi-sequence selection.
+ */
+ @Test
+ public void testAddEdit_groupInsertGaps()
+ {
+ /*
+ * 2 insert gap in a row (aka mouse drag right), on two sequences:
+ */
+ Edit e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { seqs[0], seqs[1] }, 1, 1, al);
+ testee.addEdit(e);
+ SequenceI seq1edited = new Sequence("seq0", "a?bcdefghjk");
+ seq1edited.setDatasetSequence(seqs[0].getDatasetSequence());
+ SequenceI seq2edited = new Sequence("seq1", "f?ghjklmnopq");
+ seq2edited.setDatasetSequence(seqs[1].getDatasetSequence());
+ e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[]
+ { seq1edited, seq2edited }, 2, 1, al);
+ testee.addEdit(e);
+
+ assertEquals(1, testee.getSize());
+ assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
+ assertEquals(1, testee.getEdit(0).getPosition());
+ assertEquals(2, testee.getEdit(0).getNumber());
+ assertEquals(seqs[0].getDatasetSequence(), testee.getEdit(0)
+ .getSequences()[0].getDatasetSequence());
+ assertEquals(seqs[1].getDatasetSequence(), testee.getEdit(0)
+ .getSequences()[1].getDatasetSequence());
+ }
+
+ /**
+ * Test for 'undoing' a series of gap insertions.
+ * <ul>
+ * <li>Start: ABCDEF insert 2 at pos 1</li>
+ * <li>next: A--BCDEF insert 1 at pos 4</li>
+ * <li>next: A--B-CDEF insert 2 at pos 0</li>
+ * <li>last: --A--B-CDEF</li>
+ * </ul>
+ */
+ @Test
+ public void testPriorState_multipleInserts()
+ {
+ EditCommand command = new EditCommand();
+ SequenceI seq = new Sequence("", "--A--B-CDEF");
+ SequenceI ds = new Sequence("", "ABCDEF");
+ seq.setDatasetSequence(ds);
+ SequenceI[] seqs = new SequenceI[]
+ { seq };
+ Edit e = command.new Edit(Action.INSERT_GAP, seqs, 1, 2, '-');
+ command.addEdit(e);
+ e = command.new Edit(Action.INSERT_GAP, seqs, 4, 1, '-');
+ command.addEdit(e);
+ e = command.new Edit(Action.INSERT_GAP, seqs, 0, 2, '-');
+ command.addEdit(e);
+
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString());
+ }
+
+ /**
+ * Test for 'undoing' a series of gap deletions.
+ * <ul>
+ * <li>Start: A-B-C delete 1 at pos 1</li>
+ * <li>Next: AB-C delete 1 at pos 2</li>
+ * <li>End: ABC</li>
+ * </ul>
+ */
+ @Test
+ public void testPriorState_removeAllGaps()
+ {
+ EditCommand command = new EditCommand();
+ SequenceI seq = new Sequence("", "ABC");
+ SequenceI ds = new Sequence("", "ABC");
+ seq.setDatasetSequence(ds);
+ SequenceI[] seqs = new SequenceI[]
+ { seq };
+ Edit e = command.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
+ command.addEdit(e);
+ e = command.new Edit(Action.DELETE_GAP, seqs, 2, 1, '-');
+ command.addEdit(e);
+
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals("A-B-C", unwound.get(ds).getSequenceAsString());
+ }
+
+ /**
+ * Test for 'undoing' a single delete edit.
+ */
+ @Test
+ public void testPriorState_singleDelete()
+ {
+ EditCommand command = new EditCommand();
+ SequenceI seq = new Sequence("", "ABCDEF");
+ SequenceI ds = new Sequence("", "ABCDEF");
+ seq.setDatasetSequence(ds);
+ SequenceI[] seqs = new SequenceI[]
+ { seq };
+ Edit e = command.new Edit(Action.DELETE_GAP, seqs, 2, 2, '-');
+ command.addEdit(e);
+
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals("AB--CDEF", unwound.get(ds).getSequenceAsString());
+ }
+
+ /**
+ * Test 'undoing' a single gap insertion edit command.
+ */
+ @Test
+ public void testPriorState_singleInsert()
+ {
+ EditCommand command = new EditCommand();
+ SequenceI seq = new Sequence("", "AB---CDEF");
+ SequenceI ds = new Sequence("", "ABCDEF");
+ seq.setDatasetSequence(ds);
+ SequenceI[] seqs = new SequenceI[]
+ { seq };
+ Edit e = command.new Edit(Action.INSERT_GAP, seqs, 2, 3, '-');
+ command.addEdit(e);
+
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString());
+ }
+
+ /**
+ * Test that mimics 'remove all gaps' action. This generates delete gap edits
+ * for contiguous gaps in each sequence separately.
+ */
+ @Test
+ public void testPriorState_removeGapsMultipleSeqs()
+ {
+ EditCommand command = new EditCommand();
+ String original1 = "--ABC-DEF";
+ String original2 = "FG-HI--J";
+ String original3 = "M-NOPQ";
+
+ /*
+ * Two edits for the first sequence
+ */
+ SequenceI seq = new Sequence("", "ABC-DEF");
+ SequenceI ds1 = new Sequence("", "ABCDEF");
+ seq.setDatasetSequence(ds1);
+ SequenceI[] seqs = new SequenceI[]
+ { seq };
+ Edit e = command.new Edit(Action.DELETE_GAP, seqs, 0, 2, '-');
+ command.addEdit(e);
+ seq = new Sequence("", "ABCDEF");
+ seq.setDatasetSequence(ds1);
+ seqs = new SequenceI[]
+ { seq };
+ e = command.new Edit(Action.DELETE_GAP, seqs, 3, 1, '-');
+ command.addEdit(e);
+
+ /*
+ * Two edits for the second sequence
+ */
+ seq = new Sequence("", "FGHI--J");
+ SequenceI ds2 = new Sequence("", "FGHIJ");
+ seq.setDatasetSequence(ds2);
+ seqs = new SequenceI[]
+ { seq };
+ e = command.new Edit(Action.DELETE_GAP, seqs, 2, 1, '-');
+ command.addEdit(e);
+ seq = new Sequence("", "FGHIJ");
+ seq.setDatasetSequence(ds2);
+ seqs = new SequenceI[]
+ { seq };
+ e = command.new Edit(Action.DELETE_GAP, seqs, 4, 2, '-');
+ command.addEdit(e);
+
+ /*
+ * One edit for the third sequence.
+ */
+ seq = new Sequence("", "MNOPQ");
+ SequenceI ds3 = new Sequence("", "MNOPQ");
+ seq.setDatasetSequence(ds3);
+ seqs = new SequenceI[]
+ { seq };
+ e = command.new Edit(Action.DELETE_GAP, seqs, 1, 1, '-');
+ command.addEdit(e);
+
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals(original1, unwound.get(ds1).getSequenceAsString());
+ assertEquals(original2, unwound.get(ds2).getSequenceAsString());
+ assertEquals(original3, unwound.get(ds3).getSequenceAsString());
+ }
+
+ /**
+ * Test that mimics 'remove all gapped columns' action. This generates a
+ * series Delete Gap edits that each act on all sequences that share a gapped
+ * column region.
+ */
+ @Test
+ public void testPriorState_removeGappedCols()
+ {
+ EditCommand command = new EditCommand();
+ String original1 = "--ABC--DEF";
+ String original2 = "-G-HI--J";
+ String original3 = "-M-NO--PQ";
+
+ /*
+ * First edit deletes the first column.
+ */
+ SequenceI seq1 = new Sequence("", "-ABC--DEF");
+ SequenceI ds1 = new Sequence("", "ABCDEF");
+ seq1.setDatasetSequence(ds1);
+ SequenceI seq2 = new Sequence("", "G-HI--J");
+ SequenceI ds2 = new Sequence("", "GHIJ");
+ seq2.setDatasetSequence(ds2);
+ SequenceI seq3 = new Sequence("", "M-NO--PQ");
+ SequenceI ds3 = new Sequence("", "MNOPQ");
+ seq3.setDatasetSequence(ds3);
+ SequenceI[] seqs = new SequenceI[]
+ { seq1, seq2, seq3 };
+ Edit e = command.new Edit(Action.DELETE_GAP, seqs, 0, 1, '-');
+ command.addEdit(e);
+
+ /*
+ * Second edit deletes what is now columns 4 and 5.
+ */
+ seq1 = new Sequence("", "-ABCDEF");
+ seq1.setDatasetSequence(ds1);
+ seq2 = new Sequence("", "G-HIJ");
+ seq2.setDatasetSequence(ds2);
+ seq3 = new Sequence("", "M-NOPQ");
+ seq3.setDatasetSequence(ds3);
+ seqs = new SequenceI[]
+ { seq1, seq2, seq3 };
+ e = command.new Edit(Action.DELETE_GAP, seqs, 4, 2, '-');
+ command.addEdit(e);
+ Map<SequenceI, SequenceI> unwound = command.priorState(false);
+ assertEquals(original1, unwound.get(ds1).getSequenceAsString());
+ assertEquals(original2, unwound.get(ds2).getSequenceAsString());
+ assertEquals(original3, unwound.get(ds3).getSequenceAsString());
+ assertEquals(ds1, unwound.get(ds1).getDatasetSequence());
+ assertEquals(ds2, unwound.get(ds2).getDatasetSequence());
+ assertEquals(ds3, unwound.get(ds3).getDatasetSequence());
}
}