Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.commands

File EditCommandTest.java

 
testCut_withFeatures: expected:<4> but was:<5>
testCut_gappedWithFeatures: expected:<1> but was:<3>
 

Code metrics

22
372
28
1
873
566
40
0.11
13.29
28
1.43

Classes

Class Line # Actions
EditCommandTest 51 372 40 54
0.872037987.2%
 

Contributing tests

This file is covered by 23 tests. .

Source view

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.assertNull;
25    import static org.testng.AssertJUnit.assertSame;
26   
27    import jalview.commands.EditCommand.Action;
28    import jalview.commands.EditCommand.Edit;
29    import jalview.datamodel.Alignment;
30    import jalview.datamodel.AlignmentI;
31    import jalview.datamodel.Sequence;
32    import jalview.datamodel.SequenceFeature;
33    import jalview.datamodel.SequenceI;
34    import jalview.datamodel.features.SequenceFeatures;
35    import jalview.gui.JvOptionPane;
36   
37    import java.util.List;
38    import java.util.Map;
39   
40    import org.testng.Assert;
41    import org.testng.annotations.BeforeClass;
42    import org.testng.annotations.BeforeMethod;
43    import org.testng.annotations.Test;
44   
45    /**
46    * Unit tests for EditCommand
47    *
48    * @author gmcarstairs
49    *
50    */
 
51    public class EditCommandTest
52    {
53    /*
54    * compute n(n+1)/2 e.g.
55    * func(5) = 5 + 4 + 3 + 2 + 1 = 15
56    */
 
57  7 toggle private static int func(int i)
58    {
59  7 return i * (i + 1) / 2;
60    }
61   
 
62  1 toggle @BeforeClass(alwaysRun = true)
63    public void setUpJvOptionPane()
64    {
65  1 JvOptionPane.setInteractiveMode(false);
66  1 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
67    }
68   
69    private EditCommand testee;
70   
71    private SequenceI[] seqs;
72   
73    private Alignment al;
74   
 
75  23 toggle @BeforeMethod(alwaysRun = true)
76    public void setUp()
77    {
78  23 testee = new EditCommand();
79  23 seqs = new SequenceI[4];
80  23 seqs[0] = new Sequence("seq0", "abcdefghjk");
81  23 seqs[0].setDatasetSequence(new Sequence("seq0ds", "abcdefghjk"));
82  23 seqs[1] = new Sequence("seq1", "fghjklmnopq");
83  23 seqs[1].setDatasetSequence(new Sequence("seq1ds", "fghjklmnopq"));
84  23 seqs[2] = new Sequence("seq2", "qrstuvwxyz");
85  23 seqs[2].setDatasetSequence(new Sequence("seq2ds", "qrstuvwxyz"));
86  23 seqs[3] = new Sequence("seq3", "1234567890");
87  23 seqs[3].setDatasetSequence(new Sequence("seq3ds", "1234567890"));
88  23 al = new Alignment(seqs);
89  23 al.setGapCharacter('?');
90    }
91   
92    /**
93    * Test inserting gap characters
94    */
 
95  1 toggle @Test(groups = { "Functional" })
96    public void testAppendEdit_insertGap()
97    {
98    // set a non-standard gap character to prove it is actually used
99  1 testee.appendEdit(Action.INSERT_GAP, seqs, 4, 3, al, true);
100  1 assertEquals("abcd???efghjk", seqs[0].getSequenceAsString());
101  1 assertEquals("fghj???klmnopq", seqs[1].getSequenceAsString());
102  1 assertEquals("qrst???uvwxyz", seqs[2].getSequenceAsString());
103  1 assertEquals("1234???567890", seqs[3].getSequenceAsString());
104   
105    // todo: test for handling out of range positions?
106    }
107   
108    /**
109    * Test deleting characters from sequences. Note the deleteGap() action does
110    * not check that only gap characters are being removed.
111    */
 
112  1 toggle @Test(groups = { "Functional" })
113    public void testAppendEdit_deleteGap()
114    {
115  1 testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true);
116  1 assertEquals("abcdhjk", seqs[0].getSequenceAsString());
117  1 assertEquals("fghjnopq", seqs[1].getSequenceAsString());
118  1 assertEquals("qrstxyz", seqs[2].getSequenceAsString());
119  1 assertEquals("1234890", seqs[3].getSequenceAsString());
120    }
121   
122    /**
123    * Test a cut action. The command should store the cut characters to support
124    * undo.
125    */
 
