JAL-2418 updated JABAWS server in what’s new
[jalview.git] / test / jalview / commands / EditCommandTest.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.commands;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertSame;
25
26 import jalview.commands.EditCommand.Action;
27 import jalview.commands.EditCommand.Edit;
28 import jalview.datamodel.Alignment;
29 import jalview.datamodel.AlignmentI;
30 import jalview.datamodel.Sequence;
31 import jalview.datamodel.SequenceI;
32 import jalview.gui.JvOptionPane;
33
34 import java.util.Map;
35
36 import org.testng.annotations.BeforeClass;
37 import org.testng.annotations.BeforeMethod;
38 import org.testng.annotations.Test;
39
40 /**
41  * Unit tests for EditCommand
42  * 
43  * @author gmcarstairs
44  *
45  */
46 public class EditCommandTest
47 {
48
49   @BeforeClass(alwaysRun = true)
50   public void setUpJvOptionPane()
51   {
52     JvOptionPane.setInteractiveMode(false);
53     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
54   }
55
56   private EditCommand testee;
57
58   private SequenceI[] seqs;
59
60   private Alignment al;
61
62   @BeforeMethod(alwaysRun = true)
63   public void setUp()
64   {
65     testee = new EditCommand();
66     seqs = new SequenceI[4];
67     seqs[0] = new Sequence("seq0", "abcdefghjk");
68     seqs[0].setDatasetSequence(new Sequence("seq0ds", "abcdefghjk"));
69     seqs[1] = new Sequence("seq1", "fghjklmnopq");
70     seqs[1].setDatasetSequence(new Sequence("seq1ds", "fghjklmnopq"));
71     seqs[2] = new Sequence("seq2", "qrstuvwxyz");
72     seqs[2].setDatasetSequence(new Sequence("seq2ds", "qrstuvwxyz"));
73     seqs[3] = new Sequence("seq3", "1234567890");
74     seqs[3].setDatasetSequence(new Sequence("seq3ds", "1234567890"));
75     al = new Alignment(seqs);
76     al.setGapCharacter('?');
77   }
78
79   /**
80    * Test inserting gap characters
81    */
82   @Test(groups = { "Functional" })
83   public void testAppendEdit_insertGap()
84   {
85     // set a non-standard gap character to prove it is actually used
86     testee.appendEdit(Action.INSERT_GAP, seqs, 4, 3, al, true);
87     assertEquals("abcd???efghjk", seqs[0].getSequenceAsString());
88     assertEquals("fghj???klmnopq", seqs[1].getSequenceAsString());
89     assertEquals("qrst???uvwxyz", seqs[2].getSequenceAsString());
90     assertEquals("1234???567890", seqs[3].getSequenceAsString());
91
92     // todo: test for handling out of range positions?
93   }
94
95   /**
96    * Test deleting characters from sequences. Note the deleteGap() action does
97    * not check that only gap characters are being removed.
98    */
99   @Test(groups = { "Functional" })
100   public void testAppendEdit_deleteGap()
101   {
102     testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true);
103     assertEquals("abcdhjk", seqs[0].getSequenceAsString());
104     assertEquals("fghjnopq", seqs[1].getSequenceAsString());
105     assertEquals("qrstxyz", seqs[2].getSequenceAsString());
106     assertEquals("1234890", seqs[3].getSequenceAsString());
107   }
108
109   /**
110    * Test a cut action. The command should store the cut characters to support
111    * undo.
112    */
113   @Test(groups = { "Functional" })
114   public void testCut()
115   {
116     Edit ec = testee.new Edit(Action.CUT, seqs, 4, 3, al);
117     EditCommand.cut(ec, new AlignmentI[] { al });
118     assertEquals("abcdhjk", seqs[0].getSequenceAsString());
119     assertEquals("fghjnopq", seqs[1].getSequenceAsString());
120     assertEquals("qrstxyz", seqs[2].getSequenceAsString());
121     assertEquals("1234890", seqs[3].getSequenceAsString());
122
123     assertEquals("efg", new String(ec.string[0]));
124     assertEquals("klm", new String(ec.string[1]));
125     assertEquals("uvw", new String(ec.string[2]));
126     assertEquals("567", new String(ec.string[3]));
127     // TODO: case where whole sequence is deleted as nothing left; etc
128   }
129
130   /**
131    * Test a Paste action, where this adds sequences to an alignment.
132    */
133   @Test(groups = { "Functional" }, enabled = false)
134   // TODO fix so it works
135   public void testPaste_addToAlignment()
136   {
137     SequenceI[] newSeqs = new SequenceI[2];
138     newSeqs[0] = new Sequence("newseq0", "ACEFKL");
139     newSeqs[1] = new Sequence("newseq1", "JWMPDH");
140
141     Edit ec = testee.new Edit(Action.PASTE, newSeqs, 0, al.getWidth(), al);
142     EditCommand.paste(ec, new AlignmentI[] { al });
143     assertEquals(6, al.getSequences().size());
144     assertEquals("1234567890", seqs[3].getSequenceAsString());
145     assertEquals("ACEFKL", seqs[4].getSequenceAsString());
146     assertEquals("JWMPDH", seqs[5].getSequenceAsString());
147   }
148
149   /**
150    * Test insertGap followed by undo command
151    */
152   @Test(groups = { "Functional" })
153   public void testUndo_insertGap()
154   {
155     // Edit ec = testee.new Edit(Action.INSERT_GAP, seqs, 4, 3, '?');
156     testee.appendEdit(Action.INSERT_GAP, seqs, 4, 3, al, true);
157     // check something changed
158     assertEquals("abcd???efghjk", seqs[0].getSequenceAsString());
159     testee.undoCommand(new AlignmentI[] { al });
160     assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
161     assertEquals("fghjklmnopq", seqs[1].getSequenceAsString());
162     assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
163     assertEquals("1234567890", seqs[3].getSequenceAsString());
164   }
165
166   /**
167    * Test deleteGap followed by undo command
168    */
169   @Test(groups = { "Functional" })
170   public void testUndo_deleteGap()
171   {
172     testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true);
173     // check something changed
174     assertEquals("abcdhjk", seqs[0].getSequenceAsString());
175     testee.undoCommand(new AlignmentI[] { al });
176     // deleteGap doesn't 'remember' deleted characters, only gaps get put back
177     assertEquals("abcd???hjk", seqs[0].getSequenceAsString());
178     assertEquals("fghj???nopq", seqs[1].getSequenceAsString());
179     assertEquals("qrst???xyz", seqs[2].getSequenceAsString());
180     assertEquals("1234???890", seqs[3].getSequenceAsString());
181   }
182
183   /**
184    * Test several commands followed by an undo command
185    */
186   @Test(groups = { "Functional" })
187   public void testUndo_multipleCommands()
188   {
189     // delete positions 3/4/5 (counting from 1)
190     testee.appendEdit(Action.DELETE_GAP, seqs, 2, 3, al, true);
191     assertEquals("abfghjk", seqs[0].getSequenceAsString());
192     assertEquals("1267890", seqs[3].getSequenceAsString());
193
194     // insert 2 gaps after the second residue
195     testee.appendEdit(Action.INSERT_GAP, seqs, 2, 2, al, true);
196     assertEquals("ab??fghjk", seqs[0].getSequenceAsString());
197     assertEquals("12??67890", seqs[3].getSequenceAsString());
198
199     // delete positions 4/5/6
200     testee.appendEdit(Action.DELETE_GAP, seqs, 3, 3, al, true);
201     assertEquals("ab?hjk", seqs[0].getSequenceAsString());
202     assertEquals("12?890", seqs[3].getSequenceAsString());
203
204     // undo edit commands
205     testee.undoCommand(new AlignmentI[] { al });
206     assertEquals("ab?????hjk", seqs[0].getSequenceAsString());
207     assertEquals("12?????890", seqs[3].getSequenceAsString());
208   }
209
210   /**
211    * Unit test for JAL-1594 bug: click and drag sequence right to insert gaps -
212    * undo did not remove them all.
213    */
214   @Test(groups = { "Functional" })
215   public void testUndo_multipleInsertGaps()
216   {
217     testee.appendEdit(Action.INSERT_GAP, seqs, 4, 1, al, true);
218     testee.appendEdit(Action.INSERT_GAP, seqs, 5, 1, al, true);
219     testee.appendEdit(Action.INSERT_GAP, seqs, 6, 1, al, true);
220
221     // undo edit commands
222     testee.undoCommand(new AlignmentI[] { al });
223     assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
224     assertEquals("1234567890", seqs[3].getSequenceAsString());
225
226   }
227
228   /**
229    * Test cut followed by undo command
230    */
231   @Test(groups = { "Functional" })
232   public void testUndo_cut()
233   {
234     testee.appendEdit(Action.CUT, seqs, 4, 3, al, true);
235     // check something changed
236     assertEquals("abcdhjk", seqs[0].getSequenceAsString());
237     testee.undoCommand(new AlignmentI[] { al });
238     assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
239     assertEquals("fghjklmnopq", seqs[1].getSequenceAsString());
240     assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
241     assertEquals("1234567890", seqs[3].getSequenceAsString());
242   }
243
244   /**
245    * Test the replace command (used to manually edit a sequence)
246    */
247   @Test(groups = { "Functional" })
248   public void testReplace()
249   {
250     // seem to need a dataset sequence on the edited sequence here
251     seqs[1].createDatasetSequence();
252     new EditCommand("", Action.REPLACE, "ZXY", new SequenceI[] { seqs[1] },
253             4, 8, al);
254     assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
255     assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
256     assertEquals("1234567890", seqs[3].getSequenceAsString());
257     seqs[1] = new Sequence("seq1", "fghjZXYnopq");
258   }
259
260   /**
261    * Test that the addEdit command correctly merges insert gap commands when
262    * possible.
263    */
264   @Test(groups = { "Functional" })
265   public void testAddEdit_multipleInsertGap()
266   {
267     /*
268      * 3 insert gap in a row (aka mouse drag right):
269      */
270     Edit e = new EditCommand().new Edit(Action.INSERT_GAP,
271             new SequenceI[] { seqs[0] }, 1, 1, al);
272     testee.addEdit(e);
273     SequenceI edited = new Sequence("seq0", "a?bcdefghjk");
274     edited.setDatasetSequence(seqs[0].getDatasetSequence());
275     e = new EditCommand().new Edit(Action.INSERT_GAP,
276             new SequenceI[] { edited }, 2, 1, al);
277     testee.addEdit(e);
278     edited = new Sequence("seq0", "a??bcdefghjk");
279     edited.setDatasetSequence(seqs[0].getDatasetSequence());
280     e = new EditCommand().new Edit(Action.INSERT_GAP,
281             new SequenceI[] { edited }, 3, 1, al);
282     testee.addEdit(e);
283     assertEquals(1, testee.getSize());
284     assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
285     assertEquals(1, testee.getEdit(0).getPosition());
286     assertEquals(3, testee.getEdit(0).getNumber());
287
288     /*
289      * Add a non-contiguous edit - should not be merged.
290      */
291     e = new EditCommand().new Edit(Action.INSERT_GAP,
292             new SequenceI[] { edited }, 5, 2, al);
293     testee.addEdit(e);
294     assertEquals(2, testee.getSize());
295     assertEquals(5, testee.getEdit(1).getPosition());
296     assertEquals(2, testee.getEdit(1).getNumber());
297
298     /*
299      * Add a Delete after the Insert - should not be merged.
300      */
301     e = new EditCommand().new Edit(Action.DELETE_GAP,
302             new SequenceI[] { edited }, 6, 2, al);
303     testee.addEdit(e);
304     assertEquals(3, testee.getSize());
305     assertEquals(Action.DELETE_GAP, testee.getEdit(2).getAction());
306     assertEquals(6, testee.getEdit(2).getPosition());
307     assertEquals(2, testee.getEdit(2).getNumber());
308   }
309
310   /**
311    * Test that the addEdit command correctly merges delete gap commands when
312    * possible.
313    */
314   @Test(groups = { "Functional" })
315   public void testAddEdit_multipleDeleteGap()
316   {
317     /*
318      * 3 delete gap in a row (aka mouse drag left):
319      */
320     seqs[0].setSequence("a???bcdefghjk");
321     Edit e = new EditCommand().new Edit(Action.DELETE_GAP,
322             new SequenceI[] { seqs[0] }, 4, 1, al);
323     testee.addEdit(e);
324     assertEquals(1, testee.getSize());
325
326     SequenceI edited = new Sequence("seq0", "a??bcdefghjk");
327     edited.setDatasetSequence(seqs[0].getDatasetSequence());
328     e = new EditCommand().new Edit(Action.DELETE_GAP,
329             new SequenceI[] { edited }, 3, 1, al);
330     testee.addEdit(e);
331     assertEquals(1, testee.getSize());
332
333     edited = new Sequence("seq0", "a?bcdefghjk");
334     edited.setDatasetSequence(seqs[0].getDatasetSequence());
335     e = new EditCommand().new Edit(Action.DELETE_GAP,
336             new SequenceI[] { edited }, 2, 1, al);
337     testee.addEdit(e);
338     assertEquals(1, testee.getSize());
339     assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
340     assertEquals(2, testee.getEdit(0).getPosition());
341     assertEquals(3, testee.getEdit(0).getNumber());
342
343     /*
344      * Add a non-contiguous edit - should not be merged.
345      */
346     e = new EditCommand().new Edit(Action.DELETE_GAP,
347             new SequenceI[] { edited }, 2, 1, al);
348     testee.addEdit(e);
349     assertEquals(2, testee.getSize());
350     assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
351     assertEquals(2, testee.getEdit(1).getPosition());
352     assertEquals(1, testee.getEdit(1).getNumber());
353
354     /*
355      * Add an Insert after the Delete - should not be merged.
356      */
357     e = new EditCommand().new Edit(Action.INSERT_GAP,
358             new SequenceI[] { edited }, 1, 1, al);
359     testee.addEdit(e);
360     assertEquals(3, testee.getSize());
361     assertEquals(Action.INSERT_GAP, testee.getEdit(2).getAction());
362     assertEquals(1, testee.getEdit(2).getPosition());
363     assertEquals(1, testee.getEdit(2).getNumber());
364   }
365
366   /**
367    * Test that the addEdit command correctly handles 'remove gaps' edits for the
368    * case when they appear contiguous but are acting on different sequences.
369    * They should not be merged.
370    */
371   @Test(groups = { "Functional" })
372   public void testAddEdit_removeAllGaps()
373   {
374     seqs[0].setSequence("a???bcdefghjk");
375     Edit e = new EditCommand().new Edit(Action.DELETE_GAP,
376             new SequenceI[] { seqs[0] }, 4, 1, al);
377     testee.addEdit(e);
378
379     seqs[1].setSequence("f??ghjklmnopq");
380     Edit e2 = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
381     { seqs[1] }, 3, 1, al);
382     testee.addEdit(e2);
383     assertEquals(2, testee.getSize());
384     assertSame(e, testee.getEdit(0));
385     assertSame(e2, testee.getEdit(1));
386   }
387
388   /**
389    * Test that the addEdit command correctly merges insert gap commands acting
390    * on a multi-sequence selection.
391    */
392   @Test(groups = { "Functional" })
393   public void testAddEdit_groupInsertGaps()
394   {
395     /*
396      * 2 insert gap in a row (aka mouse drag right), on two sequences:
397      */
398     Edit e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[] {
399         seqs[0], seqs[1] }, 1, 1, al);
400     testee.addEdit(e);
401     SequenceI seq1edited = new Sequence("seq0", "a?bcdefghjk");
402     seq1edited.setDatasetSequence(seqs[0].getDatasetSequence());
403     SequenceI seq2edited = new Sequence("seq1", "f?ghjklmnopq");
404     seq2edited.setDatasetSequence(seqs[1].getDatasetSequence());
405     e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[] {
406         seq1edited, seq2edited }, 2, 1, al);
407     testee.addEdit(e);
408
409     assertEquals(1, testee.getSize());
410     assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
411     assertEquals(1, testee.getEdit(0).getPosition());
412     assertEquals(2, testee.getEdit(0).getNumber());
413     assertEquals(seqs[0].getDatasetSequence(), testee.getEdit(0)
414             .getSequences()[0].getDatasetSequence());
415     assertEquals(seqs[1].getDatasetSequence(), testee.getEdit(0)
416             .getSequences()[1].getDatasetSequence());
417   }
418
419   /**
420    * Test for 'undoing' a series of gap insertions.
421    * <ul>
422    * <li>Start: ABCDEF insert 2 at pos 1</li>
423    * <li>next: A--BCDEF insert 1 at pos 4</li>
424    * <li>next: A--B-CDEF insert 2 at pos 0</li>
425    * <li>last: --A--B-CDEF</li>
426    * </ul>
427    */
428   @Test(groups = { "Functional" })
429   public void testPriorState_multipleInserts()
430   {
431     EditCommand command = new EditCommand();
432     SequenceI seq = new Sequence("", "--A--B-CDEF");
433     SequenceI ds = new Sequence("", "ABCDEF");
434     seq.setDatasetSequence(ds);
435     SequenceI[] sqs = new SequenceI[] { seq };
436     Edit e = command.new Edit(Action.INSERT_GAP, sqs, 1, 2, '-');
437     command.addEdit(e);
438     e = command.new Edit(Action.INSERT_GAP, sqs, 4, 1, '-');
439     command.addEdit(e);
440     e = command.new Edit(Action.INSERT_GAP, sqs, 0, 2, '-');
441     command.addEdit(e);
442
443     Map<SequenceI, SequenceI> unwound = command.priorState(false);
444     assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString());
445   }
446
447   /**
448    * Test for 'undoing' a series of gap deletions.
449    * <ul>
450    * <li>Start: A-B-C delete 1 at pos 1</li>
451    * <li>Next: AB-C delete 1 at pos 2</li>
452    * <li>End: ABC</li>
453    * </ul>
454    */
455   @Test(groups = { "Functional" })
456   public void testPriorState_removeAllGaps()
457   {
458     EditCommand command = new EditCommand();
459     SequenceI seq = new Sequence("", "ABC");
460     SequenceI ds = new Sequence("", "ABC");
461     seq.setDatasetSequence(ds);
462     SequenceI[] sqs = new SequenceI[] { seq };
463     Edit e = command.new Edit(Action.DELETE_GAP, sqs, 1, 1, '-');
464     command.addEdit(e);
465     e = command.new Edit(Action.DELETE_GAP, sqs, 2, 1, '-');
466     command.addEdit(e);
467
468     Map<SequenceI, SequenceI> unwound = command.priorState(false);
469     assertEquals("A-B-C", unwound.get(ds).getSequenceAsString());
470   }
471
472   /**
473    * Test for 'undoing' a single delete edit.
474    */
475   @Test(groups = { "Functional" })
476   public void testPriorState_singleDelete()
477   {
478     EditCommand command = new EditCommand();
479     SequenceI seq = new Sequence("", "ABCDEF");
480     SequenceI ds = new Sequence("", "ABCDEF");
481     seq.setDatasetSequence(ds);
482     SequenceI[] sqs = new SequenceI[] { seq };
483     Edit e = command.new Edit(Action.DELETE_GAP, sqs, 2, 2, '-');
484     command.addEdit(e);
485
486     Map<SequenceI, SequenceI> unwound = command.priorState(false);
487     assertEquals("AB--CDEF", unwound.get(ds).getSequenceAsString());
488   }
489
490   /**
491    * Test 'undoing' a single gap insertion edit command.
492    */
493   @Test(groups = { "Functional" })
494   public void testPriorState_singleInsert()
495   {
496     EditCommand command = new EditCommand();
497     SequenceI seq = new Sequence("", "AB---CDEF");
498     SequenceI ds = new Sequence("", "ABCDEF");
499     seq.setDatasetSequence(ds);
500     SequenceI[] sqs = new SequenceI[] { seq };
501     Edit e = command.new Edit(Action.INSERT_GAP, sqs, 2, 3, '-');
502     command.addEdit(e);
503
504     Map<SequenceI, SequenceI> unwound = command.priorState(false);
505     SequenceI prior = unwound.get(ds);
506     assertEquals("ABCDEF", prior.getSequenceAsString());
507     assertEquals(1, prior.getStart());
508     assertEquals(6, prior.getEnd());
509   }
510
511   /**
512    * Test 'undoing' a single gap insertion edit command, on a sequence whose
513    * start residue is other than 1
514    */
515   @Test(groups = { "Functional" })
516   public void testPriorState_singleInsertWithOffset()
517   {
518     EditCommand command = new EditCommand();
519     SequenceI seq = new Sequence("", "AB---CDEF", 8, 13);
520     // SequenceI ds = new Sequence("", "ABCDEF", 8, 13);
521     // seq.setDatasetSequence(ds);
522     seq.createDatasetSequence();
523     SequenceI[] sqs = new SequenceI[] { seq };
524     Edit e = command.new Edit(Action.INSERT_GAP, sqs, 2, 3, '-');
525     command.addEdit(e);
526
527     Map<SequenceI, SequenceI> unwound = command.priorState(false);
528     SequenceI prior = unwound.get(seq.getDatasetSequence());
529     assertEquals("ABCDEF", prior.getSequenceAsString());
530     assertEquals(8, prior.getStart());
531     assertEquals(13, prior.getEnd());
532   }
533
534   /**
535    * Test that mimics 'remove all gaps' action. This generates delete gap edits
536    * for contiguous gaps in each sequence separately.
537    */
538   @Test(groups = { "Functional" })
539   public void testPriorState_removeGapsMultipleSeqs()
540   {
541     EditCommand command = new EditCommand();
542     String original1 = "--ABC-DEF";
543     String original2 = "FG-HI--J";
544     String original3 = "M-NOPQ";
545
546     /*
547      * Two edits for the first sequence
548      */
549     SequenceI seq = new Sequence("", "ABC-DEF");
550     SequenceI ds1 = new Sequence("", "ABCDEF");
551     seq.setDatasetSequence(ds1);
552     SequenceI[] sqs = new SequenceI[] { seq };
553     Edit e = command.new Edit(Action.DELETE_GAP, sqs, 0, 2, '-');
554     command.addEdit(e);
555     seq = new Sequence("", "ABCDEF");
556     seq.setDatasetSequence(ds1);
557     sqs = new SequenceI[] { seq };
558     e = command.new Edit(Action.DELETE_GAP, sqs, 3, 1, '-');
559     command.addEdit(e);
560
561     /*
562      * Two edits for the second sequence
563      */
564     seq = new Sequence("", "FGHI--J");
565     SequenceI ds2 = new Sequence("", "FGHIJ");
566     seq.setDatasetSequence(ds2);
567     sqs = new SequenceI[] { seq };
568     e = command.new Edit(Action.DELETE_GAP, sqs, 2, 1, '-');
569     command.addEdit(e);
570     seq = new Sequence("", "FGHIJ");
571     seq.setDatasetSequence(ds2);
572     sqs = new SequenceI[] { seq };
573     e = command.new Edit(Action.DELETE_GAP, sqs, 4, 2, '-');
574     command.addEdit(e);
575
576     /*
577      * One edit for the third sequence.
578      */
579     seq = new Sequence("", "MNOPQ");
580     SequenceI ds3 = new Sequence("", "MNOPQ");
581     seq.setDatasetSequence(ds3);
582     sqs = new SequenceI[] { seq };
583     e = command.new Edit(Action.DELETE_GAP, sqs, 1, 1, '-');
584     command.addEdit(e);
585
586     Map<SequenceI, SequenceI> unwound = command.priorState(false);
587     assertEquals(original1, unwound.get(ds1).getSequenceAsString());
588     assertEquals(original2, unwound.get(ds2).getSequenceAsString());
589     assertEquals(original3, unwound.get(ds3).getSequenceAsString());
590   }
591
592   /**
593    * Test that mimics 'remove all gapped columns' action. This generates a
594    * series Delete Gap edits that each act on all sequences that share a gapped
595    * column region.
596    */
597   @Test(groups = { "Functional" })
598   public void testPriorState_removeGappedCols()
599   {
600     EditCommand command = new EditCommand();
601     String original1 = "--ABC--DEF";
602     String original2 = "-G-HI--J";
603     String original3 = "-M-NO--PQ";
604
605     /*
606      * First edit deletes the first column.
607      */
608     SequenceI seq1 = new Sequence("", "-ABC--DEF");
609     SequenceI ds1 = new Sequence("", "ABCDEF");
610     seq1.setDatasetSequence(ds1);
611     SequenceI seq2 = new Sequence("", "G-HI--J");
612     SequenceI ds2 = new Sequence("", "GHIJ");
613     seq2.setDatasetSequence(ds2);
614     SequenceI seq3 = new Sequence("", "M-NO--PQ");
615     SequenceI ds3 = new Sequence("", "MNOPQ");
616     seq3.setDatasetSequence(ds3);
617     SequenceI[] sqs = new SequenceI[] { seq1, seq2, seq3 };
618     Edit e = command.new Edit(Action.DELETE_GAP, sqs, 0, 1, '-');
619     command.addEdit(e);
620
621     /*
622      * Second edit deletes what is now columns 4 and 5.
623      */
624     seq1 = new Sequence("", "-ABCDEF");
625     seq1.setDatasetSequence(ds1);
626     seq2 = new Sequence("", "G-HIJ");
627     seq2.setDatasetSequence(ds2);
628     seq3 = new Sequence("", "M-NOPQ");
629     seq3.setDatasetSequence(ds3);
630     sqs = new SequenceI[] { seq1, seq2, seq3 };
631     e = command.new Edit(Action.DELETE_GAP, sqs, 4, 2, '-');
632     command.addEdit(e);
633
634     Map<SequenceI, SequenceI> unwound = command.priorState(false);
635     assertEquals(original1, unwound.get(ds1).getSequenceAsString());
636     assertEquals(original2, unwound.get(ds2).getSequenceAsString());
637     assertEquals(original3, unwound.get(ds3).getSequenceAsString());
638     assertEquals(ds1, unwound.get(ds1).getDatasetSequence());
639     assertEquals(ds2, unwound.get(ds2).getDatasetSequence());
640     assertEquals(ds3, unwound.get(ds3).getDatasetSequence());
641   }
642 }