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