126  1 toggle @Test(groups = { "Functional" })
127    public void testCut()
128    {
129  1 Edit ec = testee.new Edit(Action.CUT, seqs, 4, 3, al);
130  1 EditCommand.cut(ec, new AlignmentI[] { al });
131  1 assertEquals("abcdhjk", seqs[0].getSequenceAsString());
132  1 assertEquals("fghjnopq", seqs[1].getSequenceAsString());
133  1 assertEquals("qrstxyz", seqs[2].getSequenceAsString());
134  1 assertEquals("1234890", seqs[3].getSequenceAsString());
135   
136  1 assertEquals("efg", new String(ec.string[0]));
137  1 assertEquals("klm", new String(ec.string[1]));
138  1 assertEquals("uvw", new String(ec.string[2]));
139  1 assertEquals("567", new String(ec.string[3]));
140    // TODO: case where whole sequence is deleted as nothing left; etc
141    }
142   
143    /**
144    * Test a Paste action, where this adds sequences to an alignment.
145    */
 
146  0 toggle @Test(groups = { "Functional" }, enabled = false)
147    // TODO fix so it works
148    public void testPaste_addToAlignment()
149    {
150  0 SequenceI[] newSeqs = new SequenceI[2];
151  0 newSeqs[0] = new Sequence("newseq0", "ACEFKL");
152  0 newSeqs[1] = new Sequence("newseq1", "JWMPDH");
153   
154  0 Edit ec = testee.new Edit(Action.PASTE, newSeqs, 0, al.getWidth(), al);
155  0 EditCommand.paste(ec, new AlignmentI[] { al });
156  0 assertEquals(6, al.getSequences().size());
157  0 assertEquals("1234567890", seqs[3].getSequenceAsString());
158  0 assertEquals("ACEFKL", seqs[4].getSequenceAsString());
159  0 assertEquals("JWMPDH", seqs[5].getSequenceAsString());
160    }
161   
162    /**
163    * Test insertGap followed by undo command
164    */
 
165  1 toggle @Test(groups = { "Functional" })
166    public void testUndo_insertGap()
167    {
168    // Edit ec = testee.new Edit(Action.INSERT_GAP, seqs, 4, 3, '?');
169  1 testee.appendEdit(Action.INSERT_GAP, seqs, 4, 3, al, true);
170    // check something changed
171  1 assertEquals("abcd???efghjk", seqs[0].getSequenceAsString());
172  1 testee.undoCommand(new AlignmentI[] { al });
173  1 assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
174  1 assertEquals("fghjklmnopq", seqs[1].getSequenceAsString());
175  1 assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
176  1 assertEquals("1234567890", seqs[3].getSequenceAsString());
177    }
178   
179    /**
180    * Test deleteGap followed by undo command
181    */
 
182  1 toggle @Test(groups = { "Functional" })
183    public void testUndo_deleteGap()
184    {
185  1 testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true);
186    // check something changed
187  1 assertEquals("abcdhjk", seqs[0].getSequenceAsString());
188  1 testee.undoCommand(new AlignmentI[] { al });
189    // deleteGap doesn't 'remember' deleted characters, only gaps get put back
190  1 assertEquals("abcd???hjk", seqs[0].getSequenceAsString());
191  1 assertEquals("fghj???nopq", seqs[1].getSequenceAsString());
192  1 assertEquals("qrst???xyz", seqs[2].getSequenceAsString());
193  1 assertEquals("1234???890", seqs[3].getSequenceAsString());
194    }
195   
196    /**
197    * Test several commands followed by an undo command
198    */
 
199  1 toggle @Test(groups = { "Functional" })
200    public void testUndo_multipleCommands()
201    {
202    // delete positions 3/4/5 (counting from 1)
203  1 testee.appendEdit(Action.DELETE_GAP, seqs, 2, 3, al, true);
204  1 assertEquals("abfghjk", seqs[0].getSequenceAsString());
205  1 assertEquals("1267890", seqs[3].getSequenceAsString());
206   
207    // insert 2 gaps after the second residue
208  1 testee.appendEdit(Action.INSERT_GAP, seqs, 2, 2, al, true);
209  1 assertEquals("ab??fghjk", seqs[0].getSequenceAsString());
210  1 assertEquals("12??67890", seqs[3].getSequenceAsString());
211   
212    // delete positions 4/5/6
213  1 testee.appendEdit(Action.DELETE_GAP, seqs, 3, 3, al, true);
214  1 assertEquals("ab?hjk", seqs[0].getSequenceAsString());
215  1 assertEquals("12?890", seqs[3].getSequenceAsString());
216   
217    // undo edit commands
218  1 testee.undoCommand(new AlignmentI[] { al });
219  1 assertEquals("ab?????hjk", seqs[0].getSequenceAsString());
220  1 assertEquals("12?????890", seqs[3].getSequenceAsString());
221    }
222   
223    /**
224    * Unit test for JAL-1594 bug: click and drag sequence right to insert gaps -
225    * undo did not remove them all.
226    */
 
