X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=test%2Fjalview%2Fcommands%2FEditCommandTest.java;h=00a759e33572a311e13d55e5b50d8c1bdd3b13c3;hb=c19d2a91ca05e052e3408bf5852d88eb5d0608f1;hp=fc821b91922c90f041d3b9af5e066156b69dd527;hpb=2273eba5668e5340354da60fed329c6c716cc439;p=jalview.git diff --git a/test/jalview/commands/EditCommandTest.java b/test/jalview/commands/EditCommandTest.java index fc821b9..00a759e 100644 --- a/test/jalview/commands/EditCommandTest.java +++ b/test/jalview/commands/EditCommandTest.java @@ -1,6 +1,28 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9.0b2) + * Copyright (C) 2015 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.commands; -import static org.junit.Assert.assertEquals; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertSame; + import jalview.commands.EditCommand.Action; import jalview.commands.EditCommand.Edit; import jalview.datamodel.Alignment; @@ -8,9 +30,10 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import java.util.Map; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; /** * Unit tests for EditCommand @@ -27,15 +50,19 @@ public class EditCommandTest private Alignment al; - @Before + @BeforeMethod(alwaysRun = true) public void setUp() { 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('?'); } @@ -43,7 +70,7 @@ public class EditCommandTest /** * Test inserting gap characters */ - @Test + @Test(groups = { "Functional" }) public void testAppendEdit_insertGap() { // set a non-standard gap character to prove it is actually used @@ -60,7 +87,7 @@ public class EditCommandTest * Test deleting characters from sequences. Note the deleteGap() action does * not check that only gap characters are being removed. */ - @Test + @Test(groups = { "Functional" }) public void testAppendEdit_deleteGap() { testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true); @@ -74,12 +101,11 @@ public class EditCommandTest * Test a cut action. The command should store the cut characters to support * undo. */ - @Test + @Test(groups = { "Functional" }) public void testCut() { Edit ec = testee.new Edit(Action.CUT, seqs, 4, 3, al); - testee.cut(ec, new AlignmentI[] - { al }); + EditCommand.cut(ec, new AlignmentI[] { al }); assertEquals("abcdhjk", seqs[0].getSequenceAsString()); assertEquals("fghjnopq", seqs[1].getSequenceAsString()); assertEquals("qrstxyz", seqs[2].getSequenceAsString()); @@ -95,8 +121,7 @@ public class EditCommandTest /** * Test a Paste action, where this adds sequences to an alignment. */ - @Test - @Ignore + @Test(groups = { "Functional" }, enabled = false) // TODO fix so it works public void testPaste_addToAlignment() { @@ -105,8 +130,7 @@ public class EditCommandTest newSeqs[1] = new Sequence("newseq1", "JWMPDH"); Edit ec = testee.new Edit(Action.PASTE, newSeqs, 0, al.getWidth(), al); - testee.paste(ec, new AlignmentI[] - { al }); + EditCommand.paste(ec, new AlignmentI[] { al }); assertEquals(6, al.getSequences().size()); assertEquals("1234567890", seqs[3].getSequenceAsString()); assertEquals("ACEFKL", seqs[4].getSequenceAsString()); @@ -116,15 +140,14 @@ public class EditCommandTest /** * Test insertGap followed by undo command */ - @Test + @Test(groups = { "Functional" }) public void testUndo_insertGap() { // Edit ec = testee.new Edit(Action.INSERT_GAP, seqs, 4, 3, '?'); testee.appendEdit(Action.INSERT_GAP, seqs, 4, 3, al, true); // check something changed assertEquals("abcd???efghjk", seqs[0].getSequenceAsString()); - testee.undoCommand(new AlignmentI[] - { al }); + testee.undoCommand(new AlignmentI[] { al }); assertEquals("abcdefghjk", seqs[0].getSequenceAsString()); assertEquals("fghjklmnopq", seqs[1].getSequenceAsString()); assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString()); @@ -134,14 +157,13 @@ public class EditCommandTest /** * Test deleteGap followed by undo command */ - @Test + @Test(groups = { "Functional" }) public void testUndo_deleteGap() { testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true); // check something changed assertEquals("abcdhjk", seqs[0].getSequenceAsString()); - testee.undoCommand(new AlignmentI[] - { al }); + testee.undoCommand(new AlignmentI[] { al }); // deleteGap doesn't 'remember' deleted characters, only gaps get put back assertEquals("abcd???hjk", seqs[0].getSequenceAsString()); assertEquals("fghj???nopq", seqs[1].getSequenceAsString()); @@ -152,7 +174,7 @@ public class EditCommandTest /** * Test several commands followed by an undo command */ - @Test + @Test(groups = { "Functional" }) public void testUndo_multipleCommands() { // delete positions 3/4/5 (counting from 1) @@ -171,8 +193,7 @@ public class EditCommandTest assertEquals("12?890", seqs[3].getSequenceAsString()); // undo edit commands - testee.undoCommand(new AlignmentI[] - { al }); + testee.undoCommand(new AlignmentI[] { al }); assertEquals("ab?????hjk", seqs[0].getSequenceAsString()); assertEquals("12?????890", seqs[3].getSequenceAsString()); } @@ -181,7 +202,7 @@ public class EditCommandTest * Unit test for JAL-1594 bug: click and drag sequence right to insert gaps - * undo did not remove them all. */ - @Test + @Test(groups = { "Functional" }) public void testUndo_multipleInsertGaps() { testee.appendEdit(Action.INSERT_GAP, seqs, 4, 1, al, true); @@ -189,8 +210,7 @@ public class EditCommandTest testee.appendEdit(Action.INSERT_GAP, seqs, 6, 1, al, true); // undo edit commands - testee.undoCommand(new AlignmentI[] - { al }); + testee.undoCommand(new AlignmentI[] { al }); assertEquals("abcdefghjk", seqs[0].getSequenceAsString()); assertEquals("1234567890", seqs[3].getSequenceAsString()); @@ -199,14 +219,13 @@ public class EditCommandTest /** * Test cut followed by undo command */ - @Test + @Test(groups = { "Functional" }) public void testUndo_cut() { testee.appendEdit(Action.CUT, seqs, 4, 3, al, true); // check something changed assertEquals("abcdhjk", seqs[0].getSequenceAsString()); - testee.undoCommand(new AlignmentI[] - { al }); + testee.undoCommand(new AlignmentI[] { al }); assertEquals("abcdefghjk", seqs[0].getSequenceAsString()); assertEquals("fghjklmnopq", seqs[1].getSequenceAsString()); assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString()); @@ -216,17 +235,399 @@ public class EditCommandTest /** * Test the replace command (used to manually edit a sequence) */ - @Test + @Test(groups = { "Functional" }) public void testReplace() { // seem to need a dataset sequence on the edited sequence here seqs[1].setDatasetSequence(seqs[1]); - new EditCommand("", Action.REPLACE, "ZXY", new SequenceI[] - { seqs[1] }, 4, 8, al); + new EditCommand("", Action.REPLACE, "ZXY", new SequenceI[] { seqs[1] }, + 4, 8, al); assertEquals("abcdefghjk", seqs[0].getSequenceAsString()); 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(groups = { "Functional" }) + 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(groups = { "Functional" }) + 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(groups = { "Functional" }) + 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(groups = { "Functional" }) + 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. + * + */ + @Test(groups = { "Functional" }) + public void testPriorState_multipleInserts() + { + EditCommand command = new EditCommand(); + SequenceI seq = new Sequence("", "--A--B-CDEF"); + SequenceI ds = new Sequence("", "ABCDEF"); + seq.setDatasetSequence(ds); + SequenceI[] sqs = new SequenceI[] { seq }; + Edit e = command.new Edit(Action.INSERT_GAP, sqs, 1, 2, '-'); + command.addEdit(e); + e = command.new Edit(Action.INSERT_GAP, sqs, 4, 1, '-'); + command.addEdit(e); + e = command.new Edit(Action.INSERT_GAP, sqs, 0, 2, '-'); + command.addEdit(e); + + Map unwound = command.priorState(false); + assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString()); + } + + /** + * Test for 'undoing' a series of gap deletions. + * + */ + @Test(groups = { "Functional" }) + public void testPriorState_removeAllGaps() + { + EditCommand command = new EditCommand(); + SequenceI seq = new Sequence("", "ABC"); + SequenceI ds = new Sequence("", "ABC"); + seq.setDatasetSequence(ds); + SequenceI[] sqs = new SequenceI[] { seq }; + Edit e = command.new Edit(Action.DELETE_GAP, sqs, 1, 1, '-'); + command.addEdit(e); + e = command.new Edit(Action.DELETE_GAP, sqs, 2, 1, '-'); + command.addEdit(e); + + Map unwound = command.priorState(false); + assertEquals("A-B-C", unwound.get(ds).getSequenceAsString()); + } + + /** + * Test for 'undoing' a single delete edit. + */ + @Test(groups = { "Functional" }) + public void testPriorState_singleDelete() + { + EditCommand command = new EditCommand(); + SequenceI seq = new Sequence("", "ABCDEF"); + SequenceI ds = new Sequence("", "ABCDEF"); + seq.setDatasetSequence(ds); + SequenceI[] sqs = new SequenceI[] { seq }; + Edit e = command.new Edit(Action.DELETE_GAP, sqs, 2, 2, '-'); + command.addEdit(e); + + Map unwound = command.priorState(false); + assertEquals("AB--CDEF", unwound.get(ds).getSequenceAsString()); + } + + /** + * Test 'undoing' a single gap insertion edit command. + */ + @Test(groups = { "Functional" }) + public void testPriorState_singleInsert() + { + EditCommand command = new EditCommand(); + SequenceI seq = new Sequence("", "AB---CDEF"); + SequenceI ds = new Sequence("", "ABCDEF"); + seq.setDatasetSequence(ds); + SequenceI[] sqs = new SequenceI[] { seq }; + Edit e = command.new Edit(Action.INSERT_GAP, sqs, 2, 3, '-'); + command.addEdit(e); + + Map unwound = command.priorState(false); + SequenceI prior = unwound.get(ds); + assertEquals("ABCDEF", prior.getSequenceAsString()); + assertEquals(1, prior.getStart()); + assertEquals(6, prior.getEnd()); + } + + /** + * Test 'undoing' a single gap insertion edit command, on a sequence whose + * start residue is other than 1 + */ + @Test(groups = { "Functional" }) + public void testPriorState_singleInsertWithOffset() + { + EditCommand command = new EditCommand(); + SequenceI seq = new Sequence("", "AB---CDEF", 8, 13); + // SequenceI ds = new Sequence("", "ABCDEF", 8, 13); + // seq.setDatasetSequence(ds); + seq.createDatasetSequence(); + SequenceI[] sqs = new SequenceI[] { seq }; + Edit e = command.new Edit(Action.INSERT_GAP, sqs, 2, 3, '-'); + command.addEdit(e); + + Map unwound = command.priorState(false); + SequenceI prior = unwound.get(seq.getDatasetSequence()); + assertEquals("ABCDEF", prior.getSequenceAsString()); + assertEquals(8, prior.getStart()); + assertEquals(13, prior.getEnd()); + } + + /** + * Test that mimics 'remove all gaps' action. This generates delete gap edits + * for contiguous gaps in each sequence separately. + */ + @Test(groups = { "Functional" }) + 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[] sqs = new SequenceI[] { seq }; + Edit e = command.new Edit(Action.DELETE_GAP, sqs, 0, 2, '-'); + command.addEdit(e); + seq = new Sequence("", "ABCDEF"); + seq.setDatasetSequence(ds1); + sqs = new SequenceI[] { seq }; + e = command.new Edit(Action.DELETE_GAP, sqs, 3, 1, '-'); + command.addEdit(e); + + /* + * Two edits for the second sequence + */ + seq = new Sequence("", "FGHI--J"); + SequenceI ds2 = new Sequence("", "FGHIJ"); + seq.setDatasetSequence(ds2); + sqs = new SequenceI[] { seq }; + e = command.new Edit(Action.DELETE_GAP, sqs, 2, 1, '-'); + command.addEdit(e); + seq = new Sequence("", "FGHIJ"); + seq.setDatasetSequence(ds2); + sqs = new SequenceI[] { seq }; + e = command.new Edit(Action.DELETE_GAP, sqs, 4, 2, '-'); + command.addEdit(e); + + /* + * One edit for the third sequence. + */ + seq = new Sequence("", "MNOPQ"); + SequenceI ds3 = new Sequence("", "MNOPQ"); + seq.setDatasetSequence(ds3); + sqs = new SequenceI[] { seq }; + e = command.new Edit(Action.DELETE_GAP, sqs, 1, 1, '-'); + command.addEdit(e); + + Map 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(groups = { "Functional" }) + 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[] sqs = new SequenceI[] { seq1, seq2, seq3 }; + Edit e = command.new Edit(Action.DELETE_GAP, sqs, 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); + sqs = new SequenceI[] { seq1, seq2, seq3 }; + e = command.new Edit(Action.DELETE_GAP, sqs, 4, 2, '-'); + command.addEdit(e); + Map 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()); } }