Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.commands

File EditCommandTest.java

 

Code metrics

36
473
33
1
1,166
732
52
0.11
14.33
33
1.58

Classes

Class Line # Actions
EditCommandTest 53 473 52
0.9686346796.9%
 

Contributing tests

This file is covered by 25 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.assertSame;
25    import static org.testng.AssertJUnit.assertTrue;
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.Collections;
38    import java.util.Comparator;
39    import java.util.List;
40    import java.util.Map;
41   
42    import org.testng.Assert;
43    import org.testng.annotations.BeforeClass;
44    import org.testng.annotations.BeforeMethod;
45    import org.testng.annotations.Test;
46   
47    /**
48    * Unit tests for EditCommand
49    *
50    * @author gmcarstairs
51    *
52    */
 
53    public class EditCommandTest
54    {
55    private static Comparator<SequenceFeature> BY_DESCRIPTION = new Comparator<SequenceFeature>()
56    {
57   
 
58  1144 toggle @Override
59    public int compare(SequenceFeature o1, SequenceFeature o2)
60    {
61  1144 return o1.getDescription().compareTo(o2.getDescription());
62    }
63    };
64   
65    private EditCommand testee;
66   
67    private SequenceI[] seqs;
68   
69    private Alignment al;
70   
71    /*
72    * compute n(n+1)/2 e.g.
73    * func(5) = 5 + 4 + 3 + 2 + 1 = 15
74    */
 
75  58 toggle private static int func(int i)
76    {
77  58 return i * (i + 1) / 2;
78    }
79   
 
80  1 toggle @BeforeClass(alwaysRun = true)
81    public void setUpJvOptionPane()
82    {
83  1 JvOptionPane.setInteractiveMode(false);
84  1 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
85    }
86   
 
87  25 toggle @BeforeMethod(alwaysRun = true)
88    public void setUp()
89    {
90  25 testee = new EditCommand();
91  25 seqs = new SequenceI[4];
92  25 seqs[0] = new Sequence("seq0", "abcdefghjk");
93  25 seqs[0].setDatasetSequence(new Sequence("seq0ds", "ABCDEFGHJK"));
94  25 seqs[1] = new Sequence("seq1", "fghjklmnopq");
95  25 seqs[1].setDatasetSequence(new Sequence("seq1ds", "FGHJKLMNOPQ"));
96  25 seqs[2] = new Sequence("seq2", "qrstuvwxyz");
97  25 seqs[2].setDatasetSequence(new Sequence("seq2ds", "QRSTUVWXYZ"));
98  25 seqs[3] = new Sequence("seq3", "1234567890");
99  25 seqs[3].setDatasetSequence(new Sequence("seq3ds", "1234567890"));
100  25 al = new Alignment(seqs);
101  25 al.setGapCharacter('?');
102    }
103   
104    /**
105    * Test inserting gap characters
106    */
 
107  1 toggle @Test(groups = { "Functional" })
108    public void testAppendEdit_insertGap()
109    {
110    // set a non-standard gap character to prove it is actually used
111  1 testee.appendEdit(Action.INSERT_GAP, seqs, 4, 3, al, true);
112  1 assertEquals("abcd???efghjk", seqs[0].getSequenceAsString());
113  1 assertEquals("fghj???klmnopq", seqs[1].getSequenceAsString());
114  1 assertEquals("qrst???uvwxyz", seqs[2].getSequenceAsString());
115  1 assertEquals("1234???567890", seqs[3].getSequenceAsString());
116   
117    // todo: test for handling out of range positions?
118    }
119   
120    /**
121    * Test deleting characters from sequences. Note the deleteGap() action does
122    * not check that only gap characters are being removed.
123    */
 
124  1 toggle @Test(groups = { "Functional" })
125    public void testAppendEdit_deleteGap()
126    {
127  1 testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true);
128  1 assertEquals("abcdhjk", seqs[0].getSequenceAsString());
129  1 assertEquals("fghjnopq", seqs[1].getSequenceAsString());
130  1 assertEquals("qrstxyz", seqs[2].getSequenceAsString());
131  1 assertEquals("1234890", seqs[3].getSequenceAsString());
132    }
133   
134    /**
135    * Test a cut action. The command should store the cut characters to support
136    * undo.
137    */
 
138  1 toggle @Test(groups = { "Functional" })
139    public void testCut()
140    {
141  1 Edit ec = testee.new Edit(Action.CUT, seqs, 4, 3, al);
142  1 EditCommand.cut(ec, new AlignmentI[] { al });
143  1 assertEquals("abcdhjk", seqs[0].getSequenceAsString());
144  1 assertEquals("fghjnopq", seqs[1].getSequenceAsString());
145  1 assertEquals("qrstxyz", seqs[2].getSequenceAsString());
146  1 assertEquals("1234890", seqs[3].getSequenceAsString());
147   
148  1 assertEquals("efg", new String(ec.string[0]));
149  1 assertEquals("klm", new String(ec.string[1]));
150  1 assertEquals("uvw", new String(ec.string[2]));
151  1 assertEquals("567", new String(ec.string[3]));
152    // TODO: case where whole sequence is deleted as nothing left; etc
153    }
154   
155    /**
156    * Test a Paste action, followed by Undo and Redo
157    */
 
158  0 toggle @Test(groups = { "Functional" }, enabled = false)
159    public void testPaste_undo_redo()
160    {
161    // TODO code this test properly, bearing in mind that:
162    // Paste action requires something on the clipboard (Cut/Copy)
163    // - EditCommand.paste doesn't add sequences to the alignment
164    // ... that is done in AlignFrame.paste()
165    // ... unless as a Redo
166    // ...
167   
168  0 SequenceI[] newSeqs = new SequenceI[2];
169  0 newSeqs[0] = new Sequence("newseq0", "ACEFKL");
170  0 newSeqs[1] = new Sequence("newseq1", "JWMPDH");
171   
172  0 new EditCommand("Paste", Action.PASTE, newSeqs, 0, al.getWidth(), al);
173  0 assertEquals(6, al.getSequences().size());
174  0 assertEquals("1234567890", seqs[3].getSequenceAsString());
175  0 assertEquals("ACEFKL", seqs[4].getSequenceAsString());
176  0 assertEquals("JWMPDH", seqs[5].getSequenceAsString());
177    }
178   
179    /**
180    * Test insertGap followed by undo command
181    */
 