227  1 toggle @Test(groups = { "Functional" })
228    public void testUndo_multipleInsertGaps()
229    {
230  1 testee.appendEdit(Action.INSERT_GAP, seqs, 4, 1, al, true);
231  1 testee.appendEdit(Action.INSERT_GAP, seqs, 5, 1, al, true);
232  1 testee.appendEdit(Action.INSERT_GAP, seqs, 6, 1, al, true);
233   
234    // undo edit commands
235  1 testee.undoCommand(new AlignmentI[] { al });
236  1 assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
237  1 assertEquals("1234567890", seqs[3].getSequenceAsString());
238   
239    }
240   
241    /**
242    * Test cut followed by undo command
243    */
 
244  1 toggle @Test(groups = { "Functional" })
245    public void testUndo_cut()
246    {
247  1 testee.appendEdit(Action.CUT, seqs, 4, 3, al, true);
248    // check something changed
249  1 assertEquals("abcdhjk", seqs[0].getSequenceAsString());
250  1 testee.undoCommand(new AlignmentI[] { al });
251  1 assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
252  1 assertEquals("fghjklmnopq", seqs[1].getSequenceAsString());
253  1 assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
254  1 assertEquals("1234567890", seqs[3].getSequenceAsString());
255    }
256   
257    /**
258    * Test the replace command (used to manually edit a sequence)
259    */
 
260  1 toggle @Test(groups = { "Functional" })
261    public void testReplace()
262    {
263    // seem to need a dataset sequence on the edited sequence here
264  1 seqs[1].createDatasetSequence();
265  1 new EditCommand("", Action.REPLACE, "ZXY", new SequenceI[] { seqs[1] },
266    4, 8, al);
267  1 assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
268  1 assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
269  1 assertEquals("1234567890", seqs[3].getSequenceAsString());
270  1 seqs[1] = new Sequence("seq1", "fghjZXYnopq");
271    }
272   
273    /**
274    * Test that the addEdit command correctly merges insert gap commands when
275    * possible.
276    */
 
277  1 toggle @Test(groups = { "Functional" })
278    public void testAddEdit_multipleInsertGap()
279    {
280    /*
281    * 3 insert gap in a row (aka mouse drag right):
282    */
283  1 Edit e = new EditCommand().new Edit(Action.INSERT_GAP,
284    new SequenceI[] { seqs[0] }, 1, 1, al);
285  1 testee.addEdit(e);
286  1 SequenceI edited = new Sequence("seq0", "a?bcdefghjk");
287  1 edited.setDatasetSequence(seqs[0].getDatasetSequence());
288  1 e = new EditCommand().new Edit(Action.INSERT_GAP,
289    new SequenceI[] { edited }, 2, 1, al);
290  1 testee.addEdit(e);
291  1 edited = new Sequence("seq0", "a??bcdefghjk");
292  1 edited.setDatasetSequence(seqs[0].getDatasetSequence());
293  1 e = new EditCommand().new Edit(Action.INSERT_GAP,
294    new SequenceI[] { edited }, 3, 1, al);
295  1 testee.addEdit(e);
296  1 assertEquals(1, testee.getSize());
297  1 assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
298  1 assertEquals(1, testee.getEdit(0).getPosition());
299  1 assertEquals(3, testee.getEdit(0).getNumber());
300   
301    /*
302    * Add a non-contiguous edit - should not be merged.
303    */
304  1 e = new EditCommand().new Edit(Action.INSERT_GAP,
305    new SequenceI[] { edited }, 5, 2, al);
306  1 testee.addEdit(e);
307  1 assertEquals(2, testee.getSize());
308  1 assertEquals(5, testee.getEdit(1).getPosition());
309  1 assertEquals(2, testee.getEdit(1).getNumber());
310   
311    /*
312    * Add a Delete after the Insert - should not be merged.
313    */
314  1 e = new EditCommand().new Edit(Action.DELETE_GAP,
315    new SequenceI[] { edited }, 6, 2, al);
316  1 testee.addEdit(e);
317  1 assertEquals(3, testee.getSize());
318  1 assertEquals(Action.DELETE_GAP, testee.getEdit(2).getAction());
319  1 assertEquals(6, testee.getEdit(2).getPosition());
320  1 assertEquals(2, testee.getEdit(2).getNumber());
321    }
322   
323    /**
324    * Test that the addEdit command correctly merges delete gap commands when
325    * possible.
326    */
 
