Merge branch 'develop' into update_212_Dec_merge_with_21125_chamges
[jalview.git] / test / jalview / gui / SeqCanvasTest.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.gui;
22
23 import static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertFalse;
25 import static org.testng.Assert.assertNotNull;
26 import static org.testng.Assert.assertNull;
27 import static org.testng.Assert.assertTrue;
28
29 import java.awt.Font;
30 import java.awt.FontMetrics;
31
32 import org.testng.annotations.BeforeClass;
33 import org.testng.annotations.Test;
34
35 import jalview.bin.Cache;
36 import jalview.datamodel.AlignmentI;
37 import jalview.datamodel.SearchResults;
38 import jalview.datamodel.SearchResultsI;
39 import jalview.io.DataSourceType;
40 import jalview.io.FileLoader;
41
42 import jalview.util.Platform;
43 import jalview.viewmodel.ViewportRanges;
44 import junit.extensions.PA;
45
46 public class SeqCanvasTest
47 {
48   @BeforeClass(alwaysRun = true)
49   public void setUp()
50   {
51       // 2.11.2 - beforeMethod setup
52       //          Cache.loadProperties("test/jalview/io/testProps.jvprops");
53       //Cache.applicationProperties.setProperty("SHOW_IDENTITY",
54       //      Boolean.TRUE.toString());
55       //    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
56       //     DataSourceType.FILE);
57       //     /*
58       // * wait for Consensus thread to complete
59      // */
60       // do
61         // {
62         //try
63           //{
64           // Thread.sleep(50);
65         //} catch (InterruptedException x)
66           //{
67           //}
68       //    } while (af.getViewport().getCalcManager().isWorking());
69
70     Cache.loadProperties(null);
71     Desktop.getInstance().setVisible(false);
72   }
73
74   private AlignFrame af;
75
76   /**
77    * Test the method that computes wrapped width in residues, height of wrapped
78    * widths in pixels, and the number of widths visible
79    */
80   @Test(groups = "Functional")
81   public void testCalculateWrappedGeometry_noAnnotations()
82   {
83     AlignViewport av = af.getViewport();
84     AlignmentI al = av.getAlignment();
85     assertEquals(al.getWidth(), 157);
86     assertEquals(al.getHeight(), 15);
87     av.getRanges().setStartEndSeq(0, 14);
88
89     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
90
91     av.setWrapAlignment(true);
92     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
93     int charHeight = av.getCharHeight();
94     int charWidth = av.getCharWidth();
95     assertEquals(charHeight, !Platform.isWin() ? 17 : 19);
96     assertEquals(charWidth, !Platform.isWin() ? 12 : 11);
97
98     /*
99      * first with scales above, left, right
100      */
101     av.setShowAnnotation(false);
102     av.setScaleAboveWrapped(true);
103     av.setScaleLeftWrapped(true);
104     av.setScaleRightWrapped(true);
105     FontMetrics fm = testee.getFontMetrics(av.getFont());
106     int labelWidth = fm.stringWidth("000") + charWidth;
107     assertEquals(labelWidth,
108             !Platform.isWin() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
109
110     /*
111      * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
112      * take the whole multiple of character widths
113      */
114     int canvasWidth = 400;
115     int canvasHeight = 300;
116     int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
117     int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
118     assertEquals(wrappedWidth, residueColumns);
119     assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
120     assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
121     assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
122             2 * charHeight);
123     int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
124     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
125     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
126
127     /*
128      * repeat height is 17 * (2 + 15) = 289
129      * make canvas height 2 * 289 + 3 * charHeight so just enough to
130      * draw 2 widths and the first sequence of a third
131      */
132     canvasHeight = charHeight * (17 * 2 + 3);
133     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
134     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
135
136     /*
137      * reduce canvas height by 1 pixel 
138      * - should not be enough height to draw 3 widths
139      */
140     canvasHeight -= 1;
141     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
142     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
143
144     /*
145      * turn off scale above - can now fit in 2 and a bit widths
146      */
147     av.setScaleAboveWrapped(false);
148     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
149     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
150
151     /*
152      * reduce height to enough for 2 widths and not quite a third
153      * i.e. two repeating heights + spacer + sequence - 1 pixel
154      */
155     canvasHeight = charHeight * (16 * 2 + 2) - 1;
156     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
157     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
158
159     /*
160      * make canvas width enough for scales and 20 residues
161      */
162     canvasWidth = 2 * labelWidth + 20 * charWidth;
163     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
164             canvasHeight);
165     assertEquals(wrappedWidth, 20);
166
167     /*
168      * reduce width by 1 pixel - rounds down to 19 residues
169      */
170     canvasWidth -= 1;
171     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
172             canvasHeight);
173     assertEquals(wrappedWidth, 19);
174
175     /*
176      * turn off West scale - adds labelWidth (39) to available for residues
177      * which with the 11 remainder makes 50 which is 4 more charWidths rem 2
178      */
179     av.setScaleLeftWrapped(false);
180     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
181             canvasHeight);
182     assertEquals(wrappedWidth, 23);
183
184     /*
185      * add 10 pixels to width to fit in another whole residue column
186      */
187     canvasWidth += 9;
188     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
189             canvasHeight);
190     assertEquals(wrappedWidth, 23);
191     canvasWidth += 1;
192     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
193             canvasHeight);
194     assertEquals(wrappedWidth, 24);
195
196     /*
197      * turn off East scale to gain 39 more pixels (3 columns remainder 3)
198      */
199     av.setScaleRightWrapped(false);
200     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
201             canvasHeight);
202     assertEquals(wrappedWidth, 27);
203
204     /*
205      * add 9 pixels to width to gain a residue column
206      */
207     canvasWidth += 8;
208     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
209             canvasHeight);
210     assertEquals(wrappedWidth, 27); // 8px not enough
211     canvasWidth += 1;
212     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
213             canvasHeight);
214     assertEquals(wrappedWidth, 28); // 9px is enough
215
216     /*
217      * now West but not East scale - lose 39 pixels or 4 columns
218      */
219     av.setScaleLeftWrapped(true);
220     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
221             canvasHeight);
222     assertEquals(wrappedWidth, 24);
223
224     /*
225      * adding 3 pixels to width regains one column
226      */
227     canvasWidth += 2;
228     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
229             canvasHeight);
230     assertEquals(wrappedWidth, !Platform.isWin() ? 24 : 25); // 2px not enough
231     canvasWidth += 1;
232     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
233             canvasHeight);
234     assertEquals(wrappedWidth, 25); // 3px is enough
235
236     /*
237      * turn off scales left and right, make width exactly 157 columns
238      */
239     av.setScaleLeftWrapped(false);
240     canvasWidth = al.getWidth() * charWidth;
241     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
242     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
243   }
244
245   /**
246    * Test the method that computes wrapped width in residues, height of wrapped
247    * widths in pixels, and the number of widths visible
248    */
249   @Test(groups = "Functional")
250   public void testCalculateWrappedGeometry_withAnnotations()
251   {
252     AlignViewport av = af.getViewport();
253     AlignmentI al = av.getAlignment();
254     assertEquals(al.getWidth(), 157);
255     assertEquals(al.getHeight(), 15);
256   
257     av.setWrapAlignment(true);
258     av.getRanges().setStartEndSeq(0, 14);
259     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
260     int charHeight = av.getCharHeight();
261     int charWidth = av.getCharWidth();
262     assertEquals(charHeight, !Platform.isWin() ? 17 : 19);
263     assertEquals(charWidth, !Platform.isWin() ? 12 : 11);
264     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
265   
266     /*
267      * first with scales above, left, right
268      */
269     av.setShowAnnotation(true);
270     av.setScaleAboveWrapped(true);
271     av.setScaleLeftWrapped(true);
272     av.setScaleRightWrapped(true);
273     FontMetrics fm = testee.getFontMetrics(av.getFont());
274     int labelWidth = fm.stringWidth("000") + charWidth;
275     assertEquals(labelWidth,
276             !Platform.isWin() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
277     int annotationHeight = testee.getAnnotationHeight();
278
279     /*
280      * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
281      * take the whole multiple of character widths
282      */
283     int canvasWidth = 400;
284     int canvasHeight = 300;
285     int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
286     int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
287     assertEquals(wrappedWidth, residueColumns);
288     assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
289     assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
290     assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
291             2 * charHeight);
292     int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
293     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())
294             + SeqCanvas.SEQS_ANNOTATION_GAP + annotationHeight);
295     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
296   
297     /*
298      * repeat height is 17 * (2 + 15) = 289 + 3 + annotationHeight = 510
299      * make canvas height 2 of these plus 3 charHeights 
300      * so just enough to draw 2 widths, gap + scale + the first sequence of a third
301      */
302     canvasHeight = charHeight * (17 * 2 + 3)
303             + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP);
304     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
305     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
306   
307     /*
308      * reduce canvas height by 1 pixel - should not be enough height
309      * to draw 3 widths
310      */
311     canvasHeight -= 1;
312     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
313     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
314   
315     /*
316      * turn off scale above - can now fit in 2 and a bit widths
317      */
318     av.setScaleAboveWrapped(false);
319     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
320     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
321   
322     /*
323      * reduce height to enough for 2 widths and not quite a third
324      * i.e. two repeating heights + spacer + sequence - 1 pixel
325      */
326     canvasHeight = charHeight * (16 * 2 + 2)
327             + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP) - 1;
328     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
329     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
330
331     /*
332      * add 1 pixel to height - should now get 3 widths drawn
333      */
334     canvasHeight += 1;
335     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
336     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
337   }
338
339   /**
340    * Test simulates loading an unwrapped alignment, shrinking it vertically so
341    * not all sequences are visible, then changing to wrapped mode. The ranges
342    * endSeq should be unchanged, but the vertical repeat height should include
343    * all sequences.
344    */
345   @Test(groups = "Functional_Failing")
346   public void testCalculateWrappedGeometry_fromScrolled()
347   {
348     AlignViewport av = af.getViewport();
349     AlignmentI al = av.getAlignment();
350     assertEquals(al.getWidth(), 157);
351     assertEquals(al.getHeight(), 15);
352     av.getRanges().setStartEndSeq(0, 3);
353     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
354     av.setWrapAlignment(true);
355     av.setShowAnnotation(false);
356     av.setScaleAboveWrapped(true);
357
358     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
359
360     int charHeight = av.getCharHeight();
361     int charWidth = av.getCharWidth();
362     assertEquals(charHeight, 17);
363     assertEquals(charWidth, 12);
364
365     int canvasWidth = 400;
366     int canvasHeight = 300;
367     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
368
369     assertEquals(av.getRanges().getEndSeq(), 3); // unchanged
370     int repeatingHeight = (int) PA.getValue(testee,
371             "wrappedRepeatHeightPx");
372     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
373   }
374
375   @Test(groups = "Functional")
376   public void testClear_HighlightAndSelection()
377   {
378     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
379             "examples/uniref50.fa", DataSourceType.FILE);
380     AlignViewport av = af.getViewport();
381     SearchResultsI highlight = new SearchResults();
382     highlight.addResult(
383             av.getAlignment().getSequenceAt(1).getDatasetSequence(), 50,
384             80);
385     af.alignPanel.highlightSearchResults(highlight);
386     af.avc.markHighlightedColumns(false, false, false);
387     assertNotNull(av.getSearchResults(),
388             "No highlight was created on alignment");
389     assertFalse(av.getColumnSelection().isEmpty(),
390             "No selection was created from highlight");
391     af.deselectAllSequenceMenuItem_actionPerformed(null);
392     assertTrue(av.getColumnSelection().isEmpty(),
393             "No Selection should be present after deselecting all.");
394     assertNull(av.getSearchResults(),
395             "No higlighted search results should be present after deselecting all.");
396   }
397 }