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