327  1 toggle @Test(groups = { "Functional" })
328    public void testAddEdit_multipleDeleteGap()
329    {
330    /*
331    * 3 delete gap in a row (aka mouse drag left):
332    */
333  1 seqs[0].setSequence("a???bcdefghjk");
334  1 Edit e = new EditCommand().new Edit(Action.DELETE_GAP,
335    new SequenceI[] { seqs[0] }, 4, 1, al);
336  1 testee.addEdit(e);
337  1 assertEquals(1, testee.getSize());
338   
339  1 SequenceI edited = new Sequence("seq0", "a??bcdefghjk");
340  1 edited.setDatasetSequence(seqs[0].getDatasetSequence());
341  1 e = new EditCommand().new Edit(Action.DELETE_GAP,
342    new SequenceI[] { edited }, 3, 1, al);
343  1 testee.addEdit(e);
344  1 assertEquals(1, testee.getSize());
345   
346  1 edited = new Sequence("seq0", "a?bcdefghjk");
347  1 edited.setDatasetSequence(seqs[0].getDatasetSequence());
348  1 e = new EditCommand().new Edit(Action.DELETE_GAP,
349    new SequenceI[] { edited }, 2, 1, al);
350  1 testee.addEdit(e);
351  1 assertEquals(1, testee.getSize());
352  1 assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
353  1 assertEquals(2, testee.getEdit(0).getPosition());
354  1 assertEquals(3, testee.getEdit(0).getNumber());
355   
356    /*
357    * Add a non-contiguous edit - should not be merged.
358    */
359  1 e = new EditCommand().new Edit(Action.DELETE_GAP,
360    new SequenceI[] { edited }, 2, 1, al);
361  1 testee.addEdit(e);
362  1 assertEquals(2, testee.getSize());
363  1 assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
364  1 assertEquals(2, testee.getEdit(1).getPosition());
365  1 assertEquals(1, testee.getEdit(1).getNumber());
366   
367    /*
368    * Add an Insert after the Delete - should not be merged.
369    */
370  1 e = new EditCommand().new Edit(Action.INSERT_GAP,
371    new SequenceI[] { edited }, 1, 1, al);
372  1 testee.addEdit(e);
373  1 assertEquals(3, testee.getSize());
374  1 assertEquals(Action.INSERT_GAP, testee.getEdit(2).getAction());
375  1 assertEquals(1, testee.getEdit(2).getPosition());
376  1 assertEquals(1, testee.getEdit(2).getNumber());
377    }
378   
379    /**
380    * Test that the addEdit command correctly handles 'remove gaps' edits for the
381    * case when they appear contiguous but are acting on different sequences.
382    * They should not be merged.
383    */
 
384  1 toggle @Test(groups = { "Functional" })
385    public void testAddEdit_removeAllGaps()
386    {
387  1 seqs[0].setSequence("a???bcdefghjk");
388  1 Edit e = new EditCommand().new Edit(Action.DELETE_GAP,
389    new SequenceI[] { seqs[0] }, 4, 1, al);
390  1 testee.addEdit(e);
391   
392  1 seqs[1].setSequence("f??ghjklmnopq");
393  1 Edit e2 = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
394    { seqs[1] }, 3, 1, al);
395  1 testee.addEdit(e2);
396  1 assertEquals(2, testee.getSize());
397  1 assertSame(e, testee.getEdit(0));
398  1 assertSame(e2, testee.getEdit(1));
399    }
400   
401    /**
402    * Test that the addEdit command correctly merges insert gap commands acting
403    * on a multi-sequence selection.
404    */
 
405  1 toggle @Test(groups = { "Functional" })
406    public void testAddEdit_groupInsertGaps()
407    {
408    /*
409    * 2 insert gap in a row (aka mouse drag right), on two sequences:
410    */
411  1 Edit e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[] {
412    seqs[0], seqs[1] }, 1, 1, al);
413  1 testee.addEdit(e);
414  1 SequenceI seq1edited = new Sequence("seq0", "a?bcdefghjk");
415  1 seq1edited.setDatasetSequence(seqs[0].getDatasetSequence());
416  1 SequenceI seq2edited = new Sequence("seq1", "f?ghjklmnopq");
417  1 seq2edited.setDatasetSequence(seqs[1].getDatasetSequence());
418  1 e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[] {
419    seq1edited, seq2edited }, 2, 1, al);
420  1 testee.addEdit(e);
421   
422  1 assertEquals(1, testee.getSize());
423  1 assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
424  1 assertEquals(1, testee.getEdit(0).getPosition());
425  1 assertEquals(2, testee.getEdit(0).getNumber());
426  1 assertEquals(seqs[0].getDatasetSequence(), testee.getEdit(0)
427    .getSequences()[0].getDatasetSequence());
428  1 assertEquals(seqs[1].getDatasetSequence(), testee.getEdit(0)
429    .getSequences()[1].getDatasetSequence());
430    }
431   
432    /**
433    * Test for 'undoing' a series of gap insertions.
434    * <ul>
435    * <li>Start: ABCDEF insert 2 at pos 1</li>
436    * <li>next: A--BCDEF insert 1 at pos 4</li>
437    * <li>next: A--B-CDEF insert 2 at pos 0</li>
438    * <li>last: --A--B-CDEF</li>
439    * </ul>
440    */
 
