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