JAL-3765 fix test
[jalview.git] / test / jalview / analysis / FinderTest.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.analysis;
22
23 import static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertSame;
25 import static org.testng.Assert.assertTrue;
26
27 import jalview.api.AlignViewportI;
28 import jalview.api.FinderI;
29 import jalview.bin.Cache;
30 import jalview.datamodel.Alignment;
31 import jalview.datamodel.AlignmentI;
32 import jalview.datamodel.ColumnSelection;
33 import jalview.datamodel.HiddenColumns;
34 import jalview.datamodel.SearchResultMatchI;
35 import jalview.datamodel.SearchResultsI;
36 import jalview.datamodel.Sequence;
37 import jalview.datamodel.SequenceGroup;
38 import jalview.gui.AlignFrame;
39 import jalview.gui.AlignViewport;
40 import jalview.gui.JvOptionPane;
41 import jalview.io.DataSourceType;
42 import jalview.io.FileLoader;
43
44 import java.util.List;
45
46 import org.testng.annotations.AfterMethod;
47 import org.testng.annotations.BeforeClass;
48 import org.testng.annotations.Test;
49
50 import junit.extensions.PA;
51
52 public class FinderTest
53 {
54   @BeforeClass(alwaysRun = true)
55   public void setUpJvOptionPane()
56   {
57     JvOptionPane.setInteractiveMode(false);
58     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
59   }
60
61   private AlignFrame af;
62
63   private AlignmentI al;
64
65   private AlignViewportI av;
66
67   @BeforeClass(groups = "Functional")
68   public void setUp()
69   {
70     Cache.loadProperties("test/jalview/io/testProps.jvprops");
71     Cache.applicationProperties.setProperty("PAD_GAPS",
72             Boolean.FALSE.toString());
73
74     //@formatter:off
75     String seqData = 
76         "seq1/8-18 ABCD--EF-GHIJI\n" + 
77         "seq2      A--BCDefHI\n" + 
78         "seq3      --bcdEFH\n" + 
79         "seq4      aa---aMMMMMaaa\n";
80     //@formatter:on
81     af = new FileLoader().LoadFileWaitTillLoaded(seqData,
82             DataSourceType.PASTE);
83     av = af.getViewport();
84     al = av.getAlignment();
85   }
86
87   @AfterMethod(alwaysRun = true)
88   public void tearDownAfterTest()
89   {
90     av.setSelectionGroup(null);
91   }
92
93   /**
94    * Test for find matches of a regular expression
95    */
96   @Test(groups = "Functional")
97   public void testFind_regex()
98   {
99     /*
100      * find next match only
101      */
102     Finder f = new Finder(av);
103     f.findNext("E.H", false, false, false); // 'E, any character, H'
104     // should match seq2 efH only
105     SearchResultsI sr = f.getSearchResults();
106     assertEquals(sr.getCount(), 1);
107     List<SearchResultMatchI> matches = sr.getResults();
108     assertSame(matches.get(0).getSequence(), al.getSequenceAt(1));
109     assertEquals(matches.get(0).getStart(), 5);
110     assertEquals(matches.get(0).getEnd(), 7);
111
112     f = new Finder(av);
113     f.findAll("E.H", false, false, false); // 'E, any character, H'
114     // should match seq2 efH and seq3 EFH
115     sr = f.getSearchResults();
116     assertEquals(sr.getCount(), 2);
117     matches = sr.getResults();
118     assertSame(matches.get(0).getSequence(), al.getSequenceAt(1));
119     assertSame(matches.get(1).getSequence(), al.getSequenceAt(2));
120     assertEquals(matches.get(0).getStart(), 5);
121     assertEquals(matches.get(0).getEnd(), 7);
122     assertEquals(matches.get(1).getStart(), 4);
123     assertEquals(matches.get(1).getEnd(), 6);
124   }
125
126   @Test(groups = "Functional")
127   public void testFind_findAll()
128   {
129     /*
130      * simple JAL-3765 test
131      * single symbol should find *all* matching symbols 
132      */
133     Finder f = new Finder(av);
134     f.findAll("M", false,false,false);
135     SearchResultsI sr = f.getSearchResults();
136     assertEquals(sr.getCount(),5);
137     
138   }
139   /**
140    * Test for (undocumented) find residue by position
141    */
142   @Test(groups = "Functional")
143   public void testFind_residueNumber()
144   {
145     Finder f = new Finder(av);
146
147     /*
148      * find first match should return seq1 residue 9
149      */
150     f.findNext("9", false, false, false);
151     SearchResultsI sr = f.getSearchResults();
152     assertEquals(sr.getCount(), 1);
153     List<SearchResultMatchI> matches = sr.getResults();
154     assertSame(matches.get(0).getSequence(), al.getSequenceAt(0));
155     assertEquals(matches.get(0).getStart(), 9);
156     assertEquals(matches.get(0).getEnd(), 9);
157
158     /*
159      * find all matches should return seq1 and seq4 (others are too short)
160      * (and not matches in sequence ids)
161      */
162     f = new Finder(av);
163     String name = al.getSequenceAt(0).getName();
164     al.getSequenceAt(0).setName("Q9XA0");
165     f.findAll("9", false, false, false);
166     sr = f.getSearchResults();
167     assertEquals(sr.getCount(), 2);
168     matches = sr.getResults();
169     assertSame(matches.get(0).getSequence(), al.getSequenceAt(0));
170     assertSame(matches.get(1).getSequence(), al.getSequenceAt(3));
171     assertEquals(matches.get(0).getStart(), 9);
172     assertEquals(matches.get(0).getEnd(), 9);
173     assertEquals(matches.get(1).getStart(), 9);
174     assertEquals(matches.get(1).getEnd(), 9);
175     al.getSequenceAt(0).setName(name);
176
177     /*
178      * parsing of search string as integer is strict
179      */
180     f = new Finder(av);
181     f.findNext(" 9", false, false, false);
182     assertTrue(f.getSearchResults().isEmpty());
183   }
184
185   /**
186    * Test for find next action
187    */
188   @Test(groups = "Functional")
189   public void testFindNext()
190   {
191     /*
192      * start at second sequence; residueIndex of -1
193      * means sequence id / description is searched
194      */
195     Finder f = new Finder(av);
196     PA.setValue(f, "sequenceIndex", 1);
197     PA.setValue(f, "residueIndex", -1);
198     f.findNext("e", false, false, false); // matches id
199
200     assertTrue(f.getSearchResults().isEmpty());
201     assertEquals(f.getIdMatches().size(), 1);
202     assertSame(f.getIdMatches().get(0), al.getSequenceAt(1));
203
204     // residueIndex is now 0 - for use in next find next
205     // searching A--BCDefHI
206     assertEquals(PA.getValue(f, "residueIndex"), 0);
207     f = new Finder(av);
208     PA.setValue(f, "sequenceIndex", 1);
209     PA.setValue(f, "residueIndex", 0);
210     f.findNext("e", false, false, false); // matches in sequence
211     assertTrue(f.getIdMatches().isEmpty());
212     assertEquals(f.getSearchResults().getCount(), 1);
213     List<SearchResultMatchI> matches = f.getSearchResults().getResults();
214     assertEquals(matches.get(0).getStart(), 5);
215     assertEquals(matches.get(0).getEnd(), 5);
216     assertSame(matches.get(0).getSequence(), al.getSequenceAt(1));
217     // still in the second sequence
218     assertEquals(PA.getValue(f, "sequenceIndex"), 1);
219     // next residue offset to search from is 5
220     assertEquals(PA.getValue(f, "residueIndex"), 5);
221
222     // find next from end of sequence - finds next sequence id
223     f = new Finder(av);
224     PA.setValue(f, "sequenceIndex", 1);
225     PA.setValue(f, "residueIndex", 7);
226     f.findNext("e", false, false, false);
227     assertEquals(f.getIdMatches().size(), 1);
228     assertSame(f.getIdMatches().get(0), al.getSequenceAt(2));
229     assertTrue(f.getSearchResults().isEmpty());
230   }
231
232   /**
233    * Test for matching within sequence descriptions
234    */
235   @Test(groups = "Functional")
236   public void testFind_inDescription()
237   {
238     AlignmentI al2 = new Alignment(al);
239     al2.getSequenceAt(0).setDescription("BRAF");
240     al2.getSequenceAt(1).setDescription("braf");
241
242     AlignViewportI av2 = new AlignViewport(al2);
243
244     /*
245      * find first match only
246      */
247     Finder f = new Finder(av2);
248     f.findNext("rAF", false, true, false);
249     assertEquals(f.getIdMatches().size(), 1);
250     assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0));
251     assertTrue(f.getSearchResults().isEmpty());
252
253     /*
254      * find all matches
255      */
256     f = new Finder(av2);
257     f.findAll("rAF", false, true, false);
258     assertEquals(f.getIdMatches().size(), 2);
259     assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0));
260     assertSame(f.getIdMatches().get(1), al2.getSequenceAt(1));
261     assertTrue(f.getSearchResults().isEmpty());
262
263     /*
264      * case sensitive
265      */
266     f = new Finder(av2);
267     f.findAll("RAF", true, true, false);
268     assertEquals(f.getIdMatches().size(), 1);
269     assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0));
270     assertTrue(f.getSearchResults().isEmpty());
271
272     /*
273      * match sequence id, description and sequence!
274      */
275     al2.getSequenceAt(0).setDescription("the efh sequence");
276     al2.getSequenceAt(0).setName("mouseEFHkinase");
277     al2.getSequenceAt(1).setName("humanEFHkinase");
278     f = new Finder(av2);
279
280     /*
281      * sequence matches should have no duplicates
282      */
283     f.findAll("EFH", false, true, false);
284     assertEquals(f.getIdMatches().size(), 2);
285     assertSame(f.getIdMatches().get(0), al2.getSequenceAt(0));
286     assertSame(f.getIdMatches().get(1), al2.getSequenceAt(1));
287
288     assertEquals(f.getSearchResults().getCount(), 2);
289     SearchResultMatchI match = f.getSearchResults().getResults().get(0);
290     assertSame(match.getSequence(), al2.getSequenceAt(1));
291     assertEquals(match.getStart(), 5);
292     assertEquals(match.getEnd(), 7);
293     match = f.getSearchResults().getResults().get(1);
294     assertSame(match.getSequence(), al2.getSequenceAt(2));
295     assertEquals(match.getStart(), 4);
296     assertEquals(match.getEnd(), 6);
297   }
298
299   /**
300    * Test for matching within sequence ids
301    */
302   @Test(groups = "Functional")
303   public void testFindAll_sequenceIds()
304   {
305     Finder f = new Finder(av);
306
307     /*
308      * case insensitive; seq1 occurs twice in sequence id but
309      * only one match should be returned
310      */
311     f.findAll("SEQ1", false, false, false);
312     assertEquals(f.getIdMatches().size(), 1);
313     assertSame(f.getIdMatches().get(0), al.getSequenceAt(0));
314     SearchResultsI searchResults = f.getSearchResults();
315     assertTrue(searchResults.isEmpty());
316
317     /*
318      * case sensitive
319      */
320     f = new Finder(av);
321     f.findAll("SEQ1", true, false, false);
322     searchResults = f.getSearchResults();
323     assertTrue(searchResults.isEmpty());
324
325     /*
326      * match both sequence id and sequence
327      */
328     AlignmentI al2 = new Alignment(al);
329     AlignViewportI av2 = new AlignViewport(al2);
330     al2.addSequence(new Sequence("aBz", "xyzabZpqrAbZ"));
331     f = new Finder(av2);
332     f.findAll("ABZ", false, false, false);
333     assertEquals(f.getIdMatches().size(), 1);
334     assertSame(f.getIdMatches().get(0), al2.getSequenceAt(4));
335     searchResults = f.getSearchResults();
336     assertEquals(searchResults.getCount(), 2);
337     SearchResultMatchI match = searchResults.getResults().get(0);
338     assertSame(match.getSequence(), al2.getSequenceAt(4));
339     assertEquals(match.getStart(), 4);
340     assertEquals(match.getEnd(), 6);
341     match = searchResults.getResults().get(1);
342     assertSame(match.getSequence(), al2.getSequenceAt(4));
343     assertEquals(match.getStart(), 10);
344     assertEquals(match.getEnd(), 12);
345   }
346
347   /**
348    * Test finding next match of a sequence pattern in an alignment
349    */
350   @Test(groups = "Functional")
351   public void testFind_findNext()
352   {
353     // "seq1/8-18 ABCD--EF-GHIJI\n" +
354     // "seq2 A--BCDefHI\n" +
355     // "seq3 --bcdEFH\n" +
356     // "seq4 aa---aMMMMMaaa\n";
357     /*
358      * efh should be matched in seq2 only
359      */
360     FinderI f = new Finder(av);
361     f.findNext("EfH", false, false, false);
362     SearchResultsI searchResults = f.getSearchResults();
363     assertEquals(searchResults.getCount(), 1);
364     SearchResultMatchI match = searchResults.getResults().get(0);
365     assertSame(match.getSequence(), al.getSequenceAt(1));
366     assertEquals(match.getStart(), 5);
367     assertEquals(match.getEnd(), 7);
368
369     /*
370      * I should be found in seq1 (twice) and seq2 (once)
371      */
372     f = new Finder(av);
373     f.findNext("I", false, false, false); // find next: seq1/16
374     searchResults = f.getSearchResults();
375     assertEquals(searchResults.getCount(), 1);
376     match = searchResults.getResults().get(0);
377     assertSame(match.getSequence(), al.getSequenceAt(0));
378     assertEquals(match.getStart(), 16);
379     assertEquals(match.getEnd(), 16);
380
381     f.findNext("I", false, false, false); // find next: seq1/18
382     searchResults = f.getSearchResults();
383     assertEquals(searchResults.getCount(), 1);
384     match = searchResults.getResults().get(0);
385     assertSame(match.getSequence(), al.getSequenceAt(0));
386     assertEquals(match.getStart(), 18);
387     assertEquals(match.getEnd(), 18);
388
389     f.findNext("I", false, false, false); // find next: seq2/8
390     searchResults = f.getSearchResults();
391     assertEquals(searchResults.getCount(), 1);
392     match = searchResults.getResults().get(0);
393     assertSame(match.getSequence(), al.getSequenceAt(1));
394     assertEquals(match.getStart(), 8);
395     assertEquals(match.getEnd(), 8);
396
397     f.findNext("I", false, false, false);
398     assertTrue(f.getSearchResults().isEmpty());
399
400     /*
401      * find should reset to start of alignment after a failed search
402      */
403     f.findNext("I", false, false, false); // find next: seq1/16
404     searchResults = f.getSearchResults();
405     assertEquals(searchResults.getCount(), 1);
406     match = searchResults.getResults().get(0);
407     assertSame(match.getSequence(), al.getSequenceAt(0));
408     assertEquals(match.getStart(), 16);
409     assertEquals(match.getEnd(), 16);
410   }
411
412   /**
413    * Test for JAL-2302 to verify that sub-matches are not included in a find all
414    * result
415    */
416   @Test(groups = "Functional")
417   public void testFindAll_maximalResultOnly()
418   {
419     Finder f = new Finder(av);
420     f.findAll("M+", false, false, false);
421     SearchResultsI searchResults = f.getSearchResults();
422     assertEquals(searchResults.getCount(), 1);
423     SearchResultMatchI match = searchResults.getResults().get(0);
424     assertSame(match.getSequence(), al.getSequenceAt(3));
425     assertEquals(match.getStart(), 4); // dataset sequence positions
426     assertEquals(match.getEnd(), 8); // base 1
427   }
428
429   /**
430    * Test finding all matches of a sequence pattern in an alignment
431    */
432   @Test(groups = "Functional")
433   public void testFindAll()
434   {
435     Finder f = new Finder(av);
436     f.findAll("EfH", false, false, false);
437     SearchResultsI searchResults = f.getSearchResults();
438     assertEquals(searchResults.getCount(), 2);
439     SearchResultMatchI match = searchResults.getResults().get(0);
440     assertSame(match.getSequence(), al.getSequenceAt(1));
441     assertEquals(match.getStart(), 5);
442     assertEquals(match.getEnd(), 7);
443     match = searchResults.getResults().get(1);
444     assertSame(match.getSequence(), al.getSequenceAt(2));
445     assertEquals(match.getStart(), 4);
446     assertEquals(match.getEnd(), 6);
447
448     /*
449      * find all I should find 2 positions in seq1, 1 in seq2
450      */
451     f.findAll("I", false, false, false);
452     searchResults = f.getSearchResults();
453     assertEquals(searchResults.getCount(), 3);
454     match = searchResults.getResults().get(0);
455     assertSame(match.getSequence(), al.getSequenceAt(0));
456     assertEquals(match.getStart(), 16);
457     assertEquals(match.getEnd(), 16);
458     match = searchResults.getResults().get(1);
459     assertSame(match.getSequence(), al.getSequenceAt(0));
460     assertEquals(match.getStart(), 18);
461     assertEquals(match.getEnd(), 18);
462     match = searchResults.getResults().get(2);
463     assertSame(match.getSequence(), al.getSequenceAt(1));
464     assertEquals(match.getStart(), 8);
465     assertEquals(match.getEnd(), 8);
466   }
467
468   /**
469    * Test finding all matches, case-sensitive
470    */
471   @Test(groups = "Functional")
472   public void testFindAll_caseSensitive()
473   {
474     Finder f = new Finder(av);
475
476     /*
477      * BC should match seq1/9-10 and seq2/2-3
478      */
479     f.findAll("BC", true, false, false);
480     SearchResultsI searchResults = f.getSearchResults();
481     assertEquals(searchResults.getCount(), 2);
482     SearchResultMatchI match = searchResults.getResults().get(0);
483     assertSame(match.getSequence(), al.getSequenceAt(0));
484     assertEquals(match.getStart(), 9);
485     assertEquals(match.getEnd(), 10);
486     match = searchResults.getResults().get(1);
487     assertSame(match.getSequence(), al.getSequenceAt(1));
488     assertEquals(match.getStart(), 2);
489     assertEquals(match.getEnd(), 3);
490
491     /*
492      * bc should match seq3/1-2
493      */
494     f = new Finder(av);
495     f.findAll("bc", true, false, false);
496     searchResults = f.getSearchResults();
497     assertEquals(searchResults.getCount(), 1);
498     match = searchResults.getResults().get(0);
499     assertSame(match.getSequence(), al.getSequenceAt(2));
500     assertEquals(match.getStart(), 1);
501     assertEquals(match.getEnd(), 2);
502
503     f.findAll("bC", true, false, false);
504     assertTrue(f.getSearchResults().isEmpty());
505   }
506
507   /**
508    * Test finding next match of a sequence pattern in a selection group
509    */
510   @Test(groups = "Functional")
511   public void testFindNext_inSelection()
512   {
513     /*
514      * select sequences 2 and 3, columns 4-6 which contains
515      * BCD
516      * cdE
517      */
518     SequenceGroup sg = new SequenceGroup();
519     sg.setStartRes(3);
520     sg.setEndRes(5);
521     sg.addSequence(al.getSequenceAt(1), false);
522     sg.addSequence(al.getSequenceAt(2), false);
523     av.setSelectionGroup(sg);
524
525     FinderI f = new Finder(av);
526     f.findNext("b", false, false, false);
527     assertTrue(f.getIdMatches().isEmpty());
528     SearchResultsI searchResults = f.getSearchResults();
529     assertEquals(searchResults.getCount(), 1);
530     SearchResultMatchI match = searchResults.getResults().get(0);
531     assertSame(match.getSequence(), al.getSequenceAt(1));
532     assertEquals(match.getStart(), 2);
533     assertEquals(match.getEnd(), 2);
534
535     /*
536      * a second Find should not return the 'b' in seq3 as outside the selection
537      */
538     f.findNext("b", false, false, false);
539     assertTrue(f.getSearchResults().isEmpty());
540     assertTrue(f.getIdMatches().isEmpty());
541
542     f = new Finder(av);
543     f.findNext("d", false, false, false);
544     assertTrue(f.getIdMatches().isEmpty());
545     searchResults = f.getSearchResults();
546     assertEquals(searchResults.getCount(), 1);
547     match = searchResults.getResults().get(0);
548     assertSame(match.getSequence(), al.getSequenceAt(1));
549     assertEquals(match.getStart(), 4);
550     assertEquals(match.getEnd(), 4);
551     f.findNext("d", false, false, false);
552     assertTrue(f.getIdMatches().isEmpty());
553     searchResults = f.getSearchResults();
554     assertEquals(searchResults.getCount(), 1);
555     match = searchResults.getResults().get(0);
556     assertSame(match.getSequence(), al.getSequenceAt(2));
557     assertEquals(match.getStart(), 3);
558     assertEquals(match.getEnd(), 3);
559   }
560
561   /**
562    * Test finding all matches of a search pattern in a selection group
563    */
564   @Test(groups = "Functional")
565   public void testFindAll_inSelection()
566   {
567     /*
568      * select sequences 2 and 3, columns 4-6 which contains
569      * BCD
570      * cdE
571      */
572     SequenceGroup sg = new SequenceGroup();
573     sg.setStartRes(3);
574     sg.setEndRes(5);
575     sg.addSequence(al.getSequenceAt(1), false);
576     sg.addSequence(al.getSequenceAt(2), false);
577     av.setSelectionGroup(sg);
578   
579     /*
580      * search for 'e' should match two sequence ids and one residue
581      */
582     Finder f = new Finder(av);
583     f.findAll("e", false, false, false);
584     assertEquals(f.getIdMatches().size(), 2);
585     assertSame(f.getIdMatches().get(0), al.getSequenceAt(1));
586     assertSame(f.getIdMatches().get(1), al.getSequenceAt(2));
587     SearchResultsI searchResults = f.getSearchResults();
588     assertEquals(searchResults.getCount(), 1);
589     SearchResultMatchI match = searchResults.getResults().get(0);
590     assertSame(match.getSequence(), al.getSequenceAt(2));
591     assertEquals(match.getStart(), 4);
592     assertEquals(match.getEnd(), 4);
593
594     /*
595      * search for 'Q' should match two sequence ids only
596      */
597     f = new Finder(av);
598     f.findAll("Q", false, false, false);
599     assertEquals(f.getIdMatches().size(), 2);
600     assertSame(f.getIdMatches().get(0), al.getSequenceAt(1));
601     assertSame(f.getIdMatches().get(1), al.getSequenceAt(2));
602     assertTrue(f.getSearchResults().isEmpty());
603   }
604
605   /**
606    * Test finding in selection with a sequence too short to reach it
607    */
608   @Test(groups = "Functional")
609   public void testFind_findAllInSelectionWithShortSequence()
610   {
611     /*
612      * select all sequences, columns 10-12
613      * BCD
614      * cdE
615      */
616     SequenceGroup sg = new SequenceGroup();
617     sg.setStartRes(9);
618     sg.setEndRes(11);
619     sg.addSequence(al.getSequenceAt(0), false);
620     sg.addSequence(al.getSequenceAt(1), false);
621     sg.addSequence(al.getSequenceAt(2), false);
622     sg.addSequence(al.getSequenceAt(3), false);
623     av.setSelectionGroup(sg);
624
625     /*
626      * search for 'I' should match two sequence positions
627      */
628     Finder f = new Finder(av);
629     f.findAll("I", false, false, false);
630     assertTrue(f.getIdMatches().isEmpty());
631     SearchResultsI searchResults = f.getSearchResults();
632     assertEquals(searchResults.getCount(), 2);
633     SearchResultMatchI match = searchResults.getResults().get(0);
634     assertSame(match.getSequence(), al.getSequenceAt(0));
635     assertEquals(match.getStart(), 16);
636     assertEquals(match.getEnd(), 16);
637     match = searchResults.getResults().get(1);
638     assertSame(match.getSequence(), al.getSequenceAt(1));
639     assertEquals(match.getStart(), 8);
640     assertEquals(match.getEnd(), 8);
641   }
642
643   /**
644    * Test that find does not report hidden positions, but does report matches that
645    * span hidden gaps
646    */
647   @Test(groups = "Functional")
648   public void testFind_withHiddenColumns()
649   {
650     /*
651      * 0    5   9
652      * ABCD--EF-GHI
653      * A--BCDefHI
654      * --bcdEFH
655      * aa---aMMMMMaaa
656      */
657
658     /*
659      * hide column 3 only, search for aaa
660      * should find two matches: aa-[-]-aa and trailing aaa
661      */
662     HiddenColumns hc = new HiddenColumns();
663     hc.hideColumns(3, 3);
664     al.setHiddenColumns(hc);
665     Finder f = new Finder(av);
666     f.findAll("aaa", false, false, false);
667     SearchResultsI searchResults = f.getSearchResults();
668     assertEquals(searchResults.getCount(), 2);
669     SearchResultMatchI match = searchResults.getResults().get(0);
670     assertSame(match.getSequence(), al.getSequenceAt(3));
671     assertEquals(match.getStart(), 1);
672     assertEquals(match.getEnd(), 3);
673     match = searchResults.getResults().get(1);
674     assertSame(match.getSequence(), al.getSequenceAt(3));
675     assertEquals(match.getStart(), 9);
676     assertEquals(match.getEnd(), 11);
677
678     /*
679      * hide 2-4 (CD- -BC bcd ---)
680      */
681     hc.hideColumns(2, 4);
682
683     /*
684      * find all search for D should ignore hidden positions in seq1 and seq3,
685      * find the visible D in seq2
686      */
687     f = new Finder(av);
688     f.findAll("D", false, false, false);
689     searchResults = f.getSearchResults();
690     assertEquals(searchResults.getCount(), 1);
691     match = searchResults.getResults().get(0);
692     assertSame(match.getSequence(), al.getSequenceAt(1));
693     assertEquals(match.getStart(), 4);
694     assertEquals(match.getEnd(), 4);
695
696     /*
697      * search for AD should fail although these are now
698      * consecutive in the visible columns
699      */
700     f = new Finder(av);
701     f.findAll("AD", false, false, false);
702     searchResults = f.getSearchResults();
703     assertTrue(searchResults.isEmpty());
704
705     /*
706      * find all 'aaa' should find both start and end of seq4
707      * (first run includes hidden gaps)
708      */
709     f = new Finder(av);
710     f.findAll("aaa", false, false, false);
711     searchResults = f.getSearchResults();
712     assertEquals(searchResults.getCount(), 2);
713     match = searchResults.getResults().get(0);
714     assertSame(match.getSequence(), al.getSequenceAt(3));
715     assertEquals(match.getStart(), 1);
716     assertEquals(match.getEnd(), 3);
717     match = searchResults.getResults().get(1);
718     assertSame(match.getSequence(), al.getSequenceAt(3));
719     assertEquals(match.getStart(), 9);
720     assertEquals(match.getEnd(), 11);
721
722     /*
723      * hide columns 2-5:
724      * find all 'aaa' should match twice in seq4
725      * (first match partly hidden, second all visible)
726      */
727     hc.hideColumns(2, 5);
728     f = new Finder(av);
729     f.findAll("aaa", false, false, false);
730     searchResults = f.getSearchResults();
731     assertEquals(searchResults.getCount(), 2);
732     match = searchResults.getResults().get(0);
733     assertSame(match.getSequence(), al.getSequenceAt(3));
734     assertEquals(match.getStart(), 1);
735     assertEquals(match.getEnd(), 3);
736     match = searchResults.getResults().get(1);
737     assertSame(match.getSequence(), al.getSequenceAt(3));
738     assertEquals(match.getStart(), 9);
739     assertEquals(match.getEnd(), 11);
740
741     /*
742      * find all 'BE' should not match across hidden columns in seq1
743      */
744     f.findAll("BE", false, false, false);
745     assertTrue(f.getSearchResults().isEmpty());
746
747     /*
748      * boundary case: hide columns at end of alignment
749      * search for H should match seq3/6 only
750      */
751     hc.revealAllHiddenColumns(new ColumnSelection());
752     hc.hideColumns(8, 13);
753     f = new Finder(av);
754     f.findNext("H", false, false, false);
755     searchResults = f.getSearchResults();
756     assertEquals(searchResults.getCount(), 1);
757     match = searchResults.getResults().get(0);
758     assertSame(match.getSequence(), al.getSequenceAt(2));
759     assertEquals(match.getStart(), 6);
760     assertEquals(match.getEnd(), 6);
761   }
762
763   @Test(groups = "Functional")
764   public void testFind_withHiddenColumnsAndSelection()
765   {
766     /*
767      * 0    5   9
768      * ABCD--EF-GHI
769      * A--BCDefHI
770      * --bcdEFH
771      * aa---aMMMMMaaa
772      */
773   
774     /*
775      * hide columns 2-4 and 6-7
776      */
777     HiddenColumns hc = new HiddenColumns();
778     hc.hideColumns(2, 4);
779     hc.hideColumns(6, 7);
780     al.setHiddenColumns(hc);
781   
782     /*
783      * select rows 2-3
784      */
785     SequenceGroup sg = new SequenceGroup();
786     sg.addSequence(al.getSequenceAt(1), false);
787     sg.addSequence(al.getSequenceAt(2), false);
788     sg.setStartRes(0);
789     sg.setEndRes(13);
790     av.setSelectionGroup(sg);
791
792     /*
793      * find all search for A or H
794      * should match seq2/1, seq2/7, not seq3/6
795      */
796     Finder f = new Finder(av);
797     f.findAll("[AH]", false, false, false);
798     SearchResultsI searchResults = f.getSearchResults();
799     assertEquals(searchResults.getCount(), 2);
800     SearchResultMatchI match = searchResults.getResults().get(0);
801     assertSame(match.getSequence(), al.getSequenceAt(1));
802     assertEquals(match.getStart(), 1);
803     assertEquals(match.getEnd(), 1);
804     match = searchResults.getResults().get(1);
805     assertSame(match.getSequence(), al.getSequenceAt(1));
806     assertEquals(match.getStart(), 7);
807     assertEquals(match.getEnd(), 7);
808   }
809
810   @Test(groups = "Functional")
811   public void testFind_ignoreHiddenColumns()
812   {
813     /*
814      * 0    5   9
815      * ABCD--EF-GHI
816      * A--BCDefHI
817      * --bcdEFH
818      * aa---aMMMMMaaa
819      */
820     HiddenColumns hc = new HiddenColumns();
821     hc.hideColumns(2, 4);
822     hc.hideColumns(7, 7);
823     al.setHiddenColumns(hc);
824
825     /*
826      * now have
827      * 015689
828      * AB-E-GHI
829      * A-DeHI
830      * --EF
831      * aaaMMMMaaa
832      */
833     Finder f = new Finder(av);
834     f.findAll("abe", false, false, true); // true = ignore hidden
835     SearchResultsI searchResults = f.getSearchResults();
836
837     /*
838      * match of seq1 ABE made up of AB and E
839      * note only one match is counted
840      */
841     assertEquals(searchResults.getCount(), 1);
842     assertEquals(searchResults.getResults().size(), 2);
843     SearchResultMatchI match = searchResults.getResults().get(0);
844     assertSame(match.getSequence(), al.getSequenceAt(0));
845     assertEquals(match.getStart(), 8); // A
846     assertEquals(match.getEnd(), 9); // B
847     match = searchResults.getResults().get(1);
848     assertSame(match.getSequence(), al.getSequenceAt(0));
849     assertEquals(match.getStart(), 12); // E
850     assertEquals(match.getEnd(), 12);
851
852     f = new Finder(av);
853     f.findNext("a.E", false, false, true);
854     searchResults = f.getSearchResults();
855     assertEquals(searchResults.getCount(), 1);
856     assertEquals(searchResults.getResults().size(), 2);
857     match = searchResults.getResults().get(0);
858     assertSame(match.getSequence(), al.getSequenceAt(0));
859     assertEquals(match.getStart(), 8); // A
860     assertEquals(match.getEnd(), 9); // B
861     match = searchResults.getResults().get(1);
862     assertSame(match.getSequence(), al.getSequenceAt(0));
863     assertEquals(match.getStart(), 12); // E
864     assertEquals(match.getEnd(), 12);
865
866     f.findNext("a.E", false, false, true);
867     searchResults = f.getSearchResults();
868     assertEquals(searchResults.getCount(), 1);
869     assertEquals(searchResults.getResults().size(), 2);
870     match = searchResults.getResults().get(0);
871     assertSame(match.getSequence(), al.getSequenceAt(1));
872     assertEquals(match.getStart(), 1); // a
873     assertEquals(match.getEnd(), 1);
874     match = searchResults.getResults().get(1);
875     assertSame(match.getSequence(), al.getSequenceAt(1));
876     assertEquals(match.getStart(), 4); // D
877     assertEquals(match.getEnd(), 5); // e
878
879     /*
880      * find all matching across two hidden column regions
881      * note one 'match' is returned as three contiguous matches
882      */
883     f.findAll("BEG", false, false, true);
884     searchResults = f.getSearchResults();
885     assertEquals(searchResults.getCount(), 1);
886     assertEquals(searchResults.getResults().size(), 3);
887     match = searchResults.getResults().get(0);
888     assertSame(match.getSequence(), al.getSequenceAt(0));
889     assertEquals(match.getStart(), 9); // B
890     assertEquals(match.getEnd(), 9);
891     match = searchResults.getResults().get(1);
892     assertSame(match.getSequence(), al.getSequenceAt(0));
893     assertEquals(match.getStart(), 12); // E
894     assertEquals(match.getEnd(), 12);
895     match = searchResults.getResults().get(2);
896     assertSame(match.getSequence(), al.getSequenceAt(0));
897     assertEquals(match.getStart(), 14); // G
898     assertEquals(match.getEnd(), 14);
899
900     /*
901      * now select columns 0-9 and search for A.*H
902      * this should match in the second sequence (split as 3 matches)
903      * but not the first (as H is outside the selection)
904      */
905     SequenceGroup selection = new SequenceGroup();
906     selection.setStartRes(0);
907     selection.setEndRes(9);
908     al.getSequences().forEach(seq -> selection.addSequence(seq, false));
909     av.setSelectionGroup(selection);
910     f.findAll("A.*H", false, false, true);
911     searchResults = f.getSearchResults();
912     assertEquals(searchResults.getCount(), 1);
913     assertEquals(searchResults.getResults().size(), 3);
914     // match made of contiguous matches A, DE, H
915     match = searchResults.getResults().get(0);
916     assertSame(match.getSequence(), al.getSequenceAt(1));
917     assertEquals(match.getStart(), 1); // A
918     assertEquals(match.getEnd(), 1);
919     match = searchResults.getResults().get(1);
920     assertSame(match.getSequence(), al.getSequenceAt(1));
921     assertEquals(match.getStart(), 4); // D
922     assertEquals(match.getEnd(), 5); // E
923     match = searchResults.getResults().get(2);
924     assertSame(match.getSequence(), al.getSequenceAt(1));
925     assertEquals(match.getStart(), 7); // H (there is no G)
926     assertEquals(match.getEnd(), 7);
927   }
928 }