182  1 toggle @Test(groups = { "Functional" })
183    public void testUndo_insertGap()
184    {
185    // Edit ec = testee.new Edit(Action.INSERT_GAP, seqs, 4, 3, '?');
186  1 testee.appendEdit(Action.INSERT_GAP, seqs, 4, 3, al, true);
187    // check something changed
188  1 assertEquals("abcd???efghjk", seqs[0].getSequenceAsString());
189  1 testee.undoCommand(new AlignmentI[] { al });
190  1 assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
191  1 assertEquals("fghjklmnopq", seqs[1].getSequenceAsString());
192  1 assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
193  1 assertEquals("1234567890", seqs[3].getSequenceAsString());
194    }
195   
196    /**
197    * Test deleteGap followed by undo command
198    */
 
199  1 toggle @Test(groups = { "Functional" })
200    public void testUndo_deleteGap()
201    {
202  1 testee.appendEdit(Action.DELETE_GAP, seqs, 4, 3, al, true);
203    // check something changed
204  1 assertEquals("abcdhjk", seqs[0].getSequenceAsString());
205  1 testee.undoCommand(new AlignmentI[] { al });
206    // deleteGap doesn't 'remember' deleted characters, only gaps get put back
207  1 assertEquals("abcd???hjk", seqs[0].getSequenceAsString());
208  1 assertEquals("fghj???nopq", seqs[1].getSequenceAsString());
209  1 assertEquals("qrst???xyz", seqs[2].getSequenceAsString());
210  1 assertEquals("1234???890", seqs[3].getSequenceAsString());
211    }
212   
213    /**
214    * Test several commands followed by an undo command
215    */
 
216  1 toggle @Test(groups = { "Functional" })
217    public void testUndo_multipleCommands()
218    {
219    // delete positions 3/4/5 (counting from 1)
220  1 testee.appendEdit(Action.DELETE_GAP, seqs, 2, 3, al, true);
221  1 assertEquals("abfghjk", seqs[0].getSequenceAsString());
222  1 assertEquals("1267890", seqs[3].getSequenceAsString());
223   
224    // insert 2 gaps after the second residue
225  1 testee.appendEdit(Action.INSERT_GAP, seqs, 2, 2, al, true);
226  1 assertEquals("ab??fghjk", seqs[0].getSequenceAsString());
227  1 assertEquals("12??67890", seqs[3].getSequenceAsString());
228   
229    // delete positions 4/5/6
230  1 testee.appendEdit(Action.DELETE_GAP, seqs, 3, 3, al, true);
231  1 assertEquals("ab?hjk", seqs[0].getSequenceAsString());
232  1 assertEquals("12?890", seqs[3].getSequenceAsString());
233   
234    // undo edit commands
235  1 testee.undoCommand(new AlignmentI[] { al });
236  1 assertEquals("ab?????hjk", seqs[0].getSequenceAsString());
237  1 assertEquals("12?????890", seqs[3].getSequenceAsString());
238    }
239   
240    /**
241    * Unit test for JAL-1594 bug: click and drag sequence right to insert gaps -
242    * undo did not remove them all.
243    */
 
244  1 toggle @Test(groups = { "Functional" })
245    public void testUndo_multipleInsertGaps()
246    {
247  1 testee.appendEdit(Action.INSERT_GAP, seqs, 4, 1, al, true);
248  1 testee.appendEdit(Action.INSERT_GAP, seqs, 5, 1, al, true);
249  1 testee.appendEdit(Action.INSERT_GAP, seqs, 6, 1, al, true);
250   
251    // undo edit commands
252  1 testee.undoCommand(new AlignmentI[] { al });
253  1 assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
254  1 assertEquals("1234567890", seqs[3].getSequenceAsString());
255   
256    }
257   
258    /**
259    * Test cut followed by undo command
260    */
 
261  1 toggle @Test(groups = { "Functional" })
262    public void testUndo_cut()
263    {
264  1 testee.appendEdit(Action.CUT, seqs, 4, 3, al, true);
265    // check something changed
266  1 assertEquals("abcdhjk", seqs[0].getSequenceAsString());
267  1 testee.undoCommand(new AlignmentI[] { al });
268  1 assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
269  1 assertEquals("fghjklmnopq", seqs[1].getSequenceAsString());
270  1 assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
271  1 assertEquals("1234567890", seqs[3].getSequenceAsString());
272    }
273   
274    /**
275    * Test the replace command (used to manually edit a sequence)
276    */
 
277  1 toggle @Test(groups = { "Functional" })
278    public void testReplace()
279    {
280    // seem to need a dataset sequence on the edited sequence here
281  1 seqs[1].createDatasetSequence();
282  1 assertEquals("fghjklmnopq", seqs[1].getSequenceAsString());
283    // NB command.number holds end position for a Replace command
284  1 new EditCommand("", Action.REPLACE, "Z-xY", new SequenceI[] { seqs[1] },
285    4, 8, al);
286  1 assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
287  1 assertEquals("fghjZ-xYopq", seqs[1].getSequenceAsString());
288    // Dataset Sequence should always be uppercase
289  1 assertEquals("fghjZxYopq".toUpperCase(),
290    seqs[1].getDatasetSequence().getSequenceAsString());
291  1 assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
292  1 assertEquals("1234567890", seqs[3].getSequenceAsString());
293    }
294   
295    /**
296    * Test the replace command (used to manually edit a sequence)
297    */
 
