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