JAL-3206 avoid divide by zero when threshold is graphMin / graphMax
[jalview.git] / test / jalview / schemes / AnnotationColourGradientTest.java
1 package jalview.schemes;
2
3 import static org.testng.Assert.assertEquals;
4
5 import jalview.datamodel.Alignment;
6 import jalview.datamodel.AlignmentAnnotation;
7 import jalview.datamodel.AlignmentI;
8 import jalview.datamodel.Annotation;
9 import jalview.datamodel.GraphLine;
10 import jalview.datamodel.Sequence;
11 import jalview.datamodel.SequenceI;
12
13 import java.awt.Color;
14
15 import org.testng.annotations.BeforeClass;
16 import org.testng.annotations.Test;
17
18 public class AnnotationColourGradientTest
19 {
20   final static int WIDTH = 11;
21
22   final static int THRESHOLD_FIVE = 5;
23
24   private AlignmentAnnotation ann;
25
26   private SequenceI seq;
27
28   private AlignmentI al;
29
30   Color minColour = new Color(50, 200, 150);
31
32   Color maxColour = new Color(150, 100, 250);
33
34   /**
35    * Setup creates an annotation over 11 columns with values 0-10 and threshold
36    * 5
37    */
38   @BeforeClass(alwaysRun = true)
39   public void setUp()
40   {
41     Annotation[] anns = new Annotation[WIDTH];
42     /*
43      * set annotations with values 0-10, graded colours
44      */
45     for (int col = 0; col < WIDTH; col++)
46     {
47       int hue = col * 20;
48       Color colour = new Color(hue, hue, hue);
49       anns[col] = new Annotation("a", "a", 'a', col, colour);
50     }
51
52     seq = new Sequence("Seq", "");
53     al = new Alignment(new SequenceI[]{ seq});
54     
55     /*
56      * AlignmentAnnotation constructor works out min-max range
57      */
58     ann = new AlignmentAnnotation("", "", anns);
59     ann.setThreshold(new GraphLine(THRESHOLD_FIVE, "", Color.RED));
60     seq.addAlignmentAnnotation(ann);
61   }
62
63   @Test(groups = "Functional")
64   public void testShadeCalculation_noThreshold()
65   {
66     AnnotationColourGradient testee = new AnnotationColourGradient(ann,
67             minColour, maxColour, AnnotationColourGradient.NO_THRESHOLD);
68     for (int col = 0; col < WIDTH; col++)
69     {
70       Color result = testee.shadeCalculation(ann, col);
71       /*
72        * column <n> is n/10 of the way from minCol to maxCol
73        */
74       Color expected = new Color(50 + 10 * col, 200 - 10 * col,
75               150 + 10 * col);
76       assertEquals(result, expected, "for column " + col);
77     }
78   }
79
80   /**
81    * Test the 'colour above threshold' case
82    */
83   @Test(groups = "Functional")
84   public void testShadeCalculation_aboveThreshold()
85   {
86     AnnotationColourGradient testee = new AnnotationColourGradient(ann,
87             minColour, maxColour, AnnotationColourGradient.ABOVE_THRESHOLD);
88     for (int col = 0; col < WIDTH; col++)
89     {
90       Color result = testee.shadeCalculation(ann, col);
91       /*
92        * colour is derived regardless of the threshold value 
93        * (the renderer will suppress colouring if above/below threshold)
94        */
95       Color expected = new Color(50 + 10 * col, 200 - 10 * col,
96               150 + 10 * col);
97       assertEquals(result, expected, "for column " + col);
98     }
99
100     /*
101      * now make 6-10 the span of the colour range
102      * (annotation value == column number in this test)
103      */
104     testee.setThresholdIsMinMax(true);
105     for (int col = 0; col < THRESHOLD_FIVE; col++)
106     {
107       /*
108        * colours below the threshold are computed as before
109        */
110       Color expected = new Color(50 + 10 * col, 200 - 10 * col,
111               150 + 10 * col);
112       Color result = testee.shadeCalculation(ann, col);
113       assertEquals(result, expected, "for column " + col);
114     }
115     for (int col = THRESHOLD_FIVE; col < WIDTH; col++)
116     {
117       /*
118        * colours for values >= threshold are graduated
119        * range is 6-10 so steps of 100/5 = 20
120        */
121       int factor = col - THRESHOLD_FIVE;
122       Color expected = new Color(50 + 20 * factor, 200 - 20 * factor,
123               150 + 20 * factor);
124       Color result = testee.shadeCalculation(ann, col);
125       assertEquals(result, expected, "for column " + col);
126     }
127
128     /*
129      * test for boundary case threshold == graphMax (JAL-3206)
130      */
131     float thresh = ann.threshold.value;
132     ann.threshold.value = ann.graphMax;
133     Color result = testee.shadeCalculation(ann, WIDTH - 1);
134     assertEquals(result, maxColour);
135     testee.setThresholdIsMinMax(false);
136     result = testee.shadeCalculation(ann, WIDTH - 1);
137     assertEquals(result, maxColour);
138     ann.threshold.value = thresh; // reset
139   }
140
141   /**
142    * Test the 'colour below threshold' case
143    */
144   @Test(groups = "Functional")
145   public void testShadeCalculation_belowThreshold()
146   {
147     AnnotationColourGradient testee = new AnnotationColourGradient(ann,
148             minColour, maxColour, AnnotationColourGradient.BELOW_THRESHOLD);
149
150     for (int col = 0; col < WIDTH; col++)
151     {
152       Color result = testee.shadeCalculation(ann, col);
153       /*
154        * colour is derived regardless of the threshold value 
155        * (the renderer will suppress colouring if above/below threshold)
156        */
157       Color expected = new Color(50 + 10 * col, 200 - 10 * col,
158               150 + 10 * col);
159       assertEquals(result, expected, "for column " + col);
160     }
161
162     /*
163      * now make 0-5 the span of the colour range
164      * (annotation value == column number in this test)
165      */
166     testee.setThresholdIsMinMax(true);
167     for (int col = THRESHOLD_FIVE + 1; col < WIDTH; col++)
168     {
169       /*
170        * colours above the threshold are computed as before
171        */
172       Color expected = new Color(50 + 10 * col, 200 - 10 * col,
173               150 + 10 * col);
174       Color result = testee.shadeCalculation(ann, col);
175       assertEquals(result, expected, "for column " + col);
176     }
177
178     for (int col = 0; col <= THRESHOLD_FIVE; col++)
179     {
180       /*
181        * colours for values <= threshold are graduated
182        * range is 0-5 so steps of 100/5 = 20
183        */
184       Color expected = new Color(50 + 20 * col, 200 - 20 * col,
185               150 + 20 * col);
186       Color result = testee.shadeCalculation(ann, col);
187       assertEquals(result, expected, "for column " + col);
188     }
189
190     /*
191      * test for boundary case threshold == graphMin (JAL-3206)
192      */
193     float thresh = ann.threshold.value;
194     ann.threshold.value = ann.graphMin;
195     Color result = testee.shadeCalculation(ann, 0);
196     assertEquals(result, minColour);
197     testee.setThresholdIsMinMax(false);
198     result = testee.shadeCalculation(ann, 0);
199     assertEquals(result, minColour);
200     ann.threshold.value = thresh; // reset
201   }
202
203   /**
204    * Test the 'colour above threshold' case
205    */
206   @Test(groups = "Functional")
207   public void testFindColour_aboveThreshold()
208   {
209     AnnotationColourGradient testee = new AnnotationColourGradient(ann,
210             minColour, maxColour, AnnotationColourGradient.ABOVE_THRESHOLD);
211     testee = (AnnotationColourGradient) testee.getInstance(al, null);
212
213     for (int col = 0; col < WIDTH; col++)
214     {
215       Color result = testee.findColour('a', col, seq);
216       /*
217        * expect white below threshold of 5
218        */
219       Color expected = col < 5 ? Color.white : new Color(50 + 10 * col,
220               200 - 10 * col,
221               150 + 10 * col);
222       assertEquals(result, expected, "for column " + col);
223     }
224   
225     /*
226      * now make 6-10 the span of the colour range
227      * (annotation value == column number in this test)
228      */
229     testee.setThresholdIsMinMax(true);
230     for (int col = 0; col < WIDTH; col++)
231     {
232       /*
233        * colours for values >= threshold are graduated
234        * range is 6-10 so steps of 100/5 = 20
235        */
236       int factor = col - THRESHOLD_FIVE;
237       Color expected = col < 5 ? Color.white : new Color(50 + 20 * factor,
238               200 - 20 * factor,
239               150 + 20 * factor);
240       Color result = testee.findColour('a', col, seq);
241       assertEquals(result, expected, "for column " + col);
242     }
243   }
244
245   /**
246    * Test the 'colour below threshold' case
247    */
248   @Test(groups = "Functional")
249   public void testFindColour_belowThreshold()
250   {
251     AnnotationColourGradient testee = new AnnotationColourGradient(ann,
252             minColour, maxColour, AnnotationColourGradient.BELOW_THRESHOLD);
253     testee = (AnnotationColourGradient) testee.getInstance(al, null);
254   
255     for (int col = 0; col < WIDTH; col++)
256     {
257       Color result = testee.findColour('a', col, seq);
258       Color expected = col > 5 ? Color.white : new Color(50 + 10 * col,
259               200 - 10 * col, 150 + 10 * col);
260       assertEquals(result, expected, "for column " + col);
261     }
262   
263     /*
264      * now make 0-5 the span of the colour range
265      * (annotation value == column number in this test)
266      */
267     testee.setThresholdIsMinMax(true);
268     for (int col = 0; col < WIDTH; col++)
269     {
270       /*
271        * colours for values <= threshold are graduated
272        * range is 0-5 so steps of 100/5 = 20
273        */
274       Color expected = col > 5 ? Color.white : new Color(50 + 20 * col,
275               200 - 20 * col, 150 + 20 * col);
276       Color result = testee.findColour('a', col, seq);
277       assertEquals(result, expected, "for column " + col);
278     }
279   }
280
281   @Test(groups = "Functional")
282   public void testFindColour_noThreshold()
283   {
284     AnnotationColourGradient testee = new AnnotationColourGradient(ann,
285             minColour, maxColour, AnnotationColourGradient.NO_THRESHOLD);
286     testee = (AnnotationColourGradient) testee.getInstance(al, null);
287
288     for (int col = 0; col < WIDTH; col++)
289     {
290       Color result = testee.findColour('a', col, seq);
291       /*
292        * column <n> is n/10 of the way from minCol to maxCol
293        */
294       Color expected = new Color(50 + 10 * col, 200 - 10 * col,
295               150 + 10 * col);
296       assertEquals(result, expected, "for column " + col);
297     }
298   }
299
300   @Test(groups = "Functional")
301   public void testFindColour_originalColours()
302   {
303     AnnotationColourGradient testee = new AnnotationColourGradient(ann,
304             minColour, maxColour, AnnotationColourGradient.NO_THRESHOLD);
305     testee = (AnnotationColourGradient) testee.getInstance(al, null);
306
307     /*
308      * flag corresponding to 'use original colours' checkbox
309      * - just use the individual annotation colours
310      */
311     testee.setPredefinedColours(true);
312
313     /*
314      * the annotation colour is returned, except for column 0 where it is
315      * black - in this case the colour scheme colour overrides it
316      */
317     for (int col = 0; col < WIDTH; col++)
318     {
319       int hue = col * 20;
320       Color c = col == 0 ? minColour : new Color(hue, hue, hue);
321       assertEquals(testee.findColour('a', col, seq), c, "for column " + col);
322     }
323   }
324 }