298  1 toggle @Test(groups = { "Functional" })
299    public void testReplace_withGaps()
300    {
301  1 SequenceI seq = new Sequence("seq", "ABC--DEF");
302  1 seq.createDatasetSequence();
303  1 assertEquals("ABCDEF", seq.getDatasetSequence().getSequenceAsString());
304  1 assertEquals(1, seq.getStart());
305  1 assertEquals(6, seq.getEnd());
306   
307    /*
308    * replace C- with XYZ
309    * NB arg4 = start column of selection for edit (base 0)
310    * arg5 = column after end of selection for edit
311    */
312  1 EditCommand edit = new EditCommand("", Action.REPLACE, "xyZ",
313    new SequenceI[]
314    { seq }, 2,
315    4, al);
316  1 assertEquals("ABxyZ-DEF", seq.getSequenceAsString());
317  1 assertEquals(1, seq.getStart());
318  1 assertEquals(8, seq.getEnd());
319    // Dataset sequence always uppercase
320  1 assertEquals("ABxyZDEF".toUpperCase(),
321    seq.getDatasetSequence().getSequenceAsString());
322  1 assertEquals(8, seq.getDatasetSequence().getEnd());
323   
324    /*
325    * undo the edit
326    */
327  1 AlignmentI[] views = new AlignmentI[]
328    { new Alignment(new SequenceI[] { seq }) };
329  1 edit.undoCommand(views);
330   
331  1 assertEquals("ABC--DEF", seq.getSequenceAsString());
332  1 assertEquals("ABCDEF", seq.getDatasetSequence().getSequenceAsString());
333  1 assertEquals(1, seq.getStart());
334  1 assertEquals(6, seq.getEnd());
335  1 assertEquals(6, seq.getDatasetSequence().getEnd());
336   
337    /*
338    * redo the edit
339    */
340  1 edit.doCommand(views);
341   
342  1 assertEquals("ABxyZ-DEF", seq.getSequenceAsString());
343  1 assertEquals(1, seq.getStart());
344  1 assertEquals(8, seq.getEnd());
345    // dataset sequence should be Uppercase
346  1 assertEquals("ABxyZDEF".toUpperCase(),
347    seq.getDatasetSequence().getSequenceAsString());
348  1 assertEquals(8, seq.getDatasetSequence().getEnd());
349   
350    }
351   
352    /**
353    * Test replace command when it doesn't cause a sequence edit (see comment in
354    */
 
355  1 toggle @Test(groups = { "Functional" })
356    public void testReplaceFirstResiduesWithGaps()
357    {
358    // test replace when gaps are inserted at start. Start/end should change
359    // w.r.t. original edited sequence.
360  1 SequenceI dsseq = seqs[1].getDatasetSequence();
361  1 EditCommand edit = new EditCommand("", Action.REPLACE, "----",
362    new SequenceI[]
363    { seqs[1] }, 0, 4, al);
364   
365    // trimmed start
366  1 assertEquals("----klmnopq", seqs[1].getSequenceAsString());
367    // and ds is preserved
368  1 assertTrue(dsseq == seqs[1].getDatasetSequence());
369    // and it is unchanged and UPPERCASE !
370  1 assertEquals("fghjklmnopq".toUpperCase(), dsseq.getSequenceAsString());
371    // and that alignment sequence start has been adjusted
372  1 assertEquals(5, seqs[1].getStart());
373  1 assertEquals(11, seqs[1].getEnd());
374   
375  1 AlignmentI[] views = new AlignmentI[] { new Alignment(seqs) };
376    // and undo
377  1 edit.undoCommand(views);
378   
379    // dataset sequence unchanged
380  1 assertTrue(dsseq == seqs[1].getDatasetSequence());
381    // restore sequence
382  1 assertEquals("fghjklmnopq", seqs[1].getSequenceAsString());
383    // and start/end numbering also restored
384  1 assertEquals(1, seqs[1].getStart());
385  1 assertEquals(11, seqs[1].getEnd());
386   
387    // now redo
388  1 edit.undoCommand(views);
389   
390    // and repeat asserts for the original edit
391   
392    // trimmed start
393  1 assertEquals("----klmnopq", seqs[1].getSequenceAsString());
394    // and ds is preserved
395  1 assertTrue(dsseq == seqs[1].getDatasetSequence());
396    // and it is unchanged AND UPPERCASE !
397  1 assertEquals("fghjklmnopq".toUpperCase(), dsseq.getSequenceAsString());
398    // and that alignment sequence start has been adjusted
399  1 assertEquals(5, seqs[1].getStart());
400  1 assertEquals(11, seqs[1].getEnd());
401   
402    }
403   
404    /**
405    * Test that the addEdit command correctly merges insert gap commands when
406    * possible.
407    */
 
408  1 toggle @Test(groups = { "Functional" })
409    public void testAddEdit_multipleInsertGap()
410    {
411    /*
412    * 3 insert gap in a row (aka mouse drag right):
413    */
414  1 Edit e = new EditCommand().new Edit(Action.INSERT_GAP,
415    new SequenceI[] { seqs[0] }, 1, 1, al);
416  1 testee.addEdit(e);
417  1 SequenceI edited = new Sequence("seq0", "a?bcdefghjk");
418  1 edited.setDatasetSequence(seqs[0].getDatasetSequence());
419  1 e = new EditCommand().new Edit(Action.INSERT_GAP,
420    new SequenceI[] { edited }, 2, 1, al);
421  1 testee.addEdit(e);
422  1 edited = new Sequence("seq0", "a??bcdefghjk");
423  1 edited.setDatasetSequence(seqs[0].getDatasetSequence());
424  1 e = new EditCommand().new Edit(Action.INSERT_GAP,
425    new SequenceI[] { edited }, 3, 1, al);
426  1 testee.addEdit(e);
427  1 assertEquals(1, testee.getSize());
428  1 assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
429  1 assertEquals(1, testee.getEdit(0).getPosition());
430  1 assertEquals(3, testee.getEdit(0).getNumber());
431   
432    /*
433    * Add a non-contiguous edit - should not be merged.
434    */
435  1 e = new EditCommand().new Edit(Action.INSERT_GAP,
436    new SequenceI[] { edited }, 5, 2, al);
437  1 testee.addEdit(e);
438  1 assertEquals(2, testee.getSize());
439  1 assertEquals(5, testee.getEdit(1).getPosition());
440  1 assertEquals(2, testee.getEdit(1).getNumber());
441   
442    /*
443    * Add a Delete after the Insert - should not be merged.
444    */
445  1 e = new EditCommand().new Edit(Action.DELETE_GAP,
446    new SequenceI[] { edited }, 6, 2, al);
447  1 testee.addEdit(e);
448  1 assertEquals(3, testee.getSize());
449  1 assertEquals(Action.DELETE_GAP, testee.getEdit(2).getAction());
450  1 assertEquals(6, testee.getEdit(2).getPosition());
451  1 assertEquals(2, testee.getEdit(2).getNumber());
452    }
453   
454    /**
455    * Test that the addEdit command correctly merges delete gap commands when
456    * possible.
457    */
 