441  1 toggle @Test(groups = { "Functional" })
442    public void testPriorState_multipleInserts()
443    {
444  1 EditCommand command = new EditCommand();
445  1 SequenceI seq = new Sequence("", "--A--B-CDEF");
446  1 SequenceI ds = new Sequence("", "ABCDEF");
447  1 seq.setDatasetSequence(ds);
448  1 SequenceI[] sqs = new SequenceI[] { seq };
449  1 Edit e = command.new Edit(Action.INSERT_GAP, sqs, 1, 2, '-');
450  1 command.addEdit(e);
451  1 e = command.new Edit(Action.INSERT_GAP, sqs, 4, 1, '-');
452  1 command.addEdit(e);
453  1 e = command.new Edit(Action.INSERT_GAP, sqs, 0, 2, '-');
454  1 command.addEdit(e);
455   
456  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
457  1 assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString());
458    }
459   
460    /**
461    * Test for 'undoing' a series of gap deletions.
462    * <ul>
463    * <li>Start: A-B-C delete 1 at pos 1</li>
464    * <li>Next: AB-C delete 1 at pos 2</li>
465    * <li>End: ABC</li>
466    * </ul>
467    */
 
468  1 toggle @Test(groups = { "Functional" })
469    public void testPriorState_removeAllGaps()
470    {
471  1 EditCommand command = new EditCommand();
472  1 SequenceI seq = new Sequence("", "ABC");
473  1 SequenceI ds = new Sequence("", "ABC");
474  1 seq.setDatasetSequence(ds);
475  1 SequenceI[] sqs = new SequenceI[] { seq };
476  1 Edit e = command.new Edit(Action.DELETE_GAP, sqs, 1, 1, '-');
477  1 command.addEdit(e);
478  1 e = command.new Edit(Action.DELETE_GAP, sqs, 2, 1, '-');
479  1 command.addEdit(e);
480   
481  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
482  1 assertEquals("A-B-C", unwound.get(ds).getSequenceAsString());
483    }
484   
485    /**
486    * Test for 'undoing' a single delete edit.
487    */
 
488  1 toggle @Test(groups = { "Functional" })
489    public void testPriorState_singleDelete()
490    {
491  1 EditCommand command = new EditCommand();
492  1 SequenceI seq = new Sequence("", "ABCDEF");
493  1 SequenceI ds = new Sequence("", "ABCDEF");
494  1 seq.setDatasetSequence(ds);
495  1 SequenceI[] sqs = new SequenceI[] { seq };
496  1 Edit e = command.new Edit(Action.DELETE_GAP, sqs, 2, 2, '-');
497  1 command.addEdit(e);
498   
499  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
500  1 assertEquals("AB--CDEF", unwound.get(ds).getSequenceAsString());
501    }
502   
503    /**
504    * Test 'undoing' a single gap insertion edit command.
505    */
 
506  1 toggle @Test(groups = { "Functional" })
507    public void testPriorState_singleInsert()
508    {
509  1 EditCommand command = new EditCommand();
510  1 SequenceI seq = new Sequence("", "AB---CDEF");
511  1 SequenceI ds = new Sequence("", "ABCDEF");
512  1 seq.setDatasetSequence(ds);
513  1 SequenceI[] sqs = new SequenceI[] { seq };
514  1 Edit e = command.new Edit(Action.INSERT_GAP, sqs, 2, 3, '-');
515  1 command.addEdit(e);
516   
517  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
518  1 SequenceI prior = unwound.get(ds);
519  1 assertEquals("ABCDEF", prior.getSequenceAsString());
520  1 assertEquals(1, prior.getStart());
521  1 assertEquals(6, prior.getEnd());
522    }
523   
524    /**
525    * Test 'undoing' a single gap insertion edit command, on a sequence whose
526    * start residue is other than 1
527    */
 
528  1 toggle @Test(groups = { "Functional" })
529    public void testPriorState_singleInsertWithOffset()
530    {
531  1 EditCommand command = new EditCommand();
532  1 SequenceI seq = new Sequence("", "AB---CDEF", 8, 13);
533    // SequenceI ds = new Sequence("", "ABCDEF", 8, 13);
534    // seq.setDatasetSequence(ds);
535  1 seq.createDatasetSequence();
536  1 SequenceI[] sqs = new SequenceI[] { seq };
537  1 Edit e = command.new Edit(Action.INSERT_GAP, sqs, 2, 3, '-');
538  1 command.addEdit(e);
539   
540  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
541  1 SequenceI prior = unwound.get(seq.getDatasetSequence());
542  1 assertEquals("ABCDEF", prior.getSequenceAsString());
543  1 assertEquals(8, prior.getStart());
544  1 assertEquals(13, prior.getEnd());
545    }
546   
547    /**
548    * Test that mimics 'remove all gaps' action. This generates delete gap edits
549    * for contiguous gaps in each sequence separately.
550    */
 
