Bamboo test#12 adding flushEvents()
[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
25 import jalview.bin.Cache;
26 import jalview.bin.Jalview;
27 import jalview.datamodel.AlignmentI;
28 import jalview.io.DataSourceType;
29 import jalview.io.FileLoader;
30 import jalview.viewmodel.ViewportRanges;
31
32 import java.awt.Font;
33 import java.awt.FontMetrics;
34 import java.awt.Toolkit;
35
36 import org.testng.Assert;
37 import org.testng.annotations.BeforeClass;
38 import org.testng.annotations.Test;
39
40 import junit.extensions.PA;
41
42 @Test(singleThreaded = true)
43 public class SeqCanvasTest
44 {
45   @BeforeClass(alwaysRun = true)
46   public void setUp()
47   {
48     Thread.currentThread().setName("SeqCanvasTest Setup " + ++nTest);
49
50     Cache.initLogger();
51     Jalview.setSynchronous(true);
52   }
53
54   /**
55    * Test the method that computes wrapped width in residues, height of wrapped
56    * widths in pixels, and the number of widths visible
57    */
58   @Test(groups = "Functional")
59   public void testCalculateWrappedGeometry_noAnnotations()
60   {
61     Thread.currentThread().setName("SeqCanvasTest noAnn " + ++nTest);
62     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
63             "examples/uniref50.fa", DataSourceType.FILE);
64     AlignViewport av = af.getViewport();
65     AlignmentI al = av.getAlignment();
66     assertEquals(al.getWidth(), 157);
67     assertEquals(al.getHeight(), 15);
68     av.getRanges().setStartEndSeq(0, 14);
69
70     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
71
72     av.setWrapAlignment(true);
73     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
74     int charHeight = av.getCharHeight();
75     int charWidth = av.getCharWidth();
76     Assert.assertTrue(charHeight == 17 && charWidth == 12
77             || charHeight == 19 && charWidth == 11,
78             "char height/width " + charHeight + "/" + charWidth);
79     //
80     // assertEquals(charHeight, Platform.isMac() ? 17 : 19);
81     // assertEquals(charWidth, Platform.isMac() ? 12 : 11);
82
83     /*
84      * first with scales above, left, right
85      */
86     av.setShowAnnotation(false);
87     av.setScaleAboveWrapped(true);
88     av.setScaleLeftWrapped(true);
89     av.setScaleRightWrapped(true);
90     FontMetrics fm = testee.getFontMetrics(av.getFont());
91     int labelWidth = fm.stringWidth("000") + charWidth;
92     // BH 2020.03.22 It is not really necessary to be this detailed. Different
93     // OS-based UIs will
94     // always have slightly different parameters. StringgWidths are not
95     // necessarily linear sums of the letters involved.
96     // for example, the calculation for JavaScript is a float that has to be
97     // rounded.
98     // ..............................mac................PC................linux?
99     Assert.assertTrue(
100             labelWidth == 39 || labelWidth == 35 || labelWidth == 36);// 3 * 9 +
101                                                                       // charWidth
102                                                                       // ||
103                                                                       // labelWidth
104                                                                       // == 3 *
105                                                                       // 8 +
106                                                                       // charWidth,
107                                                                       // "labelWidth
108                                                                       // 36 or
109                                                                       // 39");
110
111     /*
112      * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
113      * take the whole multiple of character widths
114      */
115     int canvasWidth = 400;
116     int canvasHeight = 300;
117     int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
118     int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
119     assertEquals(wrappedWidth, residueColumns);
120     assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
121     assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
122     assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
123             2 * charHeight);
124     int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
125     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
126     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
127
128     /*
129      * repeat height is 17 * (2 + 15) = 289
130      * make canvas height 2 * 289 + 3 * charHeight so just enough to
131      * draw 2 widths and the first sequence of a third
132      */
133     canvasHeight = charHeight * (17 * 2 + 3);
134     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
135     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
136
137     /*
138      * reduce canvas height by 1 pixel 
139      * - should not be enough height to draw 3 widths
140      */
141     canvasHeight -= 1;
142     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
143     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
144
145     /*
146      * turn off scale above - can now fit in 2 and a bit widths
147      */
148     av.setScaleAboveWrapped(false);
149     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
150     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
151
152     /*
153      * reduce height to enough for 2 widths and not quite a third
154      * i.e. two repeating heights + spacer + sequence - 1 pixel
155      */
156     canvasHeight = charHeight * (16 * 2 + 2) - 1;
157     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
158     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
159
160     /*
161      * make canvas width enough for scales and 20 residues
162      */
163     canvasWidth = 2 * labelWidth + 20 * charWidth;
164     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
165             canvasHeight);
166     assertEquals(wrappedWidth, 20);
167
168     /*
169      * reduce width by 1 pixel - rounds down to 19 residues
170      */
171     canvasWidth -= 1;
172     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
173             canvasHeight);
174     assertEquals(wrappedWidth, 19);
175
176     /*
177      * turn off West scale - adds labelWidth (39) to available for residues
178      * which with the 11 remainder makes 50 which is 4 more charWidths rem 2
179      */
180     av.setScaleLeftWrapped(false);
181     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
182             canvasHeight);
183     assertEquals(wrappedWidth, 23);
184
185     /*
186      * add 10 pixels to width to fit in another whole residue column
187      */
188     canvasWidth += 9;
189     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
190             canvasHeight);
191     assertEquals(wrappedWidth, 23);
192     canvasWidth += 1;
193     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
194             canvasHeight);
195     assertEquals(wrappedWidth, 24);
196
197     /*
198      * turn off East scale to gain 39 more pixels (3 columns remainder 3)
199      */
200     av.setScaleRightWrapped(false);
201     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
202             canvasHeight);
203     assertEquals(wrappedWidth, 27);
204
205     /*
206      * add 9 pixels to width to gain a residue column
207      */
208     canvasWidth += 8;
209     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
210             canvasHeight);
211     assertEquals(wrappedWidth, 27); // 8px not enough
212     canvasWidth += 1;
213     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
214             canvasHeight);
215     assertEquals(wrappedWidth, 28); // 9px is enough
216
217     /*
218      * now West but not East scale - lose 39 pixels or 4 columns
219      */
220     av.setScaleLeftWrapped(true);
221     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
222             canvasHeight);
223     assertEquals(wrappedWidth, 24);
224
225     /*
226      * adding 3 pixels to width regains one column
227      */
228     canvasWidth += 2;
229     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
230             canvasHeight);
231     Assert.assertTrue(wrappedWidth == 24 || wrappedWidth == 25,
232             "WrappedWidth [" + wrappedWidth + "] should be 24 or 25"); // 2px
233                                                                        // not
234                                                                        // enough
235     canvasWidth += 1;
236     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
237             canvasHeight);
238     assertEquals(wrappedWidth, 25); // 3px is enough
239
240     /*
241      * turn off scales left and right, make width exactly 157 columns
242      */
243     av.setScaleLeftWrapped(false);
244     canvasWidth = al.getWidth() * charWidth;
245     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
246     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
247   }
248
249   /**
250    * Test the method that computes wrapped width in residues, height of wrapped
251    * widths in pixels, and the number of widths visible
252    */
253   @Test(groups = "Functional")
254   public void testCalculateWrappedGeometry_withAnnotations()
255   {
256     Thread.currentThread().setName("SeqCanvasTest wAnn " + ++nTest);
257     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
258             "examples/uniref50.fa", DataSourceType.FILE);
259     AlignViewport av = af.getViewport();
260     AlignmentI al = av.getAlignment();
261     assertEquals(al.getWidth(), 157);
262     assertEquals(al.getHeight(), 15);
263   
264     av.setWrapAlignment(true);
265     av.getRanges().setStartEndSeq(0, 14);
266     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
267     int charHeight = av.getCharHeight();
268     int charWidth = av.getCharWidth();
269
270     Assert.assertTrue(
271             charHeight == 17 && charWidth == 12
272                     || charHeight == 19 && charWidth == 11,
273             "char height/width " + charHeight + "/" + charWidth);
274     // assertEquals(charHeight, Platform.isMac() ? 17 : 19);
275     // assertEquals(charWidth, Platform.isMac() ? 12 : 11);
276
277     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
278   
279     /*
280      * first with scales above, left, right
281      */
282     av.setShowAnnotation(true);
283     av.setScaleAboveWrapped(true);
284     av.setScaleLeftWrapped(true);
285     av.setScaleRightWrapped(true);
286
287     FontMetrics fm = testee.getFontMetrics(av.getFont());
288     int labelWidth = fm.stringWidth("000") + charWidth;
289     // BH 2020.03.22 It is not really necessary to be this detailed. Different
290     // OS-based UIs will
291     // always have slightly different parameters. StringgWidths are not
292     // necessarily linear sums of the letters involved.
293     // for example, the calculation for JavaScript is a float that has to be
294     // rounded.
295     // ..............................mac................PC................linux?
296     Assert.assertTrue(
297             labelWidth == 39 || labelWidth == 35 || labelWidth == 36);// 3 * 9 +
298                                                                       // charWidth
299                                                                       // ||
300                                                                       // labelWidth
301                                                                       // == 3 *
302                                                                       // 8 +
303                                                                       // charWidth,
304                                                                       // "labelWidth
305                                                                       // 36 or
306                                                                       // 39");
307     // int labelWidth = fm.stringWidth("000") + charWidth;
308     // assertEquals(labelWidth,
309     // Platform.isMac() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
310
311     int annotationHeight = testee.getAnnotationHeight();
312
313     /*
314      * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
315      * take the whole multiple of character widths
316      */
317     int canvasWidth = 400;
318     int canvasHeight = 300;
319     int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
320     int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
321     assertEquals(wrappedWidth, residueColumns);
322     assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
323     assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
324     assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
325             2 * charHeight);
326     int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
327     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())
328             + SeqCanvas.SEQS_ANNOTATION_GAP + annotationHeight);
329     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
330   
331     /*
332      * repeat height is 17 * (2 + 15) = 289 + 3 + annotationHeight = 510
333      * make canvas height 2 of these plus 3 charHeights 
334      * so just enough to draw 2 widths, gap + scale + the first sequence of a third
335      */
336     canvasHeight = charHeight * (17 * 2 + 3)
337             + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP);
338     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
339     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
340   
341     /*
342      * reduce canvas height by 1 pixel - should not be enough height
343      * to draw 3 widths
344      */
345     canvasHeight -= 1;
346     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
347     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
348   
349     /*
350      * turn off scale above - can now fit in 2 and a bit widths
351      */
352     av.setScaleAboveWrapped(false);
353     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
354     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
355   
356     /*
357      * reduce height to enough for 2 widths and not quite a third
358      * i.e. two repeating heights + spacer + sequence - 1 pixel
359      */
360     canvasHeight = charHeight * (16 * 2 + 2)
361             + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP) - 1;
362     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
363     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
364
365     /*
366      * add 1 pixel to height - should now get 3 widths drawn
367      */
368     canvasHeight += 1;
369     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
370     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
371   }
372
373   private static int nTest = 0;
374   /**
375    * Test simulates loading an unwrapped alignment, shrinking it vertically so
376    * not all sequences are visible, then changing to wrapped mode. The ranges
377    * endSeq should be unchanged, but the vertical repeat height should include
378    * all sequences.
379    */
380   @Test(groups = "Functional")
381   public void testCalculateWrappedGeometry_fromScrolled()
382   {
383     flushEvents();
384     ViewportRanges.sTest = "";
385     Thread.currentThread().setName("SeqCanvasTest fromScrolled " + ++nTest);
386     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
387             "examples/uniref50.fa", DataSourceType.FILE);
388     AlignViewport av = af.getViewport();
389     AlignmentI al = av.getAlignment();
390     assertEquals(al.getWidth(), 157);
391     assertEquals(al.getHeight(), 15);
392     flushEvents();
393     av.getRanges().setStartEndSeq(0, 3);
394     String s = "";
395     s += " SC1 " + av.getRanges() + " " + ViewportRanges.sTest;
396     av.setShowAnnotation(false);
397     av.setScaleAboveWrapped(true);
398     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
399     av.setWrapAlignment(true);
400     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
401     int charHeight = av.getCharHeight();
402     int charWidth = av.getCharWidth();
403     // Windows h=19, w=11; Mac (and Linux?) 17,11
404     Assert.assertTrue(charHeight == 17 && charWidth == 12
405             || charHeight == 19 && charWidth == 11,
406             "char height/width " + charHeight + "/" + charWidth);
407     int canvasWidth = 400;
408     int canvasHeight = 300;
409     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
410     int repeatingHeight = (int) PA.getValue(testee,
411             "wrappedRepeatHeightPx");
412     assertEquals(av.getRanges().getEndSeq(), 0, "endSeq should be 3 " + s); // unchanged
413     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
414   }
415
416   private static void flushEvents()
417   {
418     ((sun.awt.SunToolkit) Toolkit.getDefaultToolkit()).flushPendingEvents();
419   }
420 }