458  1 toggle @Test(groups = { "Functional" })
459    public void testAddEdit_multipleDeleteGap()
460    {
461    /*
462    * 3 delete gap in a row (aka mouse drag left):
463    */
464  1 seqs[0].setSequence("a???bcdefghjk");
465  1 Edit e = new EditCommand().new Edit(Action.DELETE_GAP,
466    new SequenceI[] { seqs[0] }, 4, 1, al);
467  1 testee.addEdit(e);
468  1 assertEquals(1, testee.getSize());
469   
470  1 SequenceI edited = new Sequence("seq0", "a??bcdefghjk");
471  1 edited.setDatasetSequence(seqs[0].getDatasetSequence());
472  1 e = new EditCommand().new Edit(Action.DELETE_GAP,
473    new SequenceI[] { edited }, 3, 1, al);
474  1 testee.addEdit(e);
475  1 assertEquals(1, testee.getSize());
476   
477  1 edited = new Sequence("seq0", "a?bcdefghjk");
478  1 edited.setDatasetSequence(seqs[0].getDatasetSequence());
479  1 e = new EditCommand().new Edit(Action.DELETE_GAP,
480    new SequenceI[] { edited }, 2, 1, al);
481  1 testee.addEdit(e);
482  1 assertEquals(1, testee.getSize());
483  1 assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
484  1 assertEquals(2, testee.getEdit(0).getPosition());
485  1 assertEquals(3, testee.getEdit(0).getNumber());
486   
487    /*
488    * Add a non-contiguous edit - should not be merged.
489    */
490  1 e = new EditCommand().new Edit(Action.DELETE_GAP,
491    new SequenceI[] { edited }, 2, 1, al);
492  1 testee.addEdit(e);
493  1 assertEquals(2, testee.getSize());
494  1 assertEquals(Action.DELETE_GAP, testee.getEdit(0).getAction());
495  1 assertEquals(2, testee.getEdit(1).getPosition());
496  1 assertEquals(1, testee.getEdit(1).getNumber());
497   
498    /*
499    * Add an Insert after the Delete - should not be merged.
500    */
501  1 e = new EditCommand().new Edit(Action.INSERT_GAP,
502    new SequenceI[] { edited }, 1, 1, al);
503  1 testee.addEdit(e);
504  1 assertEquals(3, testee.getSize());
505  1 assertEquals(Action.INSERT_GAP, testee.getEdit(2).getAction());
506  1 assertEquals(1, testee.getEdit(2).getPosition());
507  1 assertEquals(1, testee.getEdit(2).getNumber());
508    }
509   
510    /**
511    * Test that the addEdit command correctly handles 'remove gaps' edits for the
512    * case when they appear contiguous but are acting on different sequences.
513    * They should not be merged.
514    */
 
515  1 toggle @Test(groups = { "Functional" })
516    public void testAddEdit_removeAllGaps()
517    {
518  1 seqs[0].setSequence("a???bcdefghjk");
519  1 Edit e = new EditCommand().new Edit(Action.DELETE_GAP,
520    new SequenceI[] { seqs[0] }, 4, 1, al);
521  1 testee.addEdit(e);
522   
523  1 seqs[1].setSequence("f??ghjklmnopq");
524  1 Edit e2 = new EditCommand().new Edit(Action.DELETE_GAP, new SequenceI[]
525    { seqs[1] }, 3, 1, al);
526  1 testee.addEdit(e2);
527  1 assertEquals(2, testee.getSize());
528  1 assertSame(e, testee.getEdit(0));
529  1 assertSame(e2, testee.getEdit(1));
530    }
531   
532    /**
533    * Test that the addEdit command correctly merges insert gap commands acting
534    * on a multi-sequence selection.
535    */
 
536  1 toggle @Test(groups = { "Functional" })
537    public void testAddEdit_groupInsertGaps()
538    {
539    /*
540    * 2 insert gap in a row (aka mouse drag right), on two sequences:
541    */
542  1 Edit e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[] {
543    seqs[0], seqs[1] }, 1, 1, al);
544  1 testee.addEdit(e);
545  1 SequenceI seq1edited = new Sequence("seq0", "a?bcdefghjk");
546  1 seq1edited.setDatasetSequence(seqs[0].getDatasetSequence());
547  1 SequenceI seq2edited = new Sequence("seq1", "f?ghjklmnopq");
548  1 seq2edited.setDatasetSequence(seqs[1].getDatasetSequence());
549  1 e = new EditCommand().new Edit(Action.INSERT_GAP, new SequenceI[] {
550    seq1edited, seq2edited }, 2, 1, al);
551  1 testee.addEdit(e);
552   
553  1 assertEquals(1, testee.getSize());
554  1 assertEquals(Action.INSERT_GAP, testee.getEdit(0).getAction());
555  1 assertEquals(1, testee.getEdit(0).getPosition());
556  1 assertEquals(2, testee.getEdit(0).getNumber());
557  1 assertEquals(seqs[0].getDatasetSequence(), testee.getEdit(0)
558    .getSequences()[0].getDatasetSequence());
559  1 assertEquals(seqs[1].getDatasetSequence(), testee.getEdit(0)
560    .getSequences()[1].getDatasetSequence());
561    }
562   
563    /**
564    * Test for 'undoing' a series of gap insertions.
565    * <ul>
566    * <li>Start: ABCDEF insert 2 at pos 1</li>
567    * <li>next: A--BCDEF insert 1 at pos 4</li>
568    * <li>next: A--B-CDEF insert 2 at pos 0</li>
569    * <li>last: --A--B-CDEF</li>
570    * </ul>
571    */
 
572  1 toggle @Test(groups = { "Functional" })
573    public void testPriorState_multipleInserts()
574    {
575  1 EditCommand command = new EditCommand();
576  1 SequenceI seq = new Sequence("", "--A--B-CDEF");
577  1 SequenceI ds = new Sequence("", "ABCDEF");
578  1 seq.setDatasetSequence(ds);
579  1 SequenceI[] sqs = new SequenceI[] { seq };
580  1 Edit e = command.new Edit(Action.INSERT_GAP, sqs, 1, 2, '-');
581  1 command.addEdit(e);
582  1 e = command.new Edit(Action.INSERT_GAP, sqs, 4, 1, '-');
583  1 command.addEdit(e);
584  1 e = command.new Edit(Action.INSERT_GAP, sqs, 0, 2, '-');
585  1 command.addEdit(e);
586   
587  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
588  1 assertEquals("ABCDEF", unwound.get(ds).getSequenceAsString());
589    }
590   
591    /**
592    * Test for 'undoing' a series of gap deletions.
593    * <ul>
594    * <li>Start: A-B-C delete 1 at pos 1</li>
595    * <li>Next: AB-C delete 1 at pos 2</li>
596    * <li>End: ABC</li>
597    * </ul>
598    */
 