551  1 toggle @Test(groups = { "Functional" })
552    public void testPriorState_removeGapsMultipleSeqs()
553    {
554  1 EditCommand command = new EditCommand();
555  1 String original1 = "--ABC-DEF";
556  1 String original2 = "FG-HI--J";
557  1 String original3 = "M-NOPQ";
558   
559    /*
560    * Two edits for the first sequence
561    */
562  1 SequenceI seq = new Sequence("", "ABC-DEF");
563  1 SequenceI ds1 = new Sequence("", "ABCDEF");
564  1 seq.setDatasetSequence(ds1);
565  1 SequenceI[] sqs = new SequenceI[] { seq };
566  1 Edit e = command.new Edit(Action.DELETE_GAP, sqs, 0, 2, '-');
567  1 command.addEdit(e);
568  1 seq = new Sequence("", "ABCDEF");
569  1 seq.setDatasetSequence(ds1);
570  1 sqs = new SequenceI[] { seq };
571  1 e = command.new Edit(Action.DELETE_GAP, sqs, 3, 1, '-');
572  1 command.addEdit(e);
573   
574    /*
575    * Two edits for the second sequence
576    */
577  1 seq = new Sequence("", "FGHI--J");
578  1 SequenceI ds2 = new Sequence("", "FGHIJ");
579  1 seq.setDatasetSequence(ds2);
580  1 sqs = new SequenceI[] { seq };
581  1 e = command.new Edit(Action.DELETE_GAP, sqs, 2, 1, '-');
582  1 command.addEdit(e);
583  1 seq = new Sequence("", "FGHIJ");
584  1 seq.setDatasetSequence(ds2);
585  1 sqs = new SequenceI[] { seq };
586  1 e = command.new Edit(Action.DELETE_GAP, sqs, 4, 2, '-');
587  1 command.addEdit(e);
588   
589    /*
590    * One edit for the third sequence.
591    */
592  1 seq = new Sequence("", "MNOPQ");
593  1 SequenceI ds3 = new Sequence("", "MNOPQ");
594  1 seq.setDatasetSequence(ds3);
595  1 sqs = new SequenceI[] { seq };
596  1 e = command.new Edit(Action.DELETE_GAP, sqs, 1, 1, '-');
597  1 command.addEdit(e);
598   
599  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
600  1 assertEquals(original1, unwound.get(ds1).getSequenceAsString());
601  1 assertEquals(original2, unwound.get(ds2).getSequenceAsString());
602  1 assertEquals(original3, unwound.get(ds3).getSequenceAsString());
603    }
604   
605    /**
606    * Test that mimics 'remove all gapped columns' action. This generates a
607    * series Delete Gap edits that each act on all sequences that share a gapped
608    * column region.
609    */
 
610  1 toggle @Test(groups = { "Functional" })
611    public void testPriorState_removeGappedCols()
612    {
613  1 EditCommand command = new EditCommand();
614  1 String original1 = "--ABC--DEF";
615  1 String original2 = "-G-HI--J";
616  1 String original3 = "-M-NO--PQ";
617   
618    /*
619    * First edit deletes the first column.
620    */
621  1 SequenceI seq1 = new Sequence("", "-ABC--DEF");
622  1 SequenceI ds1 = new Sequence("", "ABCDEF");
623  1 seq1.setDatasetSequence(ds1);
624  1 SequenceI seq2 = new Sequence("", "G-HI--J");
625  1 SequenceI ds2 = new Sequence("", "GHIJ");
626  1 seq2.setDatasetSequence(ds2);
627  1 SequenceI seq3 = new Sequence("", "M-NO--PQ");
628  1 SequenceI ds3 = new Sequence("", "MNOPQ");
629  1 seq3.setDatasetSequence(ds3);
630  1 SequenceI[] sqs = new SequenceI[] { seq1, seq2, seq3 };
631  1 Edit e = command.new Edit(Action.DELETE_GAP, sqs, 0, 1, '-');
632  1 command.addEdit(e);
633   
634    /*
635    * Second edit deletes what is now columns 4 and 5.
636    */
637  1 seq1 = new Sequence("", "-ABCDEF");
638  1 seq1.setDatasetSequence(ds1);
639  1 seq2 = new Sequence("", "G-HIJ");
640  1 seq2.setDatasetSequence(ds2);
641  1 seq3 = new Sequence("", "M-NOPQ");
642  1 seq3.setDatasetSequence(ds3);
643  1 sqs = new SequenceI[] { seq1, seq2, seq3 };
644  1 e = command.new Edit(Action.DELETE_GAP, sqs, 4, 2, '-');
645  1 command.addEdit(e);
646   
647  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
648  1 assertEquals(original1, unwound.get(ds1).getSequenceAsString());
649  1 assertEquals(original2, unwound.get(ds2).getSequenceAsString());
650  1 assertEquals(original3, unwound.get(ds3).getSequenceAsString());
651  1 assertEquals(ds1, unwound.get(ds1).getDatasetSequence());
652  1 assertEquals(ds2, unwound.get(ds2).getDatasetSequence());
653  1 assertEquals(ds3, unwound.get(ds3).getDatasetSequence());
654    }
655   
656    /**
657    * Test a cut action's relocation of sequence features
658    */
 
