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