599  1 toggle @Test(groups = { "Functional" })
600    public void testPriorState_removeAllGaps()
601    {
602  1 EditCommand command = new EditCommand();
603  1 SequenceI seq = new Sequence("", "ABC");
604  1 SequenceI ds = new Sequence("", "ABC");
605  1 seq.setDatasetSequence(ds);
606  1 SequenceI[] sqs = new SequenceI[] { seq };
607  1 Edit e = command.new Edit(Action.DELETE_GAP, sqs, 1, 1, '-');
608  1 command.addEdit(e);
609  1 e = command.new Edit(Action.DELETE_GAP, sqs, 2, 1, '-');
610  1 command.addEdit(e);
611   
612  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
613  1 assertEquals("A-B-C", unwound.get(ds).getSequenceAsString());
614    }
615   
616    /**
617    * Test for 'undoing' a single delete edit.
618    */
 
619  1 toggle @Test(groups = { "Functional" })
620    public void testPriorState_singleDelete()
621    {
622  1 EditCommand command = new EditCommand();
623  1 SequenceI seq = new Sequence("", "ABCDEF");
624  1 SequenceI ds = new Sequence("", "ABCDEF");
625  1 seq.setDatasetSequence(ds);
626  1 SequenceI[] sqs = new SequenceI[] { seq };
627  1 Edit e = command.new Edit(Action.DELETE_GAP, sqs, 2, 2, '-');
628  1 command.addEdit(e);
629   
630  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
631  1 assertEquals("AB--CDEF", unwound.get(ds).getSequenceAsString());
632    }
633   
634    /**
635    * Test 'undoing' a single gap insertion edit command.
636    */
 
637  1 toggle @Test(groups = { "Functional" })
638    public void testPriorState_singleInsert()
639    {
640  1 EditCommand command = new EditCommand();
641  1 SequenceI seq = new Sequence("", "AB---CDEF");
642  1 SequenceI ds = new Sequence("", "ABCDEF");
643  1 seq.setDatasetSequence(ds);
644  1 SequenceI[] sqs = new SequenceI[] { seq };
645  1 Edit e = command.new Edit(Action.INSERT_GAP, sqs, 2, 3, '-');
646  1 command.addEdit(e);
647   
648  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
649  1 SequenceI prior = unwound.get(ds);
650  1 assertEquals("ABCDEF", prior.getSequenceAsString());
651  1 assertEquals(1, prior.getStart());
652  1 assertEquals(6, prior.getEnd());
653    }
654   
655    /**
656    * Test 'undoing' a single gap insertion edit command, on a sequence whose
657    * start residue is other than 1
658    */
 
659  1 toggle @Test(groups = { "Functional" })
660    public void testPriorState_singleInsertWithOffset()
661    {
662  1 EditCommand command = new EditCommand();
663  1 SequenceI seq = new Sequence("", "AB---CDEF", 8, 13);
664    // SequenceI ds = new Sequence("", "ABCDEF", 8, 13);
665    // seq.setDatasetSequence(ds);
666  1 seq.createDatasetSequence();
667  1 SequenceI[] sqs = new SequenceI[] { seq };
668  1 Edit e = command.new Edit(Action.INSERT_GAP, sqs, 2, 3, '-');
669  1 command.addEdit(e);
670   
671  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
672  1 SequenceI prior = unwound.get(seq.getDatasetSequence());
673  1 assertEquals("ABCDEF", prior.getSequenceAsString());
674  1 assertEquals(8, prior.getStart());
675  1 assertEquals(13, prior.getEnd());
676    }
677   
678    /**
679    * Test that mimics 'remove all gaps' action. This generates delete gap edits
680    * for contiguous gaps in each sequence separately.
681    */
 
682  1 toggle @Test(groups = { "Functional" })
683    public void testPriorState_removeGapsMultipleSeqs()
684    {
685  1 EditCommand command = new EditCommand();
686  1 String original1 = "--ABC-DEF";
687  1 String original2 = "FG-HI--J";
688  1 String original3 = "M-NOPQ";
689   
690    /*
691    * Two edits for the first sequence
692    */
693  1 SequenceI seq = new Sequence("", "ABC-DEF");
694  1 SequenceI ds1 = new Sequence("", "ABCDEF");
695  1 seq.setDatasetSequence(ds1);
696  1 SequenceI[] sqs = new SequenceI[] { seq };
697  1 Edit e = command.new Edit(Action.DELETE_GAP, sqs, 0, 2, '-');
698  1 command.addEdit(e);
699  1 seq = new Sequence("", "ABCDEF");
700  1 seq.setDatasetSequence(ds1);
701  1 sqs = new SequenceI[] { seq };
702  1 e = command.new Edit(Action.DELETE_GAP, sqs, 3, 1, '-');
703  1 command.addEdit(e);
704   
705    /*
706    * Two edits for the second sequence
707    */
708  1 seq = new Sequence("", "FGHI--J");
709  1 SequenceI ds2 = new Sequence("", "FGHIJ");
710  1 seq.setDatasetSequence(ds2);
711  1 sqs = new SequenceI[] { seq };
712  1 e = command.new Edit(Action.DELETE_GAP, sqs, 2, 1, '-');
713  1 command.addEdit(e);
714  1 seq = new Sequence("", "FGHIJ");
715  1 seq.setDatasetSequence(ds2);
716  1 sqs = new SequenceI[] { seq };
717  1 e = command.new Edit(Action.DELETE_GAP, sqs, 4, 2, '-');
718  1 command.addEdit(e);
719   
720    /*
721    * One edit for the third sequence.
722    */
723  1 seq = new Sequence("", "MNOPQ");
724  1 SequenceI ds3 = new Sequence("", "MNOPQ");
725  1 seq.setDatasetSequence(ds3);
726  1 sqs = new SequenceI[] { seq };
727  1 e = command.new Edit(Action.DELETE_GAP, sqs, 1, 1, '-');
728  1 command.addEdit(e);
729   
730  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
731  1 assertEquals(original1, unwound.get(ds1).getSequenceAsString());
732  1 assertEquals(original2, unwound.get(ds2).getSequenceAsString());
733  1 assertEquals(original3, unwound.get(ds3).getSequenceAsString());
734    }
735   
736    /**
737    * Test that mimics 'remove all gapped columns' action. This generates a
738    * series Delete Gap edits that each act on all sequences that share a gapped
739    * column region.
740    */
 
