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 java.awt.Color;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.HashMap;
34 import java.util.List;
37 import org.testng.annotations.BeforeMethod;
38 import org.testng.annotations.Test;
40 import jalview.analysis.GeneticCodes;
41 import jalview.api.AlignViewportI;
42 import jalview.api.FeatureColourI;
43 import jalview.bin.Jalview;
44 import jalview.datamodel.MappedFeatures;
45 import jalview.datamodel.SequenceFeature;
46 import jalview.datamodel.SequenceI;
47 import jalview.datamodel.features.FeatureMatcher;
48 import jalview.datamodel.features.FeatureMatcherSet;
49 import jalview.datamodel.features.FeatureMatcherSetI;
50 import jalview.gui.AlignFrame;
51 import jalview.gui.AlignViewport;
52 import jalview.gui.Desktop;
53 import jalview.io.DataSourceType;
54 import jalview.io.FileLoader;
55 import jalview.schemes.FeatureColour;
56 import jalview.util.matcher.Condition;
57 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
59 public class FeatureRendererTest
61 @BeforeMethod(alwaysRun = true)
62 public void closeAll()
64 if (Desktop.instance != null)
65 Desktop.instance.closeAll_actionPerformed(null);
68 @Test(groups = "Functional")
69 public void testFindAllFeatures()
71 String seqData = ">s1\nabcdef\n>s2\nabcdef\n>s3\nabcdef\n>s4\nabcdef\n";
72 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
73 DataSourceType.PASTE);
74 AlignViewportI av = af.getViewport();
75 FeatureRenderer fr = new FeatureRenderer(av);
80 fr.findAllFeatures(true);
81 assertTrue(fr.getRenderOrder().isEmpty());
82 assertTrue(fr.getFeatureGroups().isEmpty());
84 List<SequenceI> seqs = av.getAlignment().getSequences();
86 // add a non-positional feature - should be ignored by FeatureRenderer
87 SequenceFeature sf1 = new SequenceFeature("Type", "Desc", 0, 0, 1f,
89 seqs.get(0).addSequenceFeature(sf1);
90 fr.findAllFeatures(true);
91 // ? bug - types and groups added for non-positional features
92 List<String> types = fr.getRenderOrder();
93 List<String> groups = fr.getFeatureGroups();
94 assertEquals(types.size(), 0);
95 assertFalse(types.contains("Type"));
96 assertEquals(groups.size(), 0);
97 assertFalse(groups.contains("Group"));
99 // add some positional features
100 seqs.get(1).addSequenceFeature(
101 new SequenceFeature("Pfam", "Desc", 5, 9, 1f, "PfamGroup"));
102 seqs.get(2).addSequenceFeature(
103 new SequenceFeature("Pfam", "Desc", 14, 22, 2f, "RfamGroup"));
104 // bug in findAllFeatures - group not checked for a known feature type
105 seqs.get(2).addSequenceFeature(new SequenceFeature("Rfam", "Desc", 5, 9,
106 Float.NaN, "RfamGroup"));
107 // existing feature type with null group
108 seqs.get(3).addSequenceFeature(
109 new SequenceFeature("Rfam", "Desc", 5, 9, Float.NaN, null));
110 // new feature type with null group
111 seqs.get(3).addSequenceFeature(
112 new SequenceFeature("Scop", "Desc", 5, 9, Float.NaN, null));
113 // null value for type produces NullPointerException
114 fr.findAllFeatures(true);
115 types = fr.getRenderOrder();
116 groups = fr.getFeatureGroups();
117 assertEquals(types.size(), 3);
118 assertFalse(types.contains("Type"));
119 assertTrue(types.contains("Pfam"));
120 assertTrue(types.contains("Rfam"));
121 assertTrue(types.contains("Scop"));
122 assertEquals(groups.size(), 2);
123 assertFalse(groups.contains("Group"));
124 assertTrue(groups.contains("PfamGroup"));
125 assertTrue(groups.contains("RfamGroup"));
126 assertFalse(groups.contains(null)); // null group is ignored
129 * check min-max values
131 Map<String, float[][]> minMax = fr.getMinMax();
132 assertEquals(minMax.size(), 1); // non-positional and NaN not stored
133 assertEquals(minMax.get("Pfam")[0][0], 1f); // positional min
134 assertEquals(minMax.get("Pfam")[0][1], 2f); // positional max
136 // increase max for Pfam, add scores for Rfam
137 seqs.get(0).addSequenceFeature(
138 new SequenceFeature("Pfam", "Desc", 14, 22, 8f, "RfamGroup"));
139 seqs.get(1).addSequenceFeature(
140 new SequenceFeature("Rfam", "Desc", 5, 9, 6f, "RfamGroup"));
141 fr.findAllFeatures(true);
142 // note minMax is not a defensive copy, shouldn't expose this
143 assertEquals(minMax.size(), 2);
144 assertEquals(minMax.get("Pfam")[0][0], 1f);
145 assertEquals(minMax.get("Pfam")[0][1], 8f);
146 assertEquals(minMax.get("Rfam")[0][0], 6f);
147 assertEquals(minMax.get("Rfam")[0][1], 6f);
150 * check render order (last is on top)
152 List<String> renderOrder = fr.getRenderOrder();
153 assertEquals(renderOrder, Arrays.asList("Scop", "Rfam", "Pfam"));
156 * change render order (todo: an easier way)
157 * nb here last comes first in the data array
159 FeatureSettingsBean[] data = new FeatureSettingsBean[3];
160 FeatureColourI colour = new FeatureColour(Color.RED);
161 data[0] = new FeatureSettingsBean("Rfam", colour, null, true);
162 data[1] = new FeatureSettingsBean("Pfam", colour, null, false);
163 data[2] = new FeatureSettingsBean("Scop", colour, null, false);
164 fr.setFeaturePriority(data);
165 assertEquals(fr.getRenderOrder(),
166 Arrays.asList("Scop", "Pfam", "Rfam"));
167 assertEquals(fr.getDisplayedFeatureTypes(), Arrays.asList("Rfam"));
170 * add a new feature type: should go on top of render order as visible,
171 * other feature ordering and visibility should be unchanged
173 seqs.get(2).addSequenceFeature(
174 new SequenceFeature("Metal", "Desc", 14, 22, 8f, "MetalGroup"));
175 fr.findAllFeatures(true);
176 assertEquals(fr.getRenderOrder(),
177 Arrays.asList("Scop", "Pfam", "Rfam", "Metal"));
178 assertEquals(fr.getDisplayedFeatureTypes(),
179 Arrays.asList("Rfam", "Metal"));
182 @Test(groups = "Functional")
183 public void testFindFeaturesAtColumn()
185 String seqData = ">s1/4-29\n-ab--cdefghijklmnopqrstuvwxyz\n";
186 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
187 DataSourceType.PASTE);
188 AlignViewportI av = af.getViewport();
189 FeatureRenderer fr = new FeatureRenderer(av);
190 SequenceI seq = av.getAlignment().getSequenceAt(0);
195 List<SequenceFeature> features = fr.findFeaturesAtColumn(seq, 3);
196 assertTrue(features.isEmpty());
201 SequenceFeature sf1 = new SequenceFeature("Type1", "Desc", 0, 0, 1f,
202 "Group"); // non-positional
203 seq.addSequenceFeature(sf1);
204 SequenceFeature sf2 = new SequenceFeature("Type2", "Desc", 8, 18, 1f,
206 seq.addSequenceFeature(sf2);
207 SequenceFeature sf3 = new SequenceFeature("Type3", "Desc", 8, 18, 1f,
209 seq.addSequenceFeature(sf3);
210 SequenceFeature sf4 = new SequenceFeature("Type3", "Desc", 8, 18, 1f,
211 null); // null group is always treated as visible
212 seq.addSequenceFeature(sf4);
215 * add contact features
217 SequenceFeature sf5 = new SequenceFeature("Disulphide Bond", "Desc", 7,
219 seq.addSequenceFeature(sf5);
220 SequenceFeature sf6 = new SequenceFeature("Disulphide Bond", "Desc", 7,
222 seq.addSequenceFeature(sf6);
223 SequenceFeature sf7 = new SequenceFeature("Disulphide Bond", "Desc", 7,
225 seq.addSequenceFeature(sf7);
227 // feature spanning B--C
228 SequenceFeature sf8 = new SequenceFeature("Type1", "Desc", 5, 6, 1f,
230 seq.addSequenceFeature(sf8);
231 // contact feature B/C
232 SequenceFeature sf9 = new SequenceFeature("Disulphide Bond", "Desc", 5,
234 seq.addSequenceFeature(sf9);
237 * let feature renderer discover features (and make visible)
239 fr.findAllFeatures(true);
240 features = fr.findFeaturesAtColumn(seq, 15); // all positional
241 assertEquals(features.size(), 6);
242 assertTrue(features.contains(sf2));
243 assertTrue(features.contains(sf3));
244 assertTrue(features.contains(sf4));
245 assertTrue(features.contains(sf5));
246 assertTrue(features.contains(sf6));
247 assertTrue(features.contains(sf7));
250 * at a non-contact position
252 features = fr.findFeaturesAtColumn(seq, 14);
253 assertEquals(features.size(), 3);
254 assertTrue(features.contains(sf2));
255 assertTrue(features.contains(sf3));
256 assertTrue(features.contains(sf4));
259 * make "Type2" not displayed
261 FeatureColourI colour = new FeatureColour(Color.RED);
262 FeatureSettingsBean[] data = new FeatureSettingsBean[4];
263 data[0] = new FeatureSettingsBean("Type1", colour, null, true);
264 data[1] = new FeatureSettingsBean("Type2", colour, null, false);
265 data[2] = new FeatureSettingsBean("Type3", colour, null, true);
266 data[3] = new FeatureSettingsBean("Disulphide Bond", colour, null,
268 fr.setFeaturePriority(data);
270 features = fr.findFeaturesAtColumn(seq, 15);
271 assertEquals(features.size(), 5); // no sf2
272 assertTrue(features.contains(sf3));
273 assertTrue(features.contains(sf4));
274 assertTrue(features.contains(sf5));
275 assertTrue(features.contains(sf6));
276 assertTrue(features.contains(sf7));
279 * make "Group2" not displayed
281 fr.setGroupVisibility("Group2", false);
283 features = fr.findFeaturesAtColumn(seq, 15);
284 assertEquals(features.size(), 3); // no sf2, sf3, sf6
285 assertTrue(features.contains(sf4));
286 assertTrue(features.contains(sf5));
287 assertTrue(features.contains(sf7));
289 // features 'at' a gap between b and c
290 // - returns enclosing feature BC but not contact feature B/C
291 features = fr.findFeaturesAtColumn(seq, 4);
292 assertEquals(features.size(), 1);
293 assertTrue(features.contains(sf8));
294 features = fr.findFeaturesAtColumn(seq, 5);
295 assertEquals(features.size(), 1);
296 assertTrue(features.contains(sf8));
299 * give "Type3" features a graduated colour scheme
300 * - first with no threshold
302 FeatureColourI gc = new FeatureColour(Color.green, Color.yellow,
303 Color.red, null, 0f, 10f);
304 fr.getFeatureColours().put("Type3", gc);
305 features = fr.findFeaturesAtColumn(seq, 8);
306 assertTrue(features.contains(sf4));
307 // now with threshold > 2f - feature score of 1f is excluded
308 gc.setAboveThreshold(true);
310 features = fr.findFeaturesAtColumn(seq, 8);
311 assertFalse(features.contains(sf4));
314 * make "Type3" graduated colour by attribute "AF"
315 * - first with no attribute held - feature should be excluded
317 gc.setAttributeName("AF");
318 features = fr.findFeaturesAtColumn(seq, 8);
319 assertFalse(features.contains(sf4));
320 // now with the attribute above threshold - should be included
321 sf4.setValue("AF", "2.4");
322 features = fr.findFeaturesAtColumn(seq, 8);
323 assertTrue(features.contains(sf4));
324 // now with the attribute below threshold - should be excluded
325 sf4.setValue("AF", "1.4");
326 features = fr.findFeaturesAtColumn(seq, 8);
327 assertFalse(features.contains(sf4));
330 @Test(groups = "Functional")
331 public void testFilterFeaturesForDisplay()
333 String seqData = ">s1\nabcdef\n";
334 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
335 DataSourceType.PASTE);
336 AlignViewportI av = af.getViewport();
337 FeatureRenderer fr = new FeatureRenderer(av);
339 List<SequenceFeature> features = new ArrayList<>();
340 fr.filterFeaturesForDisplay(features); // empty list, does nothing
342 SequenceI seq = av.getAlignment().getSequenceAt(0);
343 SequenceFeature sf1 = new SequenceFeature("Cath", "", 6, 8, Float.NaN,
345 SequenceFeature sf2 = new SequenceFeature("Cath", "", 5, 11, 2f,
347 SequenceFeature sf3 = new SequenceFeature("Cath", "", 5, 11, 3f,
349 SequenceFeature sf4 = new SequenceFeature("Cath", "", 6, 8, 4f,
351 SequenceFeature sf5 = new SequenceFeature("Cath", "", 6, 9, 5f,
353 seq.addSequenceFeature(sf1);
354 seq.addSequenceFeature(sf2);
355 seq.addSequenceFeature(sf3);
356 seq.addSequenceFeature(sf4);
357 seq.addSequenceFeature(sf5);
359 fr.findAllFeatures(true);
361 features = seq.getSequenceFeatures();
362 assertEquals(features.size(), 5);
363 assertTrue(features.contains(sf1));
364 assertTrue(features.contains(sf2));
365 assertTrue(features.contains(sf3));
366 assertTrue(features.contains(sf4));
367 assertTrue(features.contains(sf5));
370 * filter out duplicate (co-located) features
371 * note: which gets removed is not guaranteed
373 fr.filterFeaturesForDisplay(features);
374 assertEquals(features.size(), 3);
375 assertTrue(features.contains(sf1) || features.contains(sf4));
376 assertFalse(features.contains(sf1) && features.contains(sf4));
377 assertTrue(features.contains(sf2) || features.contains(sf3));
378 assertFalse(features.contains(sf2) && features.contains(sf3));
379 assertTrue(features.contains(sf5));
382 * features in hidden groups are removed
384 fr.setGroupVisibility("group2", false);
385 fr.setGroupVisibility("group3", false);
386 features = seq.getSequenceFeatures();
387 fr.filterFeaturesForDisplay(features);
388 assertEquals(features.size(), 2);
389 assertTrue(features.contains(sf1) || features.contains(sf4));
390 assertFalse(features.contains(sf1) && features.contains(sf4));
391 assertFalse(features.contains(sf2));
392 assertFalse(features.contains(sf3));
393 assertTrue(features.contains(sf5));
396 * no filtering if transparency is applied
398 fr.setTransparency(0.5f);
399 features = seq.getSequenceFeatures();
400 fr.filterFeaturesForDisplay(features);
401 assertEquals(features.size(), 5);
404 @Test(groups = "Functional")
405 public void testGetColour()
407 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(">s1\nABCD\n",
408 DataSourceType.PASTE);
409 AlignViewportI av = af.getViewport();
410 FeatureRenderer fr = new FeatureRenderer(av);
413 * simple colour, feature type and group displayed
415 FeatureColourI fc = new FeatureColour(Color.red);
416 fr.getFeatureColours().put("Cath", fc);
417 SequenceFeature sf1 = new SequenceFeature("Cath", "", 6, 8, Float.NaN,
419 assertEquals(fr.getColour(sf1), Color.red);
422 * hide feature type, then unhide
423 * - feature type visibility should not affect the result
425 FeatureSettingsBean[] data = new FeatureSettingsBean[1];
426 data[0] = new FeatureSettingsBean("Cath", fc, null, false);
427 fr.setFeaturePriority(data);
428 assertEquals(fr.getColour(sf1), Color.red);
429 data[0] = new FeatureSettingsBean("Cath", fc, null, true);
430 fr.setFeaturePriority(data);
431 assertEquals(fr.getColour(sf1), Color.red);
434 * hide feature group, then unhide
436 fr.setGroupVisibility("group1", false);
437 assertNull(fr.getColour(sf1));
438 fr.setGroupVisibility("group1", true);
439 assertEquals(fr.getColour(sf1), Color.red);
442 * graduated colour by score, no threshold, no score
445 FeatureColourI gc = new FeatureColour(Color.red, Color.yellow,
446 Color.red, Color.green, 1f, 11f);
447 fr.getFeatureColours().put("Cath", gc);
448 assertEquals(fr.getColour(sf1), Color.green);
451 * graduated colour by score, no threshold, with score value
453 SequenceFeature sf2 = new SequenceFeature("Cath", "", 6, 8, 6f,
455 // score 6 is half way from yellow(255, 255, 0) to red(255, 0, 0)
456 Color expected = new Color(255, 128, 0);
457 assertEquals(fr.getColour(sf2), expected);
460 * above threshold, score is above threshold - no change
462 gc.setAboveThreshold(true);
464 assertEquals(fr.getColour(sf2), expected);
467 * threshold is min-max; now score 6 is 1/6 of the way from 5 to 11
468 * or from yellow(255, 255, 0) to red(255, 0, 0)
470 gc = new FeatureColour(Color.red, Color.yellow, Color.red, Color.green,
472 fr.getFeatureColours().put("Cath", gc);
473 gc.setAutoScaled(false); // this does little other than save a checkbox
475 assertEquals(fr.getColour(sf2), new Color(255, 213, 0));
478 * feature score is below threshold - no colour
480 gc.setAboveThreshold(true);
482 assertNull(fr.getColour(sf2));
485 * feature score is above threshold - no colour
487 gc.setBelowThreshold(true);
489 assertNull(fr.getColour(sf2));
492 * colour by feature attribute value
493 * first with no value held
495 gc = new FeatureColour(Color.red, Color.yellow, Color.red, Color.green,
497 fr.getFeatureColours().put("Cath", gc);
498 gc.setAttributeName("AF");
499 assertEquals(fr.getColour(sf2), Color.green);
501 // with non-numeric attribute value
502 sf2.setValue("AF", "Five");
503 assertEquals(fr.getColour(sf2), Color.green);
505 // with numeric attribute value
506 sf2.setValue("AF", "6");
507 assertEquals(fr.getColour(sf2), expected);
509 // with numeric value outwith threshold
510 gc.setAboveThreshold(true);
511 gc.setThreshold(10f);
512 assertNull(fr.getColour(sf2));
514 // with filter on AF < 4
515 gc.setAboveThreshold(false);
516 assertEquals(fr.getColour(sf2), expected);
517 FeatureMatcherSetI filter = new FeatureMatcherSet();
518 filter.and(FeatureMatcher.byAttribute(Condition.LT, "4.0", "AF"));
519 fr.setFeatureFilter("Cath", filter);
520 assertNull(fr.getColour(sf2));
522 // with filter on 'Consequence contains missense'
523 filter = new FeatureMatcherSet();
524 filter.and(FeatureMatcher.byAttribute(Condition.Contains, "missense",
526 fr.setFeatureFilter("Cath", filter);
527 // if feature has no Consequence attribute, no colour
528 assertNull(fr.getColour(sf2));
529 // if attribute does not match filter, no colour
530 sf2.setValue("Consequence", "Synonymous");
531 assertNull(fr.getColour(sf2));
532 // attribute matches filter
533 sf2.setValue("Consequence", "Missense variant");
534 assertEquals(fr.getColour(sf2), expected);
536 // with filter on CSQ:Feature contains "ENST01234"
537 filter = new FeatureMatcherSet();
538 filter.and(FeatureMatcher.byAttribute(Condition.Matches, "ENST01234",
540 fr.setFeatureFilter("Cath", filter);
541 // if feature has no CSQ data, no colour
542 assertNull(fr.getColour(sf2));
543 // if CSQ data does not include Feature, no colour
544 Map<String, String> csqData = new HashMap<>();
545 csqData.put("BIOTYPE", "Transcript");
546 sf2.setValue("CSQ", csqData);
547 assertNull(fr.getColour(sf2));
548 // if attribute does not match filter, no colour
549 csqData.put("Feature", "ENST9876");
550 assertNull(fr.getColour(sf2));
551 // attribute matches filter
552 csqData.put("Feature", "ENST01234");
553 assertEquals(fr.getColour(sf2), expected);
556 @Test(groups = "Functional")
557 public void testIsVisible()
559 String seqData = ">s1\nMLQGIFPRS\n";
560 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
561 DataSourceType.PASTE);
562 AlignViewportI av = af.getViewport();
563 FeatureRenderer fr = new FeatureRenderer(av);
564 SequenceI seq = av.getAlignment().getSequenceAt(0);
565 SequenceFeature sf = new SequenceFeature("METAL", "Desc", 10, 10, 1f,
567 sf.setValue("AC", "11");
568 sf.setValue("CLIN_SIG", "Likely Pathogenic");
569 seq.addSequenceFeature(sf);
571 assertFalse(fr.isVisible(null));
574 * initial state FeatureRenderer hasn't 'found' feature
575 * and so its feature type has not yet been set visible
577 assertFalse(fr.getDisplayedFeatureCols().containsKey("METAL"));
578 assertFalse(fr.isVisible(sf));
580 fr.findAllFeatures(true);
581 assertTrue(fr.isVisible(sf));
584 * feature group not visible
586 fr.setGroupVisibility("Group", false);
587 assertFalse(fr.isVisible(sf));
588 fr.setGroupVisibility("Group", true);
589 assertTrue(fr.isVisible(sf));
592 * feature score outwith colour threshold (score > 2)
594 FeatureColourI fc = new FeatureColour(null, Color.white, Color.black,
596 fc.setAboveThreshold(true);
598 fr.setColour("METAL", fc);
599 assertFalse(fr.isVisible(sf)); // score 1 is not above threshold 2
600 fc.setBelowThreshold(true);
601 assertTrue(fr.isVisible(sf)); // score 1 is below threshold 2
604 * colour with threshold on attribute AC (value is 11)
606 fc.setAttributeName("AC");
607 assertFalse(fr.isVisible(sf)); // value 11 is not below threshold 2
608 fc.setAboveThreshold(true);
609 assertTrue(fr.isVisible(sf)); // value 11 is above threshold 2
611 fc.setAttributeName("AF"); // attribute AF is absent in sf
612 assertTrue(fr.isVisible(sf)); // feature is not excluded by threshold
614 FeatureMatcherSetI filter = new FeatureMatcherSet();
615 filter.and(FeatureMatcher.byAttribute(Condition.Contains, "pathogenic",
617 fr.setFeatureFilter("METAL", filter);
618 assertTrue(fr.isVisible(sf)); // feature matches filter
619 filter.and(FeatureMatcher.byScore(Condition.LE, "0.4"));
620 assertFalse(fr.isVisible(sf)); // feature doesn't match filter
623 @Test(groups = "Functional")
624 public void testFindComplementFeaturesAtResidue()
628 { "-nonews", "-props", "test/jalview/testProps.jvprops" });
631 String cdsSeq = ">cds\nATGtgtTGGcacTCAgaa";
632 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(cdsSeq,
633 DataSourceType.PASTE);
634 af.showTranslation_actionPerformed(
635 GeneticCodes.getInstance().getStandardCodeTable());
636 af.closeMenuItem_actionPerformed(true);
639 * find the complement frames (ugly)
641 AlignFrame[] frames = Desktop.getAlignFrames();
642 assertEquals(frames.length, 2);
643 AlignViewport av1 = frames[0].getViewport();
644 AlignViewport av2 = frames[1].getViewport();
645 AlignViewport cds = av1.getAlignment().isNucleotide() ? av1 : av2;
646 AlignViewport peptide = cds == av1 ? av2 : av1;
648 assertNotNull(peptide);
651 * add features to CDS at first codon, positions 2-3
653 SequenceI seq1 = cds.getAlignment().getSequenceAt(0);
654 SequenceFeature sf1 = new SequenceFeature("sequence_variant", "G,GT", 2,
656 seq1.addSequenceFeature(sf1);
657 SequenceFeature sf2 = new SequenceFeature("sequence_variant", "C, CA",
659 seq1.addSequenceFeature(sf2);
662 * 'find' mapped features from the peptide position
663 * - first with CDS features _not_ shown on peptide alignment
665 SequenceI seq2 = peptide.getAlignment().getSequenceAt(0);
666 FeatureRenderer frC = new FeatureRenderer(cds);
668 MappedFeatures mf = frC.findComplementFeaturesAtResidue(seq2, 1);
670 assertEquals(mf.features.size(), 2);
671 assertSame(mf.features.get(0), sf1);
672 assertSame(mf.features.get(1), sf2);
675 * add exon feature and verify it is only returned once for a
676 * peptide position, even though it is on all 3 codon positions
678 SequenceFeature sf3 = new SequenceFeature("exon", "exon1", 4, 12,
680 seq1.addSequenceFeature(sf3);
682 mf = frC.findComplementFeaturesAtResidue(seq2, 3);
684 assertEquals(mf.features.size(), 1);
685 assertSame(mf.features.get(0), sf3);