659  0 toggle @Test(groups = { "Functional" })
660    public void testCut_withFeatures()
661    {
662    /*
663    * create sequence features before, after and overlapping
664    * a cut of columns/residues 4-7
665    */
666  0 SequenceI seq0 = seqs[0];
667  0 seq0.addSequenceFeature(new SequenceFeature("before", "", 1, 3, 0f,
668    null));
669  0 seq0.addSequenceFeature(new SequenceFeature("overlap left", "", 2, 6,
670    0f, null));
671  0 seq0.addSequenceFeature(new SequenceFeature("internal", "", 5, 6, 0f,
672    null));
673  0 seq0.addSequenceFeature(new SequenceFeature("overlap right", "", 7, 8,
674    0f, null));
675  0 seq0.addSequenceFeature(new SequenceFeature("after", "", 8, 10, 0f,
676    null));
677   
678  0 Edit ec = testee.new Edit(Action.CUT, seqs, 3, 4, al); // cols 3-6 base 0
679  0 EditCommand.cut(ec, new AlignmentI[] { al });
680   
681  0 List<SequenceFeature> sfs = seq0.getSequenceFeatures();
682  0 SequenceFeatures.sortFeatures(sfs, true);
683   
684  0 assertEquals(4, sfs.size()); // feature internal to cut has been deleted
685  0 SequenceFeature sf = sfs.get(0);
686  0 assertEquals("before", sf.getType());
687  0 assertEquals(1, sf.getBegin());
688  0 assertEquals(3, sf.getEnd());
689  0 sf = sfs.get(1);
690  0 assertEquals("overlap left", sf.getType());
691  0 assertEquals(2, sf.getBegin());
692  0 assertEquals(3, sf.getEnd()); // truncated by cut
693  0 sf = sfs.get(2);
694  0 assertEquals("overlap right", sf.getType());
695  0 assertEquals(4, sf.getBegin()); // shifted left by cut
696  0 assertEquals(5, sf.getEnd()); // truncated by cut
697  0 sf = sfs.get(3);
698  0 assertEquals("after", sf.getType());
699  0 Test failure here assertEquals(4, sf.getBegin()); // shifted left by cut
700  0 assertEquals(6, sf.getEnd()); // shifted left by cut
701    }
702   
703    /**
704    * Test a cut action's relocation of sequence features, with full coverage of
705    * all possible feature and cut locations for a 5-position ungapped sequence
706    */
 
707  1 toggle @Test(groups = { "Functional" })
708    public void testCut_withFeatures_exhaustive()
709    {
710    /*
711    * create a sequence features on each subrange of 1-5
712    */
713  1 SequenceI seq0 = new Sequence("seq", "ABCDE");
714  1 AlignmentI alignment = new Alignment(new SequenceI[] { seq0 });
715  1 alignment.setDataset(null);
716  6 for (int from = 1; from <= seq0.getLength(); from++)
717    {
718  20 for (int to = from; to <= seq0.getLength(); to++)
719    {
720  15 String desc = String.format("%d-%d", from, to);
721  15 SequenceFeature sf = new SequenceFeature("test", desc, from, to,
722    0f,
723    null);
724  15 sf.setValue("from", Integer.valueOf(from));
725  15 sf.setValue("to", Integer.valueOf(to));
726  15 seq0.addSequenceFeature(sf);
727    }
728    }
729    // sanity check
730  1 List<SequenceFeature> sfs = seq0.getSequenceFeatures();
731  1 assertEquals(func(5), sfs.size());
732   
733    /*
734    * now perform all possible cuts of subranges of 1-5 (followed by Undo)
735    * and validate the resulting remaining sequence features!
736    */
737  1 SequenceI[] sqs = new SequenceI[] { seq0 };
738   
739    // goal is to have this passing for all from/to values!!
740    // for (int from = 0; from < seq0.getLength(); from++)
741    // {
742    // for (int to = from; to < seq0.getLength(); to++)
743  3 for (int from = 1; from < 3; from++)
744    {
745  4 for (int to = 2; to < 3; to++)
746    {
747  2 testee.appendEdit(Action.CUT, sqs, from, (to - from + 1),
748    alignment, true);
749   
750  2 sfs = seq0.getSequenceFeatures();
751   
752    /*
753    * confirm the number of features has reduced by the
754    * number of features within the cut region i.e. by
755    * func(length of cut)
756    */
757  2 String msg = String.format("Cut %d-%d ", from, to);
758  2 if (to - from == 4)
759    {
760    // all columns cut
761  0 assertNull(sfs);
762    }
763    else
764    {
765  2 assertEquals(msg + "wrong number of features left", func(5)
766    - func(to - from + 1), sfs.size());
767    }
768   
769    /*
770    * inspect individual features
771    */
772  2 if (sfs != null)
773    {
774  2 for (SequenceFeature sf : sfs)
775    {
776  26 checkFeatureRelocation(sf, from + 1, to + 1);
777    }
778    }
779    /*
780    * undo ready for next cut
781    */
782  2 testee.undoCommand(new AlignmentI[] { alignment });
783  2 assertEquals(func(5), seq0.getSequenceFeatures().size());
784    }
785    }
786    }
787   
788    /**
789    * Helper method to check a feature has been correctly relocated after a cut
790    *
791    * @param sf
792    * @param from
793    * start of cut (first residue cut)
794    * @param to
795    * end of cut (last residue cut)
796    */
 