741  1 toggle @Test(groups = { "Functional" })
742    public void testPriorState_removeGappedCols()
743    {
744  1 EditCommand command = new EditCommand();
745  1 String original1 = "--ABC--DEF";
746  1 String original2 = "-G-HI--J";
747  1 String original3 = "-M-NO--PQ";
748   
749    /*
750    * First edit deletes the first column.
751    */
752  1 SequenceI seq1 = new Sequence("", "-ABC--DEF");
753  1 SequenceI ds1 = new Sequence("", "ABCDEF");
754  1 seq1.setDatasetSequence(ds1);
755  1 SequenceI seq2 = new Sequence("", "G-HI--J");
756  1 SequenceI ds2 = new Sequence("", "GHIJ");
757  1 seq2.setDatasetSequence(ds2);
758  1 SequenceI seq3 = new Sequence("", "M-NO--PQ");
759  1 SequenceI ds3 = new Sequence("", "MNOPQ");
760  1 seq3.setDatasetSequence(ds3);
761  1 SequenceI[] sqs = new SequenceI[] { seq1, seq2, seq3 };
762  1 Edit e = command.new Edit(Action.DELETE_GAP, sqs, 0, 1, '-');
763  1 command.addEdit(e);
764   
765    /*
766    * Second edit deletes what is now columns 4 and 5.
767    */
768  1 seq1 = new Sequence("", "-ABCDEF");
769  1 seq1.setDatasetSequence(ds1);
770  1 seq2 = new Sequence("", "G-HIJ");
771  1 seq2.setDatasetSequence(ds2);
772  1 seq3 = new Sequence("", "M-NOPQ");
773  1 seq3.setDatasetSequence(ds3);
774  1 sqs = new SequenceI[] { seq1, seq2, seq3 };
775  1 e = command.new Edit(Action.DELETE_GAP, sqs, 4, 2, '-');
776  1 command.addEdit(e);
777   
778  1 Map<SequenceI, SequenceI> unwound = command.priorState(false);
779  1 assertEquals(original1, unwound.get(ds1).getSequenceAsString());
780  1 assertEquals(original2, unwound.get(ds2).getSequenceAsString());
781  1 assertEquals(original3, unwound.get(ds3).getSequenceAsString());
782  1 assertEquals(ds1, unwound.get(ds1).getDatasetSequence());
783  1 assertEquals(ds2, unwound.get(ds2).getDatasetSequence());
784  1 assertEquals(ds3, unwound.get(ds3).getDatasetSequence());
785    }
786   
787    /**
788    * Test a cut action's relocation of sequence features
789    */
 
790  1 toggle @Test(groups = { "Functional" })
791    public void testCut_withFeatures()
792    {
793    /*
794    * create sequence features before, after and overlapping
795    * a cut of columns/residues 4-7
796    */
797  1 SequenceI seq0 = seqs[0]; // abcdefghjk/1-10
798  1 seq0.addSequenceFeature(new SequenceFeature("before", "", 1, 3, 0f,
799    null));
800  1 seq0.addSequenceFeature(new SequenceFeature("overlap left", "", 2, 6,
801    0f, null));
802  1 seq0.addSequenceFeature(new SequenceFeature("internal", "", 5, 6, 0f,
803    null));
804  1 seq0.addSequenceFeature(new SequenceFeature("overlap right", "", 7, 8,
805    0f, null));
806  1 seq0.addSequenceFeature(new SequenceFeature("after", "", 8, 10, 0f,
807    null));
808   
809    /*
810    * add some contact features
811    */
812  1 SequenceFeature internalContact = new SequenceFeature("disulphide bond", "", 5,
813    6, 0f, null);
814  1 seq0.addSequenceFeature(internalContact); // should get deleted
815  1 SequenceFeature overlapLeftContact = new SequenceFeature(
816    "disulphide bond", "", 2, 6, 0f, null);
817  1 seq0.addSequenceFeature(overlapLeftContact); // should get deleted
818  1 SequenceFeature overlapRightContact = new SequenceFeature(
819    "disulphide bond", "", 5, 8, 0f, null);
820  1 seq0.addSequenceFeature(overlapRightContact); // should get deleted
821  1 SequenceFeature spanningContact = new SequenceFeature(
822    "disulphide bond", "", 2, 9, 0f, null);
823  1 seq0.addSequenceFeature(spanningContact); // should get shortened 3'
824   
825    /*
826    * cut columns 3-6 (base 0), residues d-g 4-7
827    */
828  1 Edit ec = testee.new Edit(Action.CUT, seqs, 3, 4, al); // cols 3-6 base 0
829  1 EditCommand.cut(ec, new AlignmentI[] { al });
830   
831  1 List<SequenceFeature> sfs = seq0.getSequenceFeatures();
832  1 SequenceFeatures.sortFeatures(sfs, true);
833   
834  1 assertEquals(5, sfs.size()); // features internal to cut were deleted
835  1 SequenceFeature sf = sfs.get(0);
836  1 assertEquals("before", sf.getType());
837  1 assertEquals(1, sf.getBegin());
838  1 assertEquals(3, sf.getEnd());
839  1 sf = sfs.get(1);
840  1 assertEquals("disulphide bond", sf.getType());
841  1 assertEquals(2, sf.getBegin());
842  1 assertEquals(5, sf.getEnd()); // truncated by cut
843  1 sf = sfs.get(2);
844  1 assertEquals("overlap left", sf.getType());
845  1 assertEquals(2, sf.getBegin());
846  1 assertEquals(3, sf.getEnd()); // truncated by cut
847  1 sf = sfs.get(3);
848  1 assertEquals("after", sf.getType());
849  1 assertEquals(4, sf.getBegin()); // shifted left by cut
850  1 assertEquals(6, sf.getEnd()); // shifted left by cut
851  1 sf = sfs.get(4);
852  1 assertEquals("overlap right", sf.getType());
853  1 assertEquals(4, sf.getBegin()); // shifted left by cut
854  1 assertEquals(4, sf.getEnd()); // truncated by cut
855    }
856   
857    /**
858    * Test a cut action's relocation of sequence features, with full coverage of
859    * all possible feature and cut locations for a 5-position ungapped sequence
860    */
 
