Merge branch 'bug/JAL-2791exportFilteredFeature' into merge/JAL-2791
[jalview.git] / test / jalview / schemes / FeatureColourTest.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.schemes;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertFalse;
25 import static org.testng.AssertJUnit.assertNull;
26 import static org.testng.AssertJUnit.assertTrue;
27 import static org.testng.AssertJUnit.fail;
28 import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
29
30 import jalview.api.FeatureColourI;
31 import jalview.datamodel.SequenceFeature;
32 import jalview.gui.JvOptionPane;
33 import jalview.util.ColorUtils;
34 import jalview.util.Format;
35
36 import java.awt.Color;
37
38 import org.testng.annotations.BeforeClass;
39 import org.testng.annotations.Test;
40
41 import junit.extensions.PA;
42
43 public class FeatureColourTest
44 {
45
46   @BeforeClass(alwaysRun = true)
47   public void setUpJvOptionPane()
48   {
49     JvOptionPane.setInteractiveMode(false);
50     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
51   }
52
53   @Test(groups = { "Functional" })
54   public void testCopyConstructor()
55   {
56     /*
57      * plain colour
58      */
59     FeatureColour fc = new FeatureColour(Color.RED);
60     FeatureColour fc1 = new FeatureColour(fc);
61     assertTrue(fc1.getColour().equals(Color.RED));
62     assertFalse(fc1.isGraduatedColour());
63     assertFalse(fc1.isColourByLabel());
64     assertFalse(fc1.isColourByAttribute());
65     assertNull(fc1.getAttributeName());
66
67     /*
68      * min-max colour
69      */
70     fc = new FeatureColour(Color.gray, Color.black, 10f, 20f);
71     fc.setAboveThreshold(true);
72     fc.setThreshold(12f);
73     fc1 = new FeatureColour(fc);
74     assertTrue(fc1.isGraduatedColour());
75     assertFalse(fc1.isColourByLabel());
76     assertTrue(fc1.isAboveThreshold());
77     assertFalse(fc1.isColourByAttribute());
78     assertNull(fc1.getAttributeName());
79     assertEquals(12f, fc1.getThreshold());
80     assertEquals(Color.gray, fc1.getMinColour());
81     assertEquals(Color.black, fc1.getMaxColour());
82     assertEquals(Color.gray, fc1.getNoColour());
83     assertEquals(10f, fc1.getMin());
84     assertEquals(20f, fc1.getMax());
85
86     /*
87      * min-max-noValue colour
88      */
89     fc = new FeatureColour(Color.gray, Color.black, Color.green, 10f, 20f);
90     fc.setAboveThreshold(true);
91     fc.setThreshold(12f);
92     fc1 = new FeatureColour(fc);
93     assertTrue(fc1.isGraduatedColour());
94     assertFalse(fc1.isColourByLabel());
95     assertFalse(fc1.isColourByAttribute());
96     assertNull(fc1.getAttributeName());
97     assertTrue(fc1.isAboveThreshold());
98     assertEquals(12f, fc1.getThreshold());
99     assertEquals(Color.gray, fc1.getMinColour());
100     assertEquals(Color.black, fc1.getMaxColour());
101     assertEquals(Color.green, fc1.getNoColour());
102     assertEquals(10f, fc1.getMin());
103     assertEquals(20f, fc1.getMax());
104
105     /*
106      * colour by label
107      */
108     fc = new FeatureColour();
109     fc.setColourByLabel(true);
110     fc1 = new FeatureColour(fc);
111     assertTrue(fc1.isColourByLabel());
112     assertFalse(fc1.isGraduatedColour());
113     assertFalse(fc1.isColourByAttribute());
114     assertNull(fc1.getAttributeName());
115
116     /*
117      * colour by attribute (label)
118      */
119     fc = new FeatureColour();
120     fc.setColourByLabel(true);
121     fc.setAttributeName("AF");
122     fc1 = new FeatureColour(fc);
123     assertTrue(fc1.isColourByLabel());
124     assertFalse(fc1.isGraduatedColour());
125     assertTrue(fc1.isColourByAttribute());
126     assertArrayEquals(new String[] { "AF" }, fc1.getAttributeName());
127
128     /*
129      * colour by attribute (value)
130      */
131     fc = new FeatureColour(Color.gray, Color.black, Color.green, 10f, 20f);
132     fc.setAboveThreshold(true);
133     fc.setThreshold(12f);
134     fc.setAttributeName("AF");
135     fc1 = new FeatureColour(fc);
136     assertTrue(fc1.isGraduatedColour());
137     assertFalse(fc1.isColourByLabel());
138     assertTrue(fc1.isColourByAttribute());
139     assertArrayEquals(new String[] { "AF" }, fc1.getAttributeName());
140     assertTrue(fc1.isAboveThreshold());
141     assertEquals(12f, fc1.getThreshold());
142     assertEquals(Color.gray, fc1.getMinColour());
143     assertEquals(Color.black, fc1.getMaxColour());
144     assertEquals(Color.green, fc1.getNoColour());
145     assertEquals(10f, fc1.getMin());
146     assertEquals(20f, fc1.getMax());
147   }
148
149   @Test(groups = { "Functional" })
150   public void testCopyConstructor_minMax()
151   {
152     /*
153      * graduated colour
154      */
155     FeatureColour fc = new FeatureColour(Color.BLUE, Color.RED, 1f, 5f);
156     assertTrue(fc.isGraduatedColour());
157     assertFalse(fc.isColourByLabel());
158     assertFalse(fc.isColourByAttribute());
159     assertNull(fc.getAttributeName());
160     assertEquals(1f, fc.getMin());
161     assertEquals(5f, fc.getMax());
162
163     /*
164      * update min-max bounds
165      */
166     FeatureColour fc1 = new FeatureColour(fc, 2f, 6f);
167     assertTrue(fc1.isGraduatedColour());
168     assertFalse(fc1.isColourByLabel());
169     assertFalse(fc1.isColourByAttribute());
170     assertNull(fc1.getAttributeName());
171     assertEquals(2f, fc1.getMin());
172     assertEquals(6f, fc1.getMax());
173     assertFalse((boolean) PA.getValue(fc1, "isHighToLow"));
174
175     /*
176      * update min-max bounds - high to low
177      */
178     fc1 = new FeatureColour(fc, 23f, 16f);
179     assertTrue(fc1.isGraduatedColour());
180     assertFalse(fc1.isColourByLabel());
181     assertFalse(fc1.isColourByAttribute());
182     assertNull(fc1.getAttributeName());
183     assertEquals(23f, fc1.getMin());
184     assertEquals(16f, fc1.getMax());
185     assertTrue((boolean) PA.getValue(fc1, "isHighToLow"));
186
187     /*
188      * graduated colour by attribute
189      */
190     fc1.setAttributeName("AF");
191     fc1 = new FeatureColour(fc1, 13f, 36f);
192     assertTrue(fc1.isGraduatedColour());
193     assertFalse(fc1.isColourByLabel());
194     assertTrue(fc1.isColourByAttribute());
195     assertArrayEquals(new String[] { "AF" }, fc1.getAttributeName());
196     assertEquals(13f, fc1.getMin());
197     assertEquals(36f, fc1.getMax());
198     assertFalse((boolean) PA.getValue(fc1, "isHighToLow"));
199
200     /*
201      * colour by label
202      */
203     fc = new FeatureColour(Color.BLUE, Color.RED, 1f, 5f);
204     fc.setColourByLabel(true);
205     assertFalse(fc.isGraduatedColour());
206     assertTrue(fc.isColourByLabel());
207     assertFalse(fc.isColourByAttribute());
208     assertNull(fc.getAttributeName());
209     assertEquals(1f, fc.getMin());
210     assertEquals(5f, fc.getMax());
211
212     /*
213      * update min-max bounds
214      */
215     fc1 = new FeatureColour(fc, 2f, 6f);
216     assertFalse(fc1.isGraduatedColour());
217     assertTrue(fc1.isColourByLabel());
218     assertFalse(fc1.isColourByAttribute());
219     assertNull(fc1.getAttributeName());
220     assertEquals(2f, fc1.getMin());
221     assertEquals(6f, fc1.getMax());
222
223     /*
224      * colour by attribute text
225      */
226     fc1.setAttributeName("AC");
227     fc1 = new FeatureColour(fc1, 13f, 36f);
228     assertFalse(fc1.isGraduatedColour());
229     assertTrue(fc1.isColourByLabel());
230     assertTrue(fc1.isColourByAttribute());
231     assertArrayEquals(new String[] { "AC" }, fc1.getAttributeName());
232     assertEquals(13f, fc1.getMin());
233     assertEquals(36f, fc1.getMax());
234   }
235
236   @Test(groups = { "Functional" })
237   public void testGetColor_simpleColour()
238   {
239     FeatureColour fc = new FeatureColour(Color.RED);
240     assertEquals(Color.RED,
241             fc.getColor(new SequenceFeature("Cath", "", 1, 2, 0f, null)));
242   }
243
244   @Test(groups = { "Functional" })
245   public void testGetColor_colourByLabel()
246   {
247     FeatureColour fc = new FeatureColour();
248     fc.setColourByLabel(true);
249     SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 1f,
250             null);
251     Color expected = ColorUtils.createColourFromName("desc");
252     assertEquals(expected, fc.getColor(sf));
253   }
254
255   @Test(groups = { "Functional" })
256   public void testGetColor_Graduated()
257   {
258     /*
259      * graduated colour from 
260      * score 0 to 100
261      * gray(128, 128, 128) to red(255, 0, 0)
262      */
263     FeatureColour fc = new FeatureColour(Color.GRAY, Color.RED, 0f, 100f);
264     // feature score is 75 which is 3/4 of the way from GRAY to RED
265     SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 75f,
266             null);
267     // the colour gradient is computed in float values from 0-1 (where 1 == 255)
268     float red = 128 / 255f + 3 / 4f * (255 - 128) / 255f;
269     float green = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
270     float blue = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
271     Color expected = new Color(red, green, blue);
272     assertEquals(expected, fc.getColor(sf));
273   }
274
275   @Test(groups = { "Functional" })
276   public void testGetColor_aboveBelowThreshold()
277   {
278     // gradient from [50, 150] from WHITE(255, 255, 255) to BLACK(0, 0, 0)
279     FeatureColour fc = new FeatureColour(Color.WHITE, Color.BLACK, 50f,
280             150f);
281     SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 70f,
282             null);
283
284     /*
285      * feature with score of Float.NaN is always assigned minimum colour
286      */
287     SequenceFeature sf2 = new SequenceFeature("type", "desc", 0, 20,
288             Float.NaN, null);
289
290     fc.setThreshold(100f); // ignore for now
291     assertEquals(new Color(204, 204, 204), fc.getColor(sf));
292     assertEquals(Color.white, fc.getColor(sf2));
293
294     fc.setAboveThreshold(true); // feature lies below threshold
295     assertNull(fc.getColor(sf));
296     assertEquals(Color.white, fc.getColor(sf2));
297
298     fc.setBelowThreshold(true);
299     fc.setThreshold(70f);
300     assertNull(fc.getColor(sf)); // feature score == threshold - hidden
301     assertEquals(Color.white, fc.getColor(sf2));
302     fc.setThreshold(69f);
303     assertNull(fc.getColor(sf)); // feature score > threshold - hidden
304     assertEquals(Color.white, fc.getColor(sf2));
305   }
306
307   /**
308    * Test output of feature colours to Jalview features file format
309    */
310   @Test(groups = { "Functional" })
311   public void testToJalviewFormat()
312   {
313     /*
314      * plain colour - to RGB hex code
315      */
316     FeatureColour fc = new FeatureColour(Color.RED);
317     String redHex = Format.getHexString(Color.RED);
318     String hexColour = redHex;
319     assertEquals("domain\t" + hexColour, fc.toJalviewFormat("domain"));
320
321     /*
322      * colour by label (no threshold)
323      */
324     fc = new FeatureColour();
325     fc.setColourByLabel(true);
326     assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
327
328     /*
329      * colour by attribute text (no threshold)
330      */
331     fc = new FeatureColour();
332     fc.setColourByLabel(true);
333     fc.setAttributeName("CLIN_SIG");
334     assertEquals("domain\tattribute|CLIN_SIG", fc.toJalviewFormat("domain"));
335     
336     /*
337      * colour by label (autoscaled) (an odd state you can reach by selecting
338      * 'above threshold', then deselecting 'threshold is min/max' then 'colour
339      * by label')
340      */
341     fc.setAttributeName((String[]) null);
342     fc.setAutoScaled(true);
343     assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
344
345     /*
346      * colour by label (above threshold) 
347      */
348     fc.setAutoScaled(false);
349     fc.setThreshold(12.5f);
350     fc.setAboveThreshold(true);
351     // min/max values are output though not used by this scheme
352     assertEquals("domain\tlabel|||0.0|0.0|above|12.5",
353             fc.toJalviewFormat("domain"));
354
355     /*
356      * colour by label (below threshold)
357      */
358     fc.setBelowThreshold(true);
359     assertEquals("domain\tlabel|||0.0|0.0|below|12.5",
360             fc.toJalviewFormat("domain"));
361
362     /*
363      * colour by attributes text (below threshold)
364      */
365     fc.setBelowThreshold(true);
366     fc.setAttributeName("CSQ", "Consequence");
367     assertEquals("domain\tattribute|CSQ:Consequence|||0.0|0.0|below|12.5",
368             fc.toJalviewFormat("domain"));
369
370     /*
371      * graduated colour by score, no threshold
372      * - default constructor sets noValueColor = minColor
373      */
374     fc = new FeatureColour(Color.GREEN, Color.RED, 12f, 25f);
375     String greenHex = Format.getHexString(Color.GREEN);
376     String expected = String.format(
377             "domain\tscore|%s|%s|noValueMin|abso|12.0|25.0|none", greenHex,
378             redHex);
379     assertEquals(expected, fc.toJalviewFormat("domain"));
380
381     /*
382      * graduated colour by score, no threshold, no value gets min colour
383      */
384     fc = new FeatureColour(Color.GREEN, Color.RED, Color.GREEN, 12f, 25f);
385     expected = String.format(
386             "domain\tscore|%s|%s|noValueMin|abso|12.0|25.0|none", greenHex,
387             redHex);
388     assertEquals(expected, fc.toJalviewFormat("domain"));
389
390     /*
391      * graduated colour by score, no threshold, no value gets max colour
392      */
393     fc = new FeatureColour(Color.GREEN, Color.RED, Color.RED, 12f, 25f);
394     expected = String.format(
395             "domain\tscore|%s|%s|noValueMax|abso|12.0|25.0|none", greenHex,
396             redHex);
397     assertEquals(expected, fc.toJalviewFormat("domain"));
398     
399     /*
400      * colour ranges over the actual score ranges (not min/max)
401      */
402     fc.setAutoScaled(true);
403     expected = String.format(
404             "domain\tscore|%s|%s|noValueMax|12.0|25.0|none", greenHex,
405             redHex);
406     assertEquals(expected, fc.toJalviewFormat("domain"));
407
408     /*
409      * graduated colour by score, below threshold
410      */
411     fc.setThreshold(12.5f);
412     fc.setBelowThreshold(true);
413     expected = String.format(
414             "domain\tscore|%s|%s|noValueMax|12.0|25.0|below|12.5",
415             greenHex, redHex);
416     assertEquals(expected, fc.toJalviewFormat("domain"));
417
418     /*
419      * graduated colour by score, above threshold
420      */
421     fc.setThreshold(12.5f);
422     fc.setAboveThreshold(true);
423     fc.setAutoScaled(false);
424     expected = String.format(
425             "domain\tscore|%s|%s|noValueMax|abso|12.0|25.0|above|12.5",
426             greenHex, redHex);
427     assertEquals(expected, fc.toJalviewFormat("domain"));
428
429     /*
430      * graduated colour by attribute, above threshold
431      */
432     fc.setAttributeName("CSQ", "AF");
433     fc.setAboveThreshold(true);
434     fc.setAutoScaled(false);
435     expected = String.format(
436             "domain\tattribute|CSQ:AF|%s|%s|noValueMax|abso|12.0|25.0|above|12.5",
437             greenHex, redHex);
438     assertEquals(expected, fc.toJalviewFormat("domain"));
439   }
440
441   /**
442    * Test parsing of feature colours from Jalview features file format
443    */
444   @Test(groups = { "Functional" })
445   public void testParseJalviewFeatureColour()
446   {
447     /*
448      * simple colour by name
449      */
450     FeatureColourI fc = FeatureColour.parseJalviewFeatureColour("red");
451     assertTrue(fc.isSimpleColour());
452     assertEquals(Color.RED, fc.getColour());
453
454     /*
455      * simple colour by hex code
456      */
457     fc = FeatureColour.parseJalviewFeatureColour(Format
458             .getHexString(Color.RED));
459     assertTrue(fc.isSimpleColour());
460     assertEquals(Color.RED, fc.getColour());
461
462     /*
463      * simple colour by rgb triplet
464      */
465     fc = FeatureColour.parseJalviewFeatureColour("255,0,0");
466     assertTrue(fc.isSimpleColour());
467     assertEquals(Color.RED, fc.getColour());
468
469     /*
470      * malformed colour
471      */
472     try
473     {
474       fc = FeatureColour.parseJalviewFeatureColour("oops");
475       fail("expected exception");
476     } catch (IllegalArgumentException e)
477     {
478       assertEquals("Invalid colour descriptor: oops", e.getMessage());
479     }
480
481     /*
482      * colour by label (no threshold)
483      */
484     fc = FeatureColour.parseJalviewFeatureColour("label");
485     assertTrue(fc.isColourByLabel());
486     assertFalse(fc.hasThreshold());
487
488     /*
489      * colour by label (with threshold)
490      */
491     fc = FeatureColour
492             .parseJalviewFeatureColour("label|||0.0|0.0|above|12.0");
493     assertTrue(fc.isColourByLabel());
494     assertTrue(fc.isAboveThreshold());
495     assertEquals(12.0f, fc.getThreshold());
496
497     /*
498      * colour by attribute text (no threshold)
499      */
500     fc = FeatureColour.parseJalviewFeatureColour("attribute|CLIN_SIG");
501     assertTrue(fc.isColourByAttribute());
502     assertTrue(fc.isColourByLabel());
503     assertFalse(fc.hasThreshold());
504     assertArrayEquals(new String[] { "CLIN_SIG" }, fc.getAttributeName());
505
506     /*
507      * colour by attributes text (with score threshold)
508      */
509     fc = FeatureColour.parseJalviewFeatureColour(
510             "attribute|CSQ:Consequence|||0.0|0.0|above|12.0");
511     assertTrue(fc.isColourByLabel());
512     assertTrue(fc.isColourByAttribute());
513     assertArrayEquals(new String[] { "CSQ", "Consequence" },
514             fc.getAttributeName());
515     assertTrue(fc.isAboveThreshold());
516     assertEquals(12.0f, fc.getThreshold());
517
518     /*
519      * graduated colour by score (with colour names) (no threshold)
520      */
521     fc = FeatureColour.parseJalviewFeatureColour("red|green|10.0|20.0");
522     assertTrue(fc.isGraduatedColour());
523     assertFalse(fc.hasThreshold());
524     assertEquals(Color.RED, fc.getMinColour());
525     assertEquals(Color.GREEN, fc.getMaxColour());
526     assertEquals(Color.RED, fc.getNoColour());
527     assertEquals(10f, fc.getMin());
528     assertEquals(20f, fc.getMax());
529     assertTrue(fc.isAutoScaled());
530
531     /*
532      * the same, with 'no value colour' specified as max
533      */
534     fc = FeatureColour
535             .parseJalviewFeatureColour("red|green|novaluemax|10.0|20.0");
536     assertEquals(Color.RED, fc.getMinColour());
537     assertEquals(Color.GREEN, fc.getMaxColour());
538     assertEquals(Color.GREEN, fc.getNoColour());
539     assertEquals(10f, fc.getMin());
540     assertEquals(20f, fc.getMax());
541
542     /*
543      * the same, with 'no value colour' specified as min
544      */
545     fc = FeatureColour
546             .parseJalviewFeatureColour("red|green|novalueMin|10.0|20.0");
547     assertEquals(Color.RED, fc.getMinColour());
548     assertEquals(Color.GREEN, fc.getMaxColour());
549     assertEquals(Color.RED, fc.getNoColour());
550     assertEquals(10f, fc.getMin());
551     assertEquals(20f, fc.getMax());
552
553     /*
554      * the same, with 'no value colour' specified as none
555      */
556     fc = FeatureColour
557             .parseJalviewFeatureColour("red|green|novaluenone|10.0|20.0");
558     assertEquals(Color.RED, fc.getMinColour());
559     assertEquals(Color.GREEN, fc.getMaxColour());
560     assertNull(fc.getNoColour());
561     assertEquals(10f, fc.getMin());
562     assertEquals(20f, fc.getMax());
563
564     /*
565      * the same, with invalid 'no value colour'
566      */
567     try
568     {
569       fc = FeatureColour
570               .parseJalviewFeatureColour("red|green|blue|10.0|20.0");
571       fail("expected exception");
572     } catch (IllegalArgumentException e)
573     {
574       assertEquals(
575               "Couldn't parse the minimum value for graduated colour ('blue')",
576               e.getMessage());
577     }
578
579     /*
580      * graduated colour (explicitly by 'score') (no threshold)
581      */
582     fc = FeatureColour
583             .parseJalviewFeatureColour("Score|red|green|10.0|20.0");
584     assertTrue(fc.isGraduatedColour());
585     assertFalse(fc.hasThreshold());
586     assertEquals(Color.RED, fc.getMinColour());
587     assertEquals(Color.GREEN, fc.getMaxColour());
588     assertEquals(10f, fc.getMin());
589     assertEquals(20f, fc.getMax());
590     assertTrue(fc.isAutoScaled());
591
592     /*
593      * graduated colour by attribute (no threshold)
594      */
595     fc = FeatureColour
596             .parseJalviewFeatureColour("attribute|AF|red|green|10.0|20.0");
597     assertTrue(fc.isGraduatedColour());
598     assertTrue(fc.isColourByAttribute());
599     assertArrayEquals(new String[] { "AF" }, fc.getAttributeName());
600     assertFalse(fc.hasThreshold());
601     assertEquals(Color.RED, fc.getMinColour());
602     assertEquals(Color.GREEN, fc.getMaxColour());
603     assertEquals(10f, fc.getMin());
604     assertEquals(20f, fc.getMax());
605     assertTrue(fc.isAutoScaled());
606
607     /*
608      * graduated colour by score (colours by hex code) (above threshold)
609      */
610     String descriptor = String.format("%s|%s|10.0|20.0|above|15",
611             Format.getHexString(Color.RED),
612             Format.getHexString(Color.GREEN));
613     fc = FeatureColour.parseJalviewFeatureColour(descriptor);
614     assertTrue(fc.isGraduatedColour());
615     assertTrue(fc.hasThreshold());
616     assertTrue(fc.isAboveThreshold());
617     assertEquals(15f, fc.getThreshold());
618     assertEquals(Color.RED, fc.getMinColour());
619     assertEquals(Color.GREEN, fc.getMaxColour());
620     assertEquals(10f, fc.getMin());
621     assertEquals(20f, fc.getMax());
622     assertTrue(fc.isAutoScaled());
623
624     /*
625      * graduated colour by attributes (below threshold)
626      */
627     fc = FeatureColour.parseJalviewFeatureColour(
628             "attribute|CSQ:AF|red|green|10.0|20.0|below|13");
629     assertTrue(fc.isGraduatedColour());
630     assertTrue(fc.isColourByAttribute());
631     assertArrayEquals(new String[] { "CSQ", "AF" }, fc.getAttributeName());
632     assertTrue(fc.hasThreshold());
633     assertTrue(fc.isBelowThreshold());
634     assertEquals(13f, fc.getThreshold());
635     assertEquals(Color.RED, fc.getMinColour());
636     assertEquals(Color.GREEN, fc.getMaxColour());
637     assertEquals(10f, fc.getMin());
638     assertEquals(20f, fc.getMax());
639     assertTrue(fc.isAutoScaled());
640
641     /*
642      * graduated colour (by RGB triplet) (below threshold), absolute scale
643      */
644     descriptor = "255,0,0|0,255,0|abso|10.0|20.0|below|15";
645     fc = FeatureColour.parseJalviewFeatureColour(descriptor);
646     assertTrue(fc.isGraduatedColour());
647     assertFalse(fc.isAutoScaled());
648     assertTrue(fc.hasThreshold());
649     assertTrue(fc.isBelowThreshold());
650     assertEquals(15f, fc.getThreshold());
651     assertEquals(Color.RED, fc.getMinColour());
652     assertEquals(Color.GREEN, fc.getMaxColour());
653     assertEquals(10f, fc.getMin());
654     assertEquals(20f, fc.getMax());
655
656     descriptor = "blue|255,0,255|absolute|20.0|95.0|below|66.0";
657     fc = FeatureColour.parseJalviewFeatureColour(descriptor);
658     assertTrue(fc.isGraduatedColour());
659   }
660
661   @Test(groups = { "Functional" })
662   public void testGetColor_colourByAttributeText()
663   {
664     FeatureColour fc = new FeatureColour();
665     fc.setColourByLabel(true);
666     fc.setAttributeName("consequence");
667     SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 1f,
668             null);
669
670     /*
671      * if feature has no such attribute, use 'no value' colour
672      */
673     assertEquals(FeatureColour.DEFAULT_NO_COLOUR, fc.getColor(sf));
674
675     /*
676      * if feature has attribute, generate colour from value
677      */
678     sf.setValue("consequence", "benign");
679     Color expected = ColorUtils.createColourFromName("benign");
680     assertEquals(expected, fc.getColor(sf));
681   }
682
683   @Test(groups = { "Functional" })
684   public void testGetColor_GraduatedByAttributeValue()
685   {
686     /*
687      * graduated colour based on attribute value for AF
688      * given a min-max range of 0-100
689      */
690     FeatureColour fc = new FeatureColour(new Color(50, 100, 150),
691             new Color(150, 200, 250), Color.yellow, 0f, 100f);
692     String attName = "AF";
693     fc.setAttributeName(attName);
694
695     /*
696      * first case: feature lacks the attribute - use 'no value' colour
697      */
698     SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 75f,
699             null);
700     assertEquals(Color.yellow, fc.getColor(sf));
701
702     /*
703      * second case: attribute present but not numeric - treat as if absent
704      */
705     sf.setValue(attName, "twelve");
706     assertEquals(Color.yellow, fc.getColor(sf));
707
708     /*
709      * third case: valid attribute value
710      */
711     sf.setValue(attName, "20.0");
712     Color expected = new Color(70, 120, 170);
713     assertEquals(expected, fc.getColor(sf));
714   }
715
716   @Test(groups = { "Functional" })
717   public void testIsOutwithThreshold()
718   {
719     FeatureColourI fc = new FeatureColour(Color.red);
720     SequenceFeature sf = new SequenceFeature("METAL", "desc", 10, 12, 1.2f, "grp");
721     assertFalse(fc.isOutwithThreshold(null));
722     assertFalse(fc.isOutwithThreshold(sf));
723
724     fc = new FeatureColour(Color.white, Color.black, Color.green, 0f, 10f);
725     assertFalse(fc.isOutwithThreshold(sf)); // no threshold
726
727     fc.setAboveThreshold(true);
728     fc.setThreshold(1f);
729     assertFalse(fc.isOutwithThreshold(sf)); // feature score 1.2 is above 1
730
731     fc.setThreshold(2f);
732     assertTrue(fc.isOutwithThreshold(sf)); // feature score 1.2 is not above 2
733
734     fc.setBelowThreshold(true);
735     assertFalse(fc.isOutwithThreshold(sf)); // feature score 1.2 is below 2
736
737     fc.setThreshold(1f);
738     assertTrue(fc.isOutwithThreshold(sf)); // feature score 1.2 is not below 1
739
740     /*
741      * with attribute value threshold
742      */
743     fc.setAttributeName("AC");
744     assertFalse(fc.isOutwithThreshold(sf)); // missing attribute AC is ignored
745
746     sf.setValue("AC", "-1");
747     assertFalse(fc.isOutwithThreshold(sf)); // value -1 is below 1
748
749     sf.setValue("AC", "1");
750     assertTrue(fc.isOutwithThreshold(sf)); // value 1 is not below 1
751
752     sf.setValue("AC", "junk");
753     assertFalse(fc.isOutwithThreshold(sf)); // bad value is ignored
754   }
755 }