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