861  1 toggle @Test(groups = { "Functional" })
862    public void testCut_withFeatures_exhaustive()
863    {
864    /*
865    * create a sequence features on each subrange of 1-5
866    */
867  1 SequenceI seq0 = new Sequence("seq", "ABCDE");
868  1 int start = 8;
869  1 int end = 12;
870  1 seq0.setStart(start);
871  1 seq0.setEnd(end);
872  1 AlignmentI alignment = new Alignment(new SequenceI[] { seq0 });
873  1 alignment.setDataset(null);
874   
875    /*
876    * create a new alignment with shared dataset sequence
877    */
878  1 AlignmentI copy = new Alignment(
879    new SequenceI[]
880    { alignment.getDataset().getSequenceAt(0).deriveSequence() });
881  1 SequenceI copySeq0 = copy.getSequenceAt(0);
882   
883  6 for (int from = start; from <= end; from++)
884    {
885  20 for (int to = from; to <= end; to++)
886    {
887  15 String desc = String.format("%d-%d", from, to);
888  15 SequenceFeature sf = new SequenceFeature("test", desc, from, to,
889    0f, null);
890  15 sf.setValue("from", Integer.valueOf(from));
891  15 sf.setValue("to", Integer.valueOf(to));
892  15 seq0.addSequenceFeature(sf);
893    }
894    }
895    // sanity check
896  1 List<SequenceFeature> sfs = seq0.getSequenceFeatures();
897  1 assertEquals(func(5), sfs.size());
898  1 assertEquals(sfs, copySeq0.getSequenceFeatures());
899  1 String copySequenceFeatures = copySeq0.getSequenceFeatures().toString();
900   
901    /*
902    * now perform all possible cuts of subranges of columns 1-5
903    * and validate the resulting remaining sequence features!
904    */
905  1 SequenceI[] sqs = new SequenceI[] { seq0 };
906   
907  6 for (int from = 0; from < seq0.getLength(); from++)
908    {
909  20 for (int to = from; to < seq0.getLength(); to++)
910    {
911  15 EditCommand ec = new EditCommand("Cut", Action.CUT, sqs, from, (to
912    - from + 1), alignment);
913  15 final String msg = String.format("Cut %d-%d ", from + 1, to + 1);
914  15 boolean newDatasetSequence = copySeq0.getDatasetSequence() != seq0
915    .getDatasetSequence();
916   
917  15 verifyCut(seq0, from, to, msg, start);
918   
919    /*
920    * verify copy alignment dataset sequence unaffected
921    */
922  15 assertEquals("Original dataset sequence was modified",
923    copySequenceFeatures,
924    copySeq0.getSequenceFeatures().toString());
925   
926    /*
927    * verify any new dataset sequence was added to the
928    * alignment dataset
929    */
930  15 assertEquals("Wrong Dataset size after " + msg,
931  15 newDatasetSequence ? 2 : 1,
932    alignment.getDataset().getHeight());
933   
934    /*
935    * undo and verify all restored
936    */
937  15 AlignmentI[] views = new AlignmentI[] { alignment };
938  15 ec.undoCommand(views);
939  15 sfs = seq0.getSequenceFeatures();
940  15 assertEquals("After undo of " + msg, func(5), sfs.size());
941  15 verifyUndo(from, to, sfs);
942   
943    /*
944    * verify copy alignment dataset sequence still unaffected
945    * and alignment dataset has shrunk (if it was added to)
946    */
947  15 assertEquals("Original dataset sequence was modified",
948    copySequenceFeatures,
949    copySeq0.getSequenceFeatures().toString());
950  15 assertEquals("Wrong Dataset size after Undo of " + msg, 1,
951    alignment.getDataset().getHeight());
952   
953    /*
954    * redo and verify
955    */
956  15 ec.doCommand(views);
957  15 verifyCut(seq0, from, to, msg, start);
958   
959    /*
960    * verify copy alignment dataset sequence unaffected
961    * and any new dataset sequence readded to alignment dataset
962    */
963  15 assertEquals("Original dataset sequence was modified",
964    copySequenceFeatures,
965    copySeq0.getSequenceFeatures().toString());
966  15 assertEquals("Wrong Dataset size after Redo of " + msg,
967  15 newDatasetSequence ? 2 : 1,
968    alignment.getDataset().getHeight());
969   
970    /*
971    * undo ready for next cut
972    */
973  15 ec.undoCommand(views);
974   
975    /*
976    * final verify that copy alignment dataset sequence is still unaffected
977    * and that alignment dataset has shrunk
978    */
979  15 assertEquals("Original dataset sequence was modified",
980    copySequenceFeatures,
981    copySeq0.getSequenceFeatures().toString());
982  15 assertEquals("Wrong Dataset size after final Undo of " + msg, 1,
983    alignment.getDataset().getHeight());
984    }
985    }
986    }
987   
988    /**
989    * Verify by inspection that the sequence features left on the sequence after
990    * a cut match the expected results. The trick to this is that we can parse
991    * each feature's original start-end positions from its description.
992    *
993    * @param seq0
994    * @param from
995    * @param to
996    * @param msg
997    * @param seqStart
998    */
 
