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