2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.renderer.seqfeatures;
23 import static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertFalse;
25 import static org.testng.Assert.assertNotNull;
26 import static org.testng.Assert.assertNull;
27 import static org.testng.Assert.assertSame;
28 import static org.testng.Assert.assertTrue;
30 import jalview.analysis.GeneticCodes;
31 import jalview.api.AlignViewportI;
32 import jalview.api.FeatureColourI;
33 import jalview.bin.Jalview;
34 import jalview.datamodel.MappedFeatures;
35 import jalview.datamodel.SequenceFeature;
36 import jalview.datamodel.SequenceI;
37 import jalview.datamodel.features.FeatureMatcher;
38 import jalview.datamodel.features.FeatureMatcherSet;
39 import jalview.datamodel.features.FeatureMatcherSetI;
40 import jalview.gui.AlignFrame;
41 import jalview.gui.AlignViewport;
42 import jalview.gui.Desktop;
43 import jalview.io.DataSourceType;
44 import jalview.io.FileLoader;
45 import jalview.schemes.FeatureColour;
46 import jalview.util.matcher.Condition;
47 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
49 import java.awt.Color;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.HashMap;
53 import java.util.List;
56 import org.testng.annotations.Test;
58 public class FeatureRendererTest
61 @Test(groups = "Functional")
62 public void testFindAllFeatures()
64 String seqData = ">s1\nabcdef\n>s2\nabcdef\n>s3\nabcdef\n>s4\nabcdef\n";
65 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
66 DataSourceType.PASTE);
67 AlignViewportI av = af.getViewport();
68 FeatureRenderer fr = new FeatureRenderer(av);
73 fr.findAllFeatures(true);
74 assertTrue(fr.getRenderOrder().isEmpty());
75 assertTrue(fr.getFeatureGroups().isEmpty());
77 List<SequenceI> seqs = av.getAlignment().getSequences();
79 // add a non-positional feature - should be ignored by FeatureRenderer
80 SequenceFeature sf1 = new SequenceFeature("Type", "Desc", 0, 0, 1f,
82 seqs.get(0).addSequenceFeature(sf1);
83 fr.findAllFeatures(true);
84 // ? bug - types and groups added for non-positional features
85 List<String> types = fr.getRenderOrder();
86 List<String> groups = fr.getFeatureGroups();
87 assertEquals(types.size(), 0);
88 assertFalse(types.contains("Type"));
89 assertEquals(groups.size(), 0);
90 assertFalse(groups.contains("Group"));
92 // add some positional features
93 seqs.get(1).addSequenceFeature(
94 new SequenceFeature("Pfam", "Desc", 5, 9, 1f, "PfamGroup"));
95 seqs.get(2).addSequenceFeature(
96 new SequenceFeature("Pfam", "Desc", 14, 22, 2f, "RfamGroup"));
97 // bug in findAllFeatures - group not checked for a known feature type
98 seqs.get(2).addSequenceFeature(new SequenceFeature("Rfam", "Desc", 5, 9,
99 Float.NaN, "RfamGroup"));
100 // existing feature type with null group
101 seqs.get(3).addSequenceFeature(
102 new SequenceFeature("Rfam", "Desc", 5, 9, Float.NaN, null));
103 // new feature type with null group
104 seqs.get(3).addSequenceFeature(
105 new SequenceFeature("Scop", "Desc", 5, 9, Float.NaN, null));
106 // null value for type produces NullPointerException
107 fr.findAllFeatures(true);
108 types = fr.getRenderOrder();
109 groups = fr.getFeatureGroups();
110 assertEquals(types.size(), 3);
111 assertFalse(types.contains("Type"));
112 assertTrue(types.contains("Pfam"));
113 assertTrue(types.contains("Rfam"));
114 assertTrue(types.contains("Scop"));
115 assertEquals(groups.size(), 2);
116 assertFalse(groups.contains("Group"));
117 assertTrue(groups.contains("PfamGroup"));
118 assertTrue(groups.contains("RfamGroup"));
119 assertFalse(groups.contains(null)); // null group is ignored
122 * check min-max values
124 Map<String, float[][]> minMax = fr.getMinMax();
125 assertEquals(minMax.size(), 1); // non-positional and NaN not stored
126 assertEquals(minMax.get("Pfam")[0][0], 1f); // positional min
127 assertEquals(minMax.get("Pfam")[0][1], 2f); // positional max
129 // increase max for Pfam, add scores for Rfam
130 seqs.get(0).addSequenceFeature(
131 new SequenceFeature("Pfam", "Desc", 14, 22, 8f, "RfamGroup"));
132 seqs.get(1).addSequenceFeature(
133 new SequenceFeature("Rfam", "Desc", 5, 9, 6f, "RfamGroup"));
134 fr.findAllFeatures(true);
135 // note minMax is not a defensive copy, shouldn't expose this
136 assertEquals(minMax.size(), 2);
137 assertEquals(minMax.get("Pfam")[0][0], 1f);
138 assertEquals(minMax.get("Pfam")[0][1], 8f);
139 assertEquals(minMax.get("Rfam")[0][0], 6f);
140 assertEquals(minMax.get("Rfam")[0][1], 6f);
143 * check render order (last is on top)
145 List<String> renderOrder = fr.getRenderOrder();
146 assertEquals(renderOrder, Arrays.asList("Scop", "Rfam", "Pfam"));
149 * change render order (todo: an easier way)
150 * nb here last comes first in the data array
152 FeatureSettingsBean[] data = new FeatureSettingsBean[3];
153 FeatureColourI colour = new FeatureColour(Color.RED);
154 data[0] = new FeatureSettingsBean("Rfam", colour, null, true);
155 data[1] = new FeatureSettingsBean("Pfam", colour, null, false);
156 data[2] = new FeatureSettingsBean("Scop", colour, null, false);
157 fr.setFeaturePriority(data);
158 assertEquals(fr.getRenderOrder(),
159 Arrays.asList("Scop", "Pfam", "Rfam"));
160 assertEquals(fr.getDisplayedFeatureTypes(), Arrays.asList("Rfam"));
163 * add a new feature type: should go on top of render order as visible,
164 * other feature ordering and visibility should be unchanged
166 seqs.get(2).addSequenceFeature(
167 new SequenceFeature("Metal", "Desc", 14, 22, 8f, "MetalGroup"));
168 fr.findAllFeatures(true);
169 assertEquals(fr.getRenderOrder(),
170 Arrays.asList("Scop", "Pfam", "Rfam", "Metal"));
171 assertEquals(fr.getDisplayedFeatureTypes(),
172 Arrays.asList("Rfam", "Metal"));
175 @Test(groups = "Functional")
176 public void testFindFeaturesAtColumn()
178 String seqData = ">s1/4-29\n-ab--cdefghijklmnopqrstuvwxyz\n";
179 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
180 DataSourceType.PASTE);
181 AlignViewportI av = af.getViewport();
182 FeatureRenderer fr = new FeatureRenderer(av);
183 SequenceI seq = av.getAlignment().getSequenceAt(0);
188 List<SequenceFeature> features = fr.findFeaturesAtColumn(seq, 3);
189 assertTrue(features.isEmpty());
194 SequenceFeature sf1 = new SequenceFeature("Type1", "Desc", 0, 0, 1f,
195 "Group"); // non-positional
196 seq.addSequenceFeature(sf1);
197 SequenceFeature sf2 = new SequenceFeature("Type2", "Desc", 8, 18, 1f,
199 seq.addSequenceFeature(sf2);
200 SequenceFeature sf3 = new SequenceFeature("Type3", "Desc", 8, 18, 1f,
202 seq.addSequenceFeature(sf3);
203 SequenceFeature sf4 = new SequenceFeature("Type3", "Desc", 8, 18, 1f,
204 null); // null group is always treated as visible
205 seq.addSequenceFeature(sf4);
208 * add contact features
210 SequenceFeature sf5 = new SequenceFeature("Disulphide Bond", "Desc", 7,
212 seq.addSequenceFeature(sf5);
213 SequenceFeature sf6 = new SequenceFeature("Disulphide Bond", "Desc", 7,
215 seq.addSequenceFeature(sf6);
216 SequenceFeature sf7 = new SequenceFeature("Disulphide Bond", "Desc", 7,
218 seq.addSequenceFeature(sf7);
220 // feature spanning B--C
221 SequenceFeature sf8 = new SequenceFeature("Type1", "Desc", 5, 6, 1f,
223 seq.addSequenceFeature(sf8);
224 // contact feature B/C
225 SequenceFeature sf9 = new SequenceFeature("Disulphide Bond", "Desc", 5,
227 seq.addSequenceFeature(sf9);
230 * let feature renderer discover features (and make visible)
232 fr.findAllFeatures(true);
233 features = fr.findFeaturesAtColumn(seq, 15); // all positional
234 assertEquals(features.size(), 6);
235 assertTrue(features.contains(sf2));
236 assertTrue(features.contains(sf3));
237 assertTrue(features.contains(sf4));
238 assertTrue(features.contains(sf5));
239 assertTrue(features.contains(sf6));
240 assertTrue(features.contains(sf7));
243 * at a non-contact position
245 features = fr.findFeaturesAtColumn(seq, 14);
246 assertEquals(features.size(), 3);
247 assertTrue(features.contains(sf2));
248 assertTrue(features.contains(sf3));
249 assertTrue(features.contains(sf4));
252 * make "Type2" not displayed
254 FeatureColourI colour = new FeatureColour(Color.RED);
255 FeatureSettingsBean[] data = new FeatureSettingsBean[4];
256 data[0] = new FeatureSettingsBean("Type1", colour, null, true);
257 data[1] = new FeatureSettingsBean("Type2", colour, null, false);
258 data[2] = new FeatureSettingsBean("Type3", colour, null, true);
259 data[3] = new FeatureSettingsBean("Disulphide Bond", colour, null,
261 fr.setFeaturePriority(data);
263 features = fr.findFeaturesAtColumn(seq, 15);
264 assertEquals(features.size(), 5); // no sf2
265 assertTrue(features.contains(sf3));
266 assertTrue(features.contains(sf4));
267 assertTrue(features.contains(sf5));
268 assertTrue(features.contains(sf6));
269 assertTrue(features.contains(sf7));
272 * make "Group2" not displayed
274 fr.setGroupVisibility("Group2", false);
276 features = fr.findFeaturesAtColumn(seq, 15);
277 assertEquals(features.size(), 3); // no sf2, sf3, sf6
278 assertTrue(features.contains(sf4));
279 assertTrue(features.contains(sf5));
280 assertTrue(features.contains(sf7));
282 // features 'at' a gap between b and c
283 // - returns enclosing feature BC but not contact feature B/C
284 features = fr.findFeaturesAtColumn(seq, 4);
285 assertEquals(features.size(), 1);
286 assertTrue(features.contains(sf8));
287 features = fr.findFeaturesAtColumn(seq, 5);
288 assertEquals(features.size(), 1);
289 assertTrue(features.contains(sf8));
292 * give "Type3" features a graduated colour scheme
293 * - first with no threshold
295 FeatureColourI gc = new FeatureColour(Color.green, Color.yellow,
296 Color.red, null, 0f, 10f);
297 fr.getFeatureColours().put("Type3", gc);
298 features = fr.findFeaturesAtColumn(seq, 8);
299 assertTrue(features.contains(sf4));
300 // now with threshold > 2f - feature score of 1f is excluded
301 gc.setAboveThreshold(true);
303 features = fr.findFeaturesAtColumn(seq, 8);
304 assertFalse(features.contains(sf4));
307 * make "Type3" graduated colour by attribute "AF"
308 * - first with no attribute held - feature should be excluded
310 gc.setAttributeName("AF");
311 features = fr.findFeaturesAtColumn(seq, 8);
312 assertFalse(features.contains(sf4));
313 // now with the attribute above threshold - should be included
314 sf4.setValue("AF", "2.4");
315 features = fr.findFeaturesAtColumn(seq, 8);
316 assertTrue(features.contains(sf4));
317 // now with the attribute below threshold - should be excluded
318 sf4.setValue("AF", "1.4");
319 features = fr.findFeaturesAtColumn(seq, 8);
320 assertFalse(features.contains(sf4));
323 @Test(groups = "Functional")
324 public void testFilterFeaturesForDisplay()
326 String seqData = ">s1\nabcdef\n";
327 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
328 DataSourceType.PASTE);
329 AlignViewportI av = af.getViewport();
330 FeatureRenderer fr = new FeatureRenderer(av);
332 List<SequenceFeature> features = new ArrayList<>();
333 fr.filterFeaturesForDisplay(features); // empty list, does nothing
335 SequenceI seq = av.getAlignment().getSequenceAt(0);
336 SequenceFeature sf1 = new SequenceFeature("Cath", "", 6, 8, Float.NaN,
338 SequenceFeature sf2 = new SequenceFeature("Cath", "", 5, 11, 2f,
340 SequenceFeature sf3 = new SequenceFeature("Cath", "", 5, 11, 3f,
342 SequenceFeature sf4 = new SequenceFeature("Cath", "", 6, 8, 4f,
344 SequenceFeature sf5 = new SequenceFeature("Cath", "", 6, 9, 5f,
346 seq.addSequenceFeature(sf1);
347 seq.addSequenceFeature(sf2);
348 seq.addSequenceFeature(sf3);
349 seq.addSequenceFeature(sf4);
350 seq.addSequenceFeature(sf5);
352 fr.findAllFeatures(true);
354 features = seq.getSequenceFeatures();
355 assertEquals(features.size(), 5);
356 assertTrue(features.contains(sf1));
357 assertTrue(features.contains(sf2));
358 assertTrue(features.contains(sf3));
359 assertTrue(features.contains(sf4));
360 assertTrue(features.contains(sf5));
363 * filter out duplicate (co-located) features
364 * note: which gets removed is not guaranteed
366 fr.filterFeaturesForDisplay(features);
367 assertEquals(features.size(), 3);
368 assertTrue(features.contains(sf1) || features.contains(sf4));
369 assertFalse(features.contains(sf1) && features.contains(sf4));
370 assertTrue(features.contains(sf2) || features.contains(sf3));
371 assertFalse(features.contains(sf2) && features.contains(sf3));
372 assertTrue(features.contains(sf5));
375 * features in hidden groups are removed
377 fr.setGroupVisibility("group2", false);
378 fr.setGroupVisibility("group3", false);
379 features = seq.getSequenceFeatures();
380 fr.filterFeaturesForDisplay(features);
381 assertEquals(features.size(), 2);
382 assertTrue(features.contains(sf1) || features.contains(sf4));
383 assertFalse(features.contains(sf1) && features.contains(sf4));
384 assertFalse(features.contains(sf2));
385 assertFalse(features.contains(sf3));
386 assertTrue(features.contains(sf5));
389 * no filtering if transparency is applied
391 fr.setTransparency(0.5f);
392 features = seq.getSequenceFeatures();
393 fr.filterFeaturesForDisplay(features);
394 assertEquals(features.size(), 5);
397 @Test(groups = "Functional")
398 public void testGetColour()
400 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(">s1\nABCD\n",
401 DataSourceType.PASTE);
402 AlignViewportI av = af.getViewport();
403 FeatureRenderer fr = new FeatureRenderer(av);
406 * simple colour, feature type and group displayed
408 FeatureColourI fc = new FeatureColour(Color.red);
409 fr.getFeatureColours().put("Cath", fc);
410 SequenceFeature sf1 = new SequenceFeature("Cath", "", 6, 8, Float.NaN,
412 assertEquals(fr.getColour(sf1), Color.red);
415 * hide feature type, then unhide
416 * - feature type visibility should not affect the result
418 FeatureSettingsBean[] data = new FeatureSettingsBean[1];
419 data[0] = new FeatureSettingsBean("Cath", fc, null, false);
420 fr.setFeaturePriority(data);
421 assertEquals(fr.getColour(sf1), Color.red);
422 data[0] = new FeatureSettingsBean("Cath", fc, null, true);
423 fr.setFeaturePriority(data);
424 assertEquals(fr.getColour(sf1), Color.red);
427 * hide feature group, then unhide
429 fr.setGroupVisibility("group1", false);
430 assertNull(fr.getColour(sf1));
431 fr.setGroupVisibility("group1", true);
432 assertEquals(fr.getColour(sf1), Color.red);
435 * graduated colour by score, no threshold, no score
438 FeatureColourI gc = new FeatureColour(Color.red, Color.yellow,
439 Color.red, Color.green, 1f, 11f);
440 fr.getFeatureColours().put("Cath", gc);
441 assertEquals(fr.getColour(sf1), Color.green);
444 * graduated colour by score, no threshold, with score value
446 SequenceFeature sf2 = new SequenceFeature("Cath", "", 6, 8, 6f,
448 // score 6 is half way from yellow(255, 255, 0) to red(255, 0, 0)
449 Color expected = new Color(255, 128, 0);
450 assertEquals(fr.getColour(sf2), expected);
453 * above threshold, score is above threshold - no change
455 gc.setAboveThreshold(true);
457 assertEquals(fr.getColour(sf2), expected);
460 * threshold is min-max; now score 6 is 1/6 of the way from 5 to 11
461 * or from yellow(255, 255, 0) to red(255, 0, 0)
463 gc = new FeatureColour(Color.red, Color.yellow, Color.red, Color.green,
465 fr.getFeatureColours().put("Cath", gc);
466 gc.setAutoScaled(false); // this does little other than save a checkbox setting!
467 assertEquals(fr.getColour(sf2), new Color(255, 213, 0));
470 * feature score is below threshold - no colour
472 gc.setAboveThreshold(true);
474 assertNull(fr.getColour(sf2));
477 * feature score is above threshold - no colour
479 gc.setBelowThreshold(true);
481 assertNull(fr.getColour(sf2));
484 * colour by feature attribute value
485 * first with no value held
487 gc = new FeatureColour(Color.red, Color.yellow, Color.red, Color.green,
489 fr.getFeatureColours().put("Cath", gc);
490 gc.setAttributeName("AF");
491 assertEquals(fr.getColour(sf2), Color.green);
493 // with non-numeric attribute value
494 sf2.setValue("AF", "Five");
495 assertEquals(fr.getColour(sf2), Color.green);
497 // with numeric attribute value
498 sf2.setValue("AF", "6");
499 assertEquals(fr.getColour(sf2), expected);
501 // with numeric value outwith threshold
502 gc.setAboveThreshold(true);
503 gc.setThreshold(10f);
504 assertNull(fr.getColour(sf2));
506 // with filter on AF < 4
507 gc.setAboveThreshold(false);
508 assertEquals(fr.getColour(sf2), expected);
509 FeatureMatcherSetI filter = new FeatureMatcherSet();
510 filter.and(FeatureMatcher.byAttribute(Condition.LT, "4.0", "AF"));
511 fr.setFeatureFilter("Cath", filter);
512 assertNull(fr.getColour(sf2));
514 // with filter on 'Consequence contains missense'
515 filter = new FeatureMatcherSet();
516 filter.and(FeatureMatcher.byAttribute(Condition.Contains, "missense",
518 fr.setFeatureFilter("Cath", filter);
519 // if feature has no Consequence attribute, no colour
520 assertNull(fr.getColour(sf2));
521 // if attribute does not match filter, no colour
522 sf2.setValue("Consequence", "Synonymous");
523 assertNull(fr.getColour(sf2));
524 // attribute matches filter
525 sf2.setValue("Consequence", "Missense variant");
526 assertEquals(fr.getColour(sf2), expected);
528 // with filter on CSQ:Feature contains "ENST01234"
529 filter = new FeatureMatcherSet();
530 filter.and(FeatureMatcher.byAttribute(Condition.Matches, "ENST01234",
532 fr.setFeatureFilter("Cath", filter);
533 // if feature has no CSQ data, no colour
534 assertNull(fr.getColour(sf2));
535 // if CSQ data does not include Feature, no colour
536 Map<String, String> csqData = new HashMap<>();
537 csqData.put("BIOTYPE", "Transcript");
538 sf2.setValue("CSQ", csqData);
539 assertNull(fr.getColour(sf2));
540 // if attribute does not match filter, no colour
541 csqData.put("Feature", "ENST9876");
542 assertNull(fr.getColour(sf2));
543 // attribute matches filter
544 csqData.put("Feature", "ENST01234");
545 assertEquals(fr.getColour(sf2), expected);
548 @Test(groups = "Functional")
549 public void testIsVisible()
551 String seqData = ">s1\nMLQGIFPRS\n";
552 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
553 DataSourceType.PASTE);
554 AlignViewportI av = af.getViewport();
555 FeatureRenderer fr = new FeatureRenderer(av);
556 SequenceI seq = av.getAlignment().getSequenceAt(0);
557 SequenceFeature sf = new SequenceFeature("METAL", "Desc", 10, 10, 1f,
559 sf.setValue("AC", "11");
560 sf.setValue("CLIN_SIG", "Likely Pathogenic");
561 seq.addSequenceFeature(sf);
563 assertFalse(fr.isVisible(null));
566 * initial state FeatureRenderer hasn't 'found' feature
567 * and so its feature type has not yet been set visible
569 assertFalse(fr.getDisplayedFeatureCols().containsKey("METAL"));
570 assertFalse(fr.isVisible(sf));
572 fr.findAllFeatures(true);
573 assertTrue(fr.isVisible(sf));
576 * feature group not visible
578 fr.setGroupVisibility("Group", false);
579 assertFalse(fr.isVisible(sf));
580 fr.setGroupVisibility("Group", true);
581 assertTrue(fr.isVisible(sf));
584 * feature score outwith colour threshold (score > 2)
586 FeatureColourI fc = new FeatureColour(null, Color.white, Color.black,
588 fc.setAboveThreshold(true);
590 fr.setColour("METAL", fc);
591 assertFalse(fr.isVisible(sf)); // score 1 is not above threshold 2
592 fc.setBelowThreshold(true);
593 assertTrue(fr.isVisible(sf)); // score 1 is below threshold 2
596 * colour with threshold on attribute AC (value is 11)
598 fc.setAttributeName("AC");
599 assertFalse(fr.isVisible(sf)); // value 11 is not below threshold 2
600 fc.setAboveThreshold(true);
601 assertTrue(fr.isVisible(sf)); // value 11 is above threshold 2
603 fc.setAttributeName("AF"); // attribute AF is absent in sf
604 assertTrue(fr.isVisible(sf)); // feature is not excluded by threshold
606 FeatureMatcherSetI filter = new FeatureMatcherSet();
607 filter.and(FeatureMatcher.byAttribute(Condition.Contains, "pathogenic",
609 fr.setFeatureFilter("METAL", filter);
610 assertTrue(fr.isVisible(sf)); // feature matches filter
611 filter.and(FeatureMatcher.byScore(Condition.LE, "0.4"));
612 assertFalse(fr.isVisible(sf)); // feature doesn't match filter
615 @Test(groups = "Functional")
616 public void testFindComplementFeaturesAtResidue()
620 { "-nonews", "-props", "test/jalview/testProps.jvprops" });
623 String cdsSeq = ">cds\nATGtgtTGGcacTCAgaa";
624 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(cdsSeq,
625 DataSourceType.PASTE);
626 af.showTranslation_actionPerformed(
627 GeneticCodes.getInstance().getStandardCodeTable());
628 af.closeMenuItem_actionPerformed(true);
631 * find the complement frames (ugly)
633 AlignFrame[] frames = Desktop.getAlignFrames();
634 assertEquals(frames.length, 2);
635 AlignViewport av1 = frames[0].getViewport();
636 AlignViewport av2 = frames[1].getViewport();
637 AlignViewport cds = av1.getAlignment().isNucleotide() ? av1 : av2;
638 AlignViewport peptide = cds == av1 ? av2 : av1;
640 assertNotNull(peptide);
643 * add features to CDS at first codon, positions 2-3
645 SequenceI seq1 = cds.getAlignment().getSequenceAt(0);
646 SequenceFeature sf1 = new SequenceFeature("sequence_variant", "G,GT", 2,
648 seq1.addSequenceFeature(sf1);
649 SequenceFeature sf2 = new SequenceFeature("sequence_variant", "C, CA",
651 seq1.addSequenceFeature(sf2);
654 * 'find' mapped features from the peptide position
655 * - first with CDS features _not_ shown on peptide alignment
657 SequenceI seq2 = peptide.getAlignment().getSequenceAt(0);
658 FeatureRenderer frC = new FeatureRenderer(cds);
660 MappedFeatures mf = frC.findComplementFeaturesAtResidue(seq2, 1);
662 assertEquals(mf.features.size(), 2);
663 assertSame(mf.features.get(0), sf1);
664 assertSame(mf.features.get(1), sf2);
667 * add exon feature and verify it is only returned once for a
668 * peptide position, even though it is on all 3 codon positions
670 SequenceFeature sf3 = new SequenceFeature("exon", "exon1", 4, 12,
672 seq1.addSequenceFeature(sf3);
674 mf = frC.findComplementFeaturesAtResidue(seq2, 3);
676 assertEquals(mf.features.size(), 1);
677 assertSame(mf.features.get(0), sf3);