999  30 toggle protected void verifyCut(SequenceI seq0, int from, int to,
1000    final String msg, int seqStart)
1001    {
1002  30 List<SequenceFeature> sfs;
1003  30 sfs = seq0.getSequenceFeatures();
1004   
1005  30 Collections.sort(sfs, BY_DESCRIPTION);
1006   
1007    /*
1008    * confirm the number of features has reduced by the
1009    * number of features within the cut region i.e. by
1010    * func(length of cut); exception is a cut at start or end of sequence,
1011    * which retains the original coordinates, dataset sequence
1012    * and all its features
1013    */
1014  30 boolean datasetRetained = from == 0 || to == 4;
1015  30 if (datasetRetained)
1016    {
1017    // dataset and all features retained
1018  18 assertEquals(msg, func(5), sfs.size());
1019    }
1020  12 else if (to - from == 4)
1021    {
1022    // all columns were cut
1023  0 assertTrue(sfs.isEmpty());
1024    }
1025    else
1026    {
1027    // failure in checkFeatureRelocation is more informative!
1028  12 assertEquals(msg + "wrong number of features left", func(5)
1029    - func(to - from + 1), sfs.size());
1030    }
1031   
1032    /*
1033    * inspect individual features
1034    */
1035  30 for (SequenceFeature sf : sfs)
1036    {
1037  420 verifyFeatureRelocation(sf, from + 1, to + 1, !datasetRetained,
1038    seqStart);
1039    }
1040    }
1041   
1042    /**
1043    * Check that after Undo, every feature has start/end that match its original
1044    * "start" and "end" properties
1045    *
1046    * @param from
1047    * @param to
1048    * @param sfs
1049    */
 
1050  15 toggle protected void verifyUndo(int from, int to, List<SequenceFeature> sfs)
1051    {
1052  15 for (SequenceFeature sf : sfs)
1053    {
1054  225 final int oldFrom = ((Integer) sf.getValue("from")).intValue();
1055  225 final int oldTo = ((Integer) sf.getValue("to")).intValue();
1056  225 String msg = String.format(
1057    "Undo cut of [%d-%d], feature at [%d-%d] ", from + 1, to + 1,
1058    oldFrom, oldTo);
1059  225 assertEquals(msg + "start", oldFrom, sf.getBegin());
1060  225 assertEquals(msg + "end", oldTo, sf.getEnd());
1061    }
1062    }
1063   
1064    /**
1065    * Helper method to check a feature has been correctly relocated after a cut
1066    *
1067    * @param sf
1068    * @param from
1069    * start of cut (first residue cut 1..)
1070    * @param to
1071    * end of cut (last residue cut 1..)
1072    * @param newDataset
1073    * @param seqStart
1074    */
 
1075  420 toggle private void verifyFeatureRelocation(SequenceFeature sf, int from, int to,
1076    boolean newDataset, int seqStart)
1077    {
1078    // TODO handle the gapped sequence case as well
1079  420 int cutSize = to - from + 1;
1080  420 final int oldFrom = ((Integer) sf.getValue("from")).intValue();
1081  420 final int oldTo = ((Integer) sf.getValue("to")).intValue();
1082  420 final int oldFromPosition = oldFrom - seqStart + 1; // 1..
1083  420 final int oldToPosition = oldTo - seqStart + 1; // 1..
1084   
1085  420 String msg = String.format(
1086    "Feature %s relocated to %d-%d after cut of %d-%d",
1087    sf.getDescription(), sf.getBegin(), sf.getEnd(), from, to);
1088  420 if (!newDataset)
1089    {
1090    // dataset retained with all features unchanged
1091  270 assertEquals("0: " + msg, oldFrom, sf.getBegin());
1092  270 assertEquals("0: " + msg, oldTo, sf.getEnd());
1093    }
1094  150 else if (oldToPosition < from)
1095    {
1096    // before cut region so unchanged
1097  30 assertEquals("1: " + msg, oldFrom, sf.getBegin());
1098  30 assertEquals("2: " + msg, oldTo, sf.getEnd());
1099    }
1100  120 else if (oldFromPosition > to)
1101    {
1102    // follows cut region - shift by size of cut
1103  30 assertEquals("3: " + msg, newDataset ? oldFrom - cutSize : oldFrom,
1104    sf.getBegin());
1105  30 assertEquals("4: " + msg, newDataset ? oldTo - cutSize : oldTo,
1106    sf.getEnd());
1107    }
1108  90 else if (oldFromPosition < from && oldToPosition > to)
1109    {
1110    // feature encloses cut region - shrink it right
1111  30 assertEquals("5: " + msg, oldFrom, sf.getBegin());
1112  30 assertEquals("6: " + msg, oldTo - cutSize, sf.getEnd());
1113    }
1114  60 else if (oldFromPosition < from)
1115    {
1116    // feature overlaps left side of cut region - truncated right
1117  30 assertEquals("7: " + msg, from - 1 + seqStart - 1, sf.getEnd());
1118    }
1119  30 else if (oldToPosition > to)
1120    {
1121    // feature overlaps right side of cut region - truncated left
1122  30 assertEquals("8: " + msg, newDataset ? from + seqStart - 1 : to + 1,
1123    sf.getBegin());
1124  30 assertEquals("9: " + msg, newDataset ? from + oldTo - to - 1 : oldTo,
1125    sf.getEnd());
1126    }
1127    else
1128    {
1129    // feature internal to cut - should have been deleted!
1130  0 Assert.fail(msg + " - should have been deleted");
1131    }
1132    }
1133   
1134    /**
1135    * Test a cut action's relocation of sequence features
1136    */
 
1137  1 toggle @Test(groups = { "Functional" })
1138    public void testCut_withFeatures5prime()
1139    {
1140  1 SequenceI seq0 = new Sequence("seq/8-11", "A-BCC");
1141  1 seq0.createDatasetSequence();
1142  1 assertEquals(8, seq0.getStart());
1143  1 seq0.addSequenceFeature(new SequenceFeature("", "", 10, 11, 0f,
1144    null));
1145  1 SequenceI[] seqsArray = new SequenceI[] { seq0 };
1146  1 AlignmentI alignment = new Alignment(seqsArray);
1147   
1148    /*
1149    * cut columns of A-B; same dataset sequence is retained, aligned sequence
1150    * start becomes 10
1151    */
1152  1 Edit ec = testee.new Edit(Action.CUT, seqsArray, 0, 3, alignment);
1153  1 EditCommand.cut(ec, new AlignmentI[] { alignment });
1154   
1155    /*
1156    * feature on CC(10-11) should still be on CC(10-11)
1157    */
1158  1 assertSame(seq0, alignment.getSequenceAt(0));
1159  1 assertEquals(10, seq0.getStart());
1160  1 List<SequenceFeature> sfs = seq0.getSequenceFeatures();
1161  1 assertEquals(1, sfs.size());
1162  1 SequenceFeature sf = sfs.get(0);
1163  1 assertEquals(10, sf.getBegin());
1164  1 assertEquals(11, sf.getEnd());
1165    }
1166    }