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