JAL-2490 findFeaturesAtRes with performant feature lookup
[jalview.git] / test / jalview / renderer / seqfeatures / FeatureRendererTest.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.assertTrue;
6
7 import jalview.api.AlignViewportI;
8 import jalview.api.FeatureColourI;
9 import jalview.datamodel.SequenceFeature;
10 import jalview.datamodel.SequenceI;
11 import jalview.gui.AlignFrame;
12 import jalview.io.DataSourceType;
13 import jalview.io.FileLoader;
14 import jalview.schemes.FeatureColour;
15
16 import java.awt.Color;
17 import java.util.Arrays;
18 import java.util.List;
19 import java.util.Map;
20
21 import org.testng.annotations.Test;
22
23 public class FeatureRendererTest
24 {
25
26   @Test(groups = "Functional")
27   public void testFindAllFeatures()
28   {
29     String seqData = ">s1\nabcdef\n>s2\nabcdef\n>s3\nabcdef\n>s4\nabcdef\n";
30     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
31             DataSourceType.PASTE);
32     AlignViewportI av = af.getViewport();
33     FeatureRenderer fr = new FeatureRenderer(av);
34
35     /*
36      * with no features
37      */
38     fr.findAllFeatures(true);
39     assertTrue(fr.getRenderOrder().isEmpty());
40     assertTrue(fr.getFeatureGroups().isEmpty());
41
42     List<SequenceI> seqs = av.getAlignment().getSequences();
43
44     // add a non-positional feature - should be ignored by FeatureRenderer
45     SequenceFeature sf1 = new SequenceFeature("Type", "Desc", 0, 0, 1f,
46             "Group");
47     seqs.get(0).addSequenceFeature(sf1);
48     fr.findAllFeatures(true);
49     // ? bug - types and groups added for non-positional features
50     List<String> types = fr.getRenderOrder();
51     List<String> groups = fr.getFeatureGroups();
52     assertEquals(types.size(), 0);
53     assertFalse(types.contains("Type"));
54     assertEquals(groups.size(), 0);
55     assertFalse(groups.contains("Group"));
56
57     // add some positional features
58     seqs.get(1).addSequenceFeature(
59             new SequenceFeature("Pfam", "Desc", 5, 9, 1f, "PfamGroup"));
60     seqs.get(2).addSequenceFeature(
61             new SequenceFeature("Pfam", "Desc", 14, 22, 2f, "RfamGroup"));
62     // bug in findAllFeatures - group not checked for a known feature type
63     seqs.get(2).addSequenceFeature(
64             new SequenceFeature("Rfam", "Desc", 5, 9, Float.NaN,
65                     "RfamGroup"));
66     seqs.get(3).addSequenceFeature(
67             new SequenceFeature("Rfam", "Desc", 5, 9, Float.NaN, null));
68     // null value for type produces NullPointerException
69     fr.findAllFeatures(true);
70     types = fr.getRenderOrder();
71     groups = fr.getFeatureGroups();
72     assertEquals(types.size(), 2);
73     assertFalse(types.contains("Type"));
74     assertTrue(types.contains("Pfam"));
75     assertTrue(types.contains("Rfam"));
76     assertEquals(groups.size(), 2);
77     assertFalse(groups.contains("Group"));
78     assertTrue(groups.contains("PfamGroup"));
79     assertTrue(groups.contains("RfamGroup"));
80     assertFalse(groups.contains(null)); // null group is ignored
81
82     /*
83      * check min-max values
84      */
85     Map<String, float[][]> minMax = fr.getMinMax();
86     assertEquals(minMax.size(), 1); // non-positional and NaN not stored
87     assertEquals(minMax.get("Pfam")[0][0], 1f); // positional min
88     assertEquals(minMax.get("Pfam")[0][1], 2f); // positional max
89
90     // increase max for Pfam, add scores for Rfam
91     seqs.get(0).addSequenceFeature(
92             new SequenceFeature("Pfam", "Desc", 14, 22, 8f, "RfamGroup"));
93     seqs.get(1).addSequenceFeature(
94             new SequenceFeature("Rfam", "Desc", 5, 9, 6f, "RfamGroup"));
95     fr.findAllFeatures(true);
96     // note minMax is not a defensive copy, shouldn't expose this
97     assertEquals(minMax.size(), 2);
98     assertEquals(minMax.get("Pfam")[0][0], 1f);
99     assertEquals(minMax.get("Pfam")[0][1], 8f);
100     assertEquals(minMax.get("Rfam")[0][0], 6f);
101     assertEquals(minMax.get("Rfam")[0][1], 6f);
102
103     /*
104      * check render order (last is on top)
105      */
106     List<String> renderOrder = fr.getRenderOrder();
107     assertEquals(renderOrder, Arrays.asList("Rfam", "Pfam"));
108
109     /*
110      * change render order (todo: an easier way)
111      * nb here last comes first in the data array
112      */
113     Object[][] data = new Object[2][];
114     FeatureColourI colour = new FeatureColour(Color.RED);
115     data[0] = new Object[] { "Rfam", colour, true };
116     data[1] = new Object[] { "Pfam", colour, false };
117     fr.setFeaturePriority(data);
118     assertEquals(fr.getRenderOrder(), Arrays.asList("Pfam", "Rfam"));
119     assertEquals(fr.getDisplayedFeatureTypes(), Arrays.asList("Rfam"));
120
121     /*
122      * add a new feature type: should go on top of render order as visible,
123      * other feature ordering and visibility should be unchanged
124      */
125     seqs.get(2).addSequenceFeature(
126             new SequenceFeature("Metal", "Desc", 14, 22, 8f, "MetalGroup"));
127     fr.findAllFeatures(true);
128     assertEquals(fr.getRenderOrder(),
129             Arrays.asList("Pfam", "Rfam", "Metal"));
130     assertEquals(fr.getDisplayedFeatureTypes(),
131             Arrays.asList("Rfam", "Metal"));
132   }
133
134   @Test(groups = "Functional")
135   public void testFindFeaturesAtRes()
136   {
137     String seqData = ">s1\nabcdefghijklmnopqrstuvwxyz\n";
138     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
139             DataSourceType.PASTE);
140     AlignViewportI av = af.getViewport();
141     FeatureRenderer fr = new FeatureRenderer(av);
142     SequenceI seq = av.getAlignment().getSequenceAt(0);
143
144     /*
145      * with no features
146      */
147     List<SequenceFeature> features = fr.findFeaturesAtRes(seq, 3);
148     assertTrue(features.isEmpty());
149
150     /*
151      * add features
152      */
153     SequenceFeature sf1 = new SequenceFeature("Type1", "Desc", 0, 0, 1f,
154             "Group"); // non-positional
155     seq.addSequenceFeature(sf1);
156     SequenceFeature sf2 = new SequenceFeature("Type2", "Desc", 5, 15, 1f,
157             "Group1");
158     seq.addSequenceFeature(sf2);
159     SequenceFeature sf3 = new SequenceFeature("Type3", "Desc", 5, 15, 1f,
160             "Group2");
161     seq.addSequenceFeature(sf3);
162     SequenceFeature sf4 = new SequenceFeature("Type3", "Desc", 5, 15, 1f,
163             null); // null group is always treated as visible
164     seq.addSequenceFeature(sf4);
165
166     /*
167      * add contact features
168      */
169     SequenceFeature sf5 = new SequenceFeature("Disulphide Bond", "Desc", 4,
170             12, 1f, "Group1");
171     seq.addSequenceFeature(sf5);
172     SequenceFeature sf6 = new SequenceFeature("Disulphide Bond", "Desc", 4,
173             12, 1f, "Group2");
174     seq.addSequenceFeature(sf6);
175     SequenceFeature sf7 = new SequenceFeature("Disulphide Bond", "Desc", 4,
176             12, 1f, null);
177     seq.addSequenceFeature(sf7);
178
179     /*
180      * let feature renderer discover features (and make visible)
181      */
182     fr.findAllFeatures(true);
183     features = fr.findFeaturesAtRes(seq, 12); // all positional
184     assertEquals(features.size(), 6);
185     assertTrue(features.contains(sf2));
186     assertTrue(features.contains(sf3));
187     assertTrue(features.contains(sf4));
188     assertTrue(features.contains(sf5));
189     assertTrue(features.contains(sf6));
190     assertTrue(features.contains(sf7));
191
192     /*
193      * at a non-contact position
194      */
195     features = fr.findFeaturesAtRes(seq, 11);
196     assertEquals(features.size(), 3);
197     assertTrue(features.contains(sf2));
198     assertTrue(features.contains(sf3));
199     assertTrue(features.contains(sf4));
200
201     /*
202      * make "Type2" not displayed
203      */
204     Object[][] data = new Object[4][];
205     FeatureColourI colour = new FeatureColour(Color.RED);
206     data[0] = new Object[] { "Type1", colour, true };
207     data[1] = new Object[] { "Type2", colour, false };
208     data[2] = new Object[] { "Type3", colour, true };
209     data[3] = new Object[] { "Disulphide Bond", colour, true };
210     fr.setFeaturePriority(data);
211     features = fr.findFeaturesAtRes(seq, 12);
212     assertEquals(features.size(), 5); // no sf2
213     assertTrue(features.contains(sf3));
214     assertTrue(features.contains(sf4));
215     assertTrue(features.contains(sf5));
216     assertTrue(features.contains(sf6));
217     assertTrue(features.contains(sf7));
218
219     /*
220      * make "Group2" not displayed
221      */
222     fr.setGroupVisibility("Group2", false);
223     features = fr.findFeaturesAtRes(seq, 12);
224     assertEquals(features.size(), 3); // no sf2, sf3, sf6
225     assertTrue(features.contains(sf4));
226     assertTrue(features.contains(sf5));
227     assertTrue(features.contains(sf7));
228   }
229 }