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