797  26 toggle private void checkFeatureRelocation(SequenceFeature sf, int from, int to)
798    {
799    // TODO handle the gapped sequence case as well
800  26 int cutSize = to - from + 1;
801  26 int oldFrom = ((Integer) sf.getValue("from")).intValue();
802  26 int oldTo = ((Integer) sf.getValue("to")).intValue();
803   
804  26 String msg = String.format(
805    "Feature %s relocated to %d-%d after cut of %d-%d",
806    sf.getDescription(), sf.getBegin(), sf.getEnd(), from, to);
807  26 if (oldTo < from)
808    {
809    // before cut region so unchanged
810  4 assertEquals("1: " + msg, oldFrom, sf.getBegin());
811  4 assertEquals("2: " + msg, oldTo, sf.getEnd());
812    }
813  22 else if (oldFrom > to)
814    {
815    // follows cut region - shift by size of cut
816  6 assertEquals("3: " + msg, oldFrom - cutSize, sf.getBegin());
817  6 assertEquals("4: " + msg, oldTo - cutSize, sf.getEnd());
818    }
819  16 else if (oldFrom < from && oldTo > to)
820    {
821    // feature encloses cut region - shrink it right
822  6 assertEquals("5: " + msg, oldFrom, sf.getBegin());
823  6 assertEquals("6: " + msg, oldTo - cutSize, sf.getEnd());
824    }
825  10 else if (oldFrom < from)
826    {
827    // feature overlaps left side of cut region - truncated right
828  4 assertEquals("7: " + msg, from - 1, sf.getEnd());
829    }
830  6 else if (oldTo > to)
831    {
832    // feature overlaps right side of cut region - truncated left
833  6 assertEquals("8: " + msg, from, sf.getBegin());
834  6 assertEquals("9: " + msg, from + oldTo - to - 1, sf.getEnd());
835    }
836    else
837    {
838    // feature internal to cut - should have been deleted!
839  0 Assert.fail(msg + " - should have been deleted");
840    }
841    }
842   
843    /**
844    * Test a cut action's relocation of sequence features
845    */
 
846  0 toggle @Test(groups = { "Functional" })
847    public void testCut_gappedWithFeatures()
848    {
849    /*
850    * create sequence features before, after and overlapping
851    * a cut of columns/residues 4-7
852    */
853  0 SequenceI seq0 = new Sequence("seq", "A-BCC");
854  0 seq0.addSequenceFeature(new SequenceFeature("", "", 3, 4, 0f,
855    null));
856  0 AlignmentI alignment = new Alignment(new SequenceI[] { seq0 });
857    // cut columns of A-B
858  0 Edit ec = testee.new Edit(Action.CUT, seqs, 0, 3, alignment); // cols 0-3
859    // base 0
860  0 EditCommand.cut(ec, new AlignmentI[] { alignment });
861   
862    /*
863    * feature on CC(3-4) should now be on CC(1-2)
864    */
865  0 List<SequenceFeature> sfs = seq0.getSequenceFeatures();
866  0 assertEquals(1, sfs.size());
867  0 SequenceFeature sf = sfs.get(0);
868  0 Test failure here assertEquals(1, sf.getBegin());
869  0 assertEquals(2, sf.getEnd());
870   
871    // TODO add further cases including Undo - see JAL-2541
872    }
873    }