JAL-32423 tidying
[jalview.git] / test / jalview / renderer / seqfeatures / FeatureColourFinderTest.java
1 package jalview.renderer.seqfeatures;
2
3 import static org.testng.Assert.assertEquals;
4 import static org.testng.Assert.assertFalse;
5 import static org.testng.Assert.assertNotEquals;
6 import static org.testng.Assert.assertNull;
7 import static org.testng.Assert.assertTrue;
8
9 import jalview.api.FeatureColourI;
10 import jalview.bin.Cache;
11 import jalview.datamodel.SequenceFeature;
12 import jalview.datamodel.SequenceI;
13 import jalview.gui.AlignFrame;
14 import jalview.gui.AlignViewport;
15 import jalview.gui.FeatureRenderer;
16 import jalview.io.DataSourceType;
17 import jalview.io.FileLoader;
18 import jalview.schemes.FeatureColour;
19 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
20
21 import java.awt.Color;
22 import java.util.List;
23
24 import org.testng.annotations.BeforeMethod;
25 import org.testng.annotations.BeforeTest;
26 import org.testng.annotations.Test;
27
28 /**
29  * Unit tests for feature colour determination, including but not limited to
30  * <ul>
31  * <li>gap position</li>
32  * <li>no features present</li>
33  * <li>features present but show features turned off</li>
34  * <li>features displayed but selected feature turned off</li>
35  * <li>features displayed but feature group turned off</li>
36  * <li>feature displayed but none at the specified position</li>
37  * <li>multiple features at position, with no transparency</li>
38  * <li>multiple features at position, with transparency</li>
39  * <li>score graduated feature colour</li>
40  * <li>contact feature start at the selected position</li>
41  * <li>contact feature end at the selected position</li>
42  * <li>contact feature straddling the selected position (not shown)</li>
43  * </ul>
44  */
45 public class FeatureColourFinderTest
46 {
47   private AlignViewport av;
48
49   private SequenceI seq;
50
51   private FeatureColourFinder finder;
52
53   private AlignFrame af;
54
55   private FeatureRenderer fr;
56
57   @BeforeTest(alwaysRun = true)
58   public void setUp()
59   {
60     Cache.initLogger();
61     // aligned column 8 is sequence position 6
62     String s = ">s1\nABCDE---FGHIJKLMNOPQRSTUVWXYZ\n";
63     af = new FileLoader().LoadFileWaitTillLoaded(s,
64             DataSourceType.PASTE);
65     av = af.getViewport();
66     seq = av.getAlignment().getSequenceAt(0);
67     fr = af.getFeatureRenderer();
68     finder = new FeatureColourFinder(fr);
69   }
70
71   /**
72    * Clear down any sequence features before each test (not as easy as it
73    * sounds...)
74    */
75   @BeforeMethod(alwaysRun = true)
76   public void setUpBeforeTest()
77   {
78     /*
79      * fudge for failure in test suite on Windows;
80      * without this call added, av.getAlignment() is null;
81      * root cause not known - AlignViewport.dispose() called??
82      */
83     setUp();
84
85     List<SequenceFeature> sfs = seq.getSequenceFeatures();
86     for (SequenceFeature sf : sfs)
87     {
88       seq.deleteFeature(sf);
89     }
90     fr.findAllFeatures(true);
91
92     /*
93      * reset all feature groups to visible
94      */
95     for (String group : fr.getGroups(false))
96     {
97       fr.setGroupVisibility(group, true);
98     }
99
100     fr.clearRenderOrder();
101     av.setShowSequenceFeatures(true);
102   }
103
104   @Test(groups = "Functional")
105   public void testFindFeatureColour_noFeatures()
106   {
107     av.setShowSequenceFeatures(false);
108     Color c = finder.findFeatureColour(Color.blue, seq, 10);
109     assertEquals(c, Color.blue);
110
111     av.setShowSequenceFeatures(true);
112     c = finder.findFeatureColour(Color.blue, seq, 10);
113     assertEquals(c, Color.blue);
114   }
115
116   @Test(groups = "Functional")
117   public void testFindFeatureColour_noFeaturesShown()
118   {
119     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
120             Float.NaN, "MetalGroup"));
121     fr.featuresAdded();
122     av.setShowSequenceFeatures(false);
123     Color c = finder.findFeatureColour(Color.blue, seq, 10);
124     assertEquals(c, Color.blue);
125   }
126
127   @Test(groups = "Functional")
128   public void testFindFeatureColour_singleFeatureAtPosition()
129   {
130     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
131             Float.NaN, "MetalGroup"));
132     fr.setColour("Metal", new FeatureColour(Color.red));
133     fr.featuresAdded();
134     av.setShowSequenceFeatures(true);
135     Color c = finder.findFeatureColour(Color.blue, seq, 10);
136     assertEquals(c, Color.red);
137   }
138
139   /**
140    * feature colour at a gap is null (not white) - a user defined colour scheme
141    * can then provide a bespoke gap colour if configured to do so
142    */
143   @Test(groups = "Functional")
144   public void testFindFeatureColour_gapPosition()
145   {
146     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, 0f,
147             null));
148     fr.setColour("Metal", new FeatureColour(Color.red));
149     fr.featuresAdded();
150     av.setShowSequenceFeatures(true);
151     Color c = finder.findFeatureColour(null, seq, 6);
152     assertNull(c);
153   }
154
155   @Test(groups = "Functional")
156   public void testFindFeatureColour_multipleFeaturesAtPositionNoTransparency()
157   {
158     /*
159      * featuresAdded -> FeatureRendererModel.updateRenderOrder which adds any
160      * new features 'on top' (but reverses the order of any added features)
161      */
162     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
163             Float.NaN, "MetalGroup"));
164     FeatureColour red = new FeatureColour(Color.red);
165     fr.setColour("Metal", red);
166     fr.featuresAdded();
167     seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15,
168             Float.NaN, "DomainGroup"));
169     FeatureColour green = new FeatureColour(Color.green);
170     fr.setColour("Domain", green);
171     fr.featuresAdded();
172     av.setShowSequenceFeatures(true);
173
174     /*
175      * expect Domain (green) to be rendered above Metal (red)
176      */
177     Color c = finder.findFeatureColour(Color.blue, seq, 10);
178     assertEquals(c, Color.green);
179
180     /*
181      * now promote Metal above Domain
182      * - currently no way other than mimicking reordering of
183      * table in Feature Settings
184      */
185     FeatureSettingsBean[] data = new FeatureSettingsBean[2];
186     data[0] = new FeatureSettingsBean("Metal", red, null, true);
187     data[1] = new FeatureSettingsBean("Domain", green, null, true);
188     fr.setFeaturePriority(data);
189     c = finder.findFeatureColour(Color.blue, seq, 10);
190     assertEquals(c, Color.red);
191
192     /*
193      * ..and turn off display of Metal
194      */
195     data[0] = new FeatureSettingsBean("Metal", red, null, false);
196     fr.setFeaturePriority(data);
197     c = finder.findFeatureColour(Color.blue, seq, 10);
198     assertEquals(c, Color.green);
199   }
200
201   @Test(groups = "Functional")
202   public void testFindFeatureColour_singleFeatureNotAtPosition()
203   {
204     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 8, 12,
205             Float.NaN, "MetalGroup"));
206     fr.setColour("Metal", new FeatureColour(Color.red));
207     fr.featuresAdded();
208     av.setShowSequenceFeatures(true);
209     // column 2 = sequence position 3
210     Color c = finder.findFeatureColour(Color.blue, seq, 2);
211     assertEquals(c, Color.blue);
212   }
213
214   @Test(groups = "Functional")
215   public void testFindFeatureColour_featureTypeNotDisplayed()
216   {
217     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
218             Float.NaN, "MetalGroup"));
219     FeatureColour red = new FeatureColour(Color.red);
220     fr.setColour("Metal", red);
221     fr.featuresAdded();
222     av.setShowSequenceFeatures(true);
223     Color c = finder.findFeatureColour(Color.blue, seq, 10);
224     assertEquals(c, Color.red);
225
226     /*
227      * turn off display of Metal - is this the easiest way to do it??
228      */
229     FeatureSettingsBean[] data = new FeatureSettingsBean[1];
230     data[0] = new FeatureSettingsBean("Metal", red, null, false);
231     fr.setFeaturePriority(data);
232     c = finder.findFeatureColour(Color.blue, seq, 10);
233     assertEquals(c, Color.blue);
234
235     /*
236      * turn display of Metal back on
237      */
238     data[0] = new FeatureSettingsBean("Metal", red, null, true);
239     fr.setFeaturePriority(data);
240     c = finder.findFeatureColour(Color.blue, seq, 10);
241     assertEquals(c, Color.red);
242   }
243
244   @Test(groups = "Functional")
245   public void testFindFeatureColour_featureGroupNotDisplayed()
246   {
247     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
248             Float.NaN, "MetalGroup"));
249     FeatureColour red = new FeatureColour(Color.red);
250     fr.setColour("Metal", red);
251     fr.featuresAdded();
252     av.setShowSequenceFeatures(true);
253     Color c = finder.findFeatureColour(Color.blue, seq, 10);
254     assertEquals(c, Color.red);
255
256     /*
257      * turn off display of MetalGroup
258      */
259     fr.setGroupVisibility("MetalGroup", false);
260     c = finder.findFeatureColour(Color.blue, seq, 10);
261     assertEquals(c, Color.blue);
262
263     /*
264      * turn display of MetalGroup back on
265      */
266     fr.setGroupVisibility("MetalGroup", true);
267     c = finder.findFeatureColour(Color.blue, seq, 10);
268     assertEquals(c, Color.red);
269   }
270
271   @Test(groups = "Functional")
272   public void testFindFeatureColour_contactFeature()
273   {
274     /*
275      * currently contact feature == type "Disulphide Bond" or "Disulfide Bond" !!
276      */
277     seq.addSequenceFeature(new SequenceFeature("Disulphide Bond",
278             "Contact", 2, 12, Float.NaN, "Disulphide"));
279     fr.setColour("Disulphide Bond", new FeatureColour(Color.red));
280     fr.featuresAdded();
281     av.setShowSequenceFeatures(true);
282
283     /*
284      * Contact positions are residues 2 and 12
285      * which are columns 1 and 14
286      * positions in between don't count for a contact feature!
287      */
288     Color c = finder.findFeatureColour(Color.blue, seq, 10);
289     assertEquals(c, Color.blue);
290     c = finder.findFeatureColour(Color.blue, seq, 8);
291     assertEquals(c, Color.blue);
292     c = finder.findFeatureColour(Color.blue, seq, 1);
293     assertEquals(c, Color.red);
294     c = finder.findFeatureColour(Color.blue, seq, 14);
295     assertEquals(c, Color.red);
296   }
297
298   @Test(groups = "Functional")
299   public void testFindFeatureAtEnd()
300   {
301     /*
302      * terminal residue feature
303      */
304     seq.addSequenceFeature(new SequenceFeature("PDBRESNUM", "pdb res 1",
305             seq.getEnd(), seq.getEnd(), Float.NaN, "1seq.pdb"));
306     fr.setColour("PDBRESNUM", new FeatureColour(Color.red));
307     fr.featuresAdded();
308     av.setShowSequenceFeatures(true);
309
310     /*
311      * final column should have PDBRESNUM feature, the others not
312      */
313     Color c = finder.findFeatureColour(Color.blue, seq,
314             seq.getLength() - 2);
315     assertNotEquals(c, Color.red);
316     c = finder.findFeatureColour(Color.blue, seq, seq.getLength() - 1);
317     assertEquals(c, Color.red);
318   }
319
320   @Test(groups = "Functional")
321   public void testFindFeatureColour_graduatedFeatureColour()
322   {
323     seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2,
324             2, 0f, "KdGroup"));
325     seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 4,
326             4, 5f, "KdGroup"));
327     seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 7,
328             7, 10f, "KdGroup"));
329
330     /*
331      * graduated colour from 0 to 10
332      */
333     Color min = new Color(100, 50, 150);
334     Color max = new Color(200, 0, 100);
335     FeatureColourI fc = new FeatureColour(null, min, max, null, 0, 10);
336     fr.setColour("kd", fc);
337     fr.featuresAdded();
338     av.setShowSequenceFeatures(true);
339
340     /*
341      * position 2, column 1, score 0 - minimum colour in range
342      */
343     Color c = finder.findFeatureColour(Color.blue, seq, 1);
344     assertEquals(c, min);
345
346     /*
347      * position 7, column 9, score 10 - maximum colour in range
348      */
349     c = finder.findFeatureColour(Color.blue, seq, 9);
350     assertEquals(c, max);
351
352     /*
353      * position 4, column 3, score 5 - half way from min to max
354      */
355     c = finder.findFeatureColour(Color.blue, seq, 3);
356     assertEquals(c, new Color(150, 25, 125));
357   }
358
359   @Test(groups = "Functional")
360   public void testFindFeatureColour_transparencySingleFeature()
361   {
362     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
363             Float.NaN, "MetalGroup"));
364     FeatureColour red = new FeatureColour(Color.red);
365     fr.setColour("Metal", red);
366     fr.featuresAdded();
367     av.setShowSequenceFeatures(true);
368   
369     /*
370      * the FeatureSettings transparency slider has range 0-70 which
371      * corresponds to a transparency value of 1 - 0.3
372      * A value of 0.4 gives a combination of
373      * 0.4 * red(255, 0, 0) + 0.6 * cyan(0, 255, 255) = (102, 153, 153)
374      */
375     fr.setTransparency(0.4f);
376     Color c = finder.findFeatureColour(Color.cyan, seq, 10);
377     assertEquals(c, new Color(102, 153, 153));
378   }
379
380   @Test(groups = "Functional")
381   public void testFindFeatureColour_transparencyTwoFeatures()
382   {
383     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
384             Float.NaN, "MetalGroup"));
385     FeatureColour red = new FeatureColour(Color.red);
386     fr.setColour("Metal", red);
387     fr.featuresAdded();
388     seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15,
389             Float.NaN, "DomainGroup"));
390     FeatureColour green = new FeatureColour(Color.green);
391     fr.setColour("Domain", green);
392     fr.featuresAdded();
393     av.setShowSequenceFeatures(true);
394   
395     /*
396      * Domain (green) rendered above Metal (red) above background (cyan)
397      * 1) 0.6 * red(255, 0, 0) + 0.4 * cyan(0, 255, 255) = (153, 102, 102)
398      * 2) 0.6* green(0, 255, 0) + 0.4 * (153, 102, 102) = (61, 194, 41) rounded
399      */
400     fr.setTransparency(0.6f);
401     Color c = finder.findFeatureColour(Color.cyan, seq, 10);
402     assertEquals(c, new Color(61, 194, 41));
403   
404     /*
405      * now promote Metal above Domain
406      * - currently no way other than mimicking reordering of
407      * table in Feature Settings
408      * Metal (red) rendered above Domain (green) above background (cyan)
409      * 1) 0.6 * green(0, 255, 0) + 0.4 * cyan(0, 255, 255) = (0, 255, 102)
410      * 2) 0.6* red(255, 0, 0) + 0.4 * (0, 255, 102) = (153, 102, 41) rounded
411      */
412     FeatureSettingsBean[] data = new FeatureSettingsBean[2];
413     data[0] = new FeatureSettingsBean("Metal", red, null, true);
414     data[1] = new FeatureSettingsBean("Domain", green, null, true);
415     fr.setFeaturePriority(data);
416     c = finder.findFeatureColour(Color.cyan, seq, 10);
417     assertEquals(c, new Color(153, 102, 41));
418   
419     /*
420      * ..and turn off display of Metal
421      * Domain (green) above background (pink)
422      * 0.6 * green(0, 255, 0) + 0.4 * pink(255, 175, 175) = (102, 223, 70)
423      */
424     data[0] = new FeatureSettingsBean("Metal", red, null, false);
425     fr.setFeaturePriority(data);
426     c = finder.findFeatureColour(Color.pink, seq, 10);
427     assertEquals(c, new Color(102, 223, 70));
428   }
429
430   @Test(groups = "Functional")
431   public void testNoFeaturesDisplayed()
432   {
433     /*
434      * no features on alignment to render
435      */
436     assertTrue(finder.noFeaturesDisplayed());
437
438     /*
439      * add a feature
440      * it will be automatically set visible but we leave
441      * the viewport configured not to show features
442      */
443     av.setShowSequenceFeatures(false);
444     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
445             Float.NaN, "MetalGroup"));
446     FeatureColour red = new FeatureColour(Color.red);
447     fr.setColour("Metal", red);
448     fr.featuresAdded();
449     assertTrue(finder.noFeaturesDisplayed());
450
451     /*
452      * turn on feature display
453      */
454     av.setShowSequenceFeatures(true);
455     assertFalse(finder.noFeaturesDisplayed());
456
457     /*
458      * turn off display of Metal
459      */
460     FeatureSettingsBean[] data = new FeatureSettingsBean[1];
461     data[0] = new FeatureSettingsBean("Metal", red, null, false);
462     fr.setFeaturePriority(data);
463     assertTrue(finder.noFeaturesDisplayed());
464
465     /*
466      * turn display of Metal back on
467      */
468     fr.setVisible("Metal");
469     assertFalse(finder.noFeaturesDisplayed());
470
471     /*
472      * turn off MetalGroup - has no effect here since the group of a
473      * sequence feature instance is independent of its type
474      */
475     fr.setGroupVisibility("MetalGroup", false);
476     assertFalse(finder.noFeaturesDisplayed());
477
478     /*
479      * a finder with no feature renderer
480      */
481     FeatureColourFinder finder2 = new FeatureColourFinder(null);
482     assertTrue(finder2.noFeaturesDisplayed());
483   }
484
485   @Test(groups = "Functional")
486   public void testFindFeatureColour_graduatedWithThreshold()
487   {
488     String kdFeature = "kd";
489     String metalFeature = "Metal";
490     seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 2,
491             2, 0f, "KdGroup"));
492     seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 4,
493             4, 5f, "KdGroup"));
494     seq.addSequenceFeature(new SequenceFeature(metalFeature, "Fe", 4, 4,
495             5f, "MetalGroup"));
496     seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 7,
497             7, 10f, "KdGroup"));
498   
499     /*
500      * kd feature has graduated colour from 0 to 10
501      * above threshold value of 5
502      */
503     Color min = new Color(100, 50, 150);
504     Color max = new Color(200, 0, 100);
505     FeatureColourI fc = new FeatureColour(null, min, max, null, 0, 10);
506     fc.setAboveThreshold(true);
507     fc.setThreshold(5f);
508     fr.setColour(kdFeature, fc);
509     FeatureColour green = new FeatureColour(Color.green);
510     fr.setColour(metalFeature, green);
511     fr.featuresAdded();
512
513     /*
514      * render order is kd above Metal
515      */
516     FeatureSettingsBean[] data = new FeatureSettingsBean[2];
517     data[0] = new FeatureSettingsBean(kdFeature, fc, null, true);
518     data[1] = new FeatureSettingsBean(metalFeature, green, null, true);
519     fr.setFeaturePriority(data);
520
521     av.setShowSequenceFeatures(true);
522   
523     /*
524      * position 2, column 1, score 0 - below threshold - default colour
525      */
526     Color c = finder.findFeatureColour(Color.blue, seq, 1);
527     assertEquals(c, Color.blue);
528
529     /*
530      * position 4, column 3, score 5 - at threshold
531      * should return Green (colour of Metal feature)
532      */
533     c = finder.findFeatureColour(Color.blue, seq, 3);
534     assertEquals(c, Color.green);
535   
536     /*
537      * position 7, column 9, score 10 - maximum colour in range
538      */
539     c = finder.findFeatureColour(Color.blue, seq, 9);
540     assertEquals(c, max);
541
542     /*
543      * now colour below threshold of 5
544      */
545     fc.setBelowThreshold(true);
546
547     /*
548      * position 2, column 1, score 0 - min colour
549      */
550     c = finder.findFeatureColour(Color.blue, seq, 1);
551     assertEquals(c, min);
552
553     /*
554      * position 4, column 3, score 5 - at threshold
555      * should return Green (colour of Metal feature)
556      */
557     c = finder.findFeatureColour(Color.blue, seq, 3);
558     assertEquals(c, Color.green);
559
560     /*
561      * position 7, column 9, score 10 - above threshold - default colour
562      */
563     c = finder.findFeatureColour(Color.blue, seq, 9);
564     assertEquals(c, Color.blue);
565   }
566 }