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