Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 16:11:35 GMT
  2. Package jalview.io

File SequenceAnnotationReportTest.java

 

Code metrics

4
201
13
1
494
322
15
0.07
15.46
13
1.15

Classes

Class Line # Actions
SequenceAnnotationReportTest 49 201 15
1.0100%
 

Contributing tests

This file is covered by 12 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
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.
11    *
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.
16    *
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.
20    */
21    package jalview.io;
22   
23    import static org.testng.AssertJUnit.assertEquals;
24    import static org.testng.AssertJUnit.assertTrue;
25   
26    import java.awt.Color;
27    import java.util.ArrayList;
28    import java.util.List;
29    import java.util.Map;
30   
31    import org.testng.annotations.BeforeClass;
32    import org.testng.annotations.Test;
33   
34    import jalview.api.FeatureColourI;
35    import jalview.datamodel.DBRefEntry;
36    import jalview.datamodel.MappedFeatures;
37    import jalview.datamodel.Mapping;
38    import jalview.datamodel.Sequence;
39    import jalview.datamodel.SequenceFeature;
40    import jalview.datamodel.SequenceI;
41    import jalview.gui.JvOptionPane;
42    import jalview.io.gff.GffConstants;
43    import jalview.renderer.seqfeatures.FeatureRenderer;
44    import jalview.schemes.FeatureColour;
45    import jalview.util.MapList;
46    import jalview.viewmodel.seqfeatures.FeatureRendererModel;
47    import junit.extensions.PA;
48   
 
49    public class SequenceAnnotationReportTest
50    {
51   
 
52  1 toggle @BeforeClass(alwaysRun = true)
53    public void setUpJvOptionPane()
54    {
55  1 JvOptionPane.setInteractiveMode(false);
56  1 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
57    }
58   
 
59  1 toggle @Test(groups = "Functional")
60    public void testAppendFeature_disulfideBond()
61    {
62  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
63  1 StringBuilder sb = new StringBuilder();
64  1 sb.append("123456");
65  1 SequenceFeature sf = new SequenceFeature("disulfide bond", "desc", 1,
66    3, 1.2f, "group");
67   
68    // residuePos == 2 does not match start or end of feature, nothing done:
69  1 sar.appendFeature(sb, 2, null, sf, null, 0);
70  1 assertEquals("123456", sb.toString());
71   
72    // residuePos == 1 matches start of feature, text appended (but no <br>)
73    // feature score is not included
74  1 sar.appendFeature(sb, 1, null, sf, null, 0);
75  1 assertEquals("123456disulfide bond 1:3", sb.toString());
76   
77    // residuePos == 3 matches end of feature, text appended
78    // <br> is prefixed once sb.length() > 6
79  1 sar.appendFeature(sb, 3, null, sf, null, 0);
80  1 assertEquals("123456disulfide bond 1:3<br>disulfide bond 1:3",
81    sb.toString());
82    }
83   
 
84  1 toggle @Test(groups = "Functional")
85    public void testAppendFeatures_longText()
86    {
87  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
88  1 StringBuilder sb = new StringBuilder();
89  1 String longString = "Abcd".repeat(50);
90  1 SequenceFeature sf = new SequenceFeature("sequence", longString, 1, 3,
91    "group");
92   
93  1 sar.appendFeature(sb, 1, null, sf, null, 0);
94  1 assertTrue(sb.length() < 100);
95   
96  1 List<SequenceFeature> sfl = new ArrayList<>();
97  1 sb.setLength(0);
98  1 sfl.add(sf);
99  1 sfl.add(sf);
100  1 sfl.add(sf);
101  1 sfl.add(sf);
102  1 sfl.add(sf);
103  1 sfl.add(sf);
104  1 sfl.add(sf);
105  1 sfl.add(sf);
106  1 sfl.add(sf);
107  1 sfl.add(sf);
108  1 int n = sar.appendFeatures(sb, 1, sfl,
109    new FeatureRenderer(null), 200); // text should terminate before 200 characters
110  1 String s = sb.toString();
111  1 assertTrue(s.length() < 200);
112  1 assertEquals(n, 7); // should be 7 features left over
113   
114    }
115   
 
116  1 toggle @Test(groups = "Functional")
117    public void testAppendFeature_status()
118    {
119  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
120  1 StringBuilder sb = new StringBuilder();
121  1 SequenceFeature sf = new SequenceFeature("METAL", "Fe2-S", 1, 3,
122    Float.NaN, "group");
123  1 sf.setStatus("Confirmed");
124   
125  1 sar.appendFeature(sb, 1, null, sf, null, 0);
126  1 assertEquals("METAL 1 3; Fe2-S; (Confirmed)", sb.toString());
127    }
128   
 
129  1 toggle @Test(groups = "Functional")
130    public void testAppendFeature_withScore()
131    {
132  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
133  1 StringBuilder sb = new StringBuilder();
134  1 SequenceFeature sf = new SequenceFeature("METAL", "Fe2-S", 1, 3, 1.3f,
135    "group");
136   
137  1 FeatureRendererModel fr = new FeatureRenderer(null);
138  1 Map<String, float[][]> minmax = fr.getMinMax();
139  1 sar.appendFeature(sb, 1, fr, sf, null, 0);
140    /*
141    * map has no entry for this feature type - score is not shown:
142    */
143  1 assertEquals("METAL 1 3; Fe2-S", sb.toString());
144   
145    /*
146    * map has entry for this feature type - score is shown:
147    */
148  1 minmax.put("METAL", new float[][] { { 0f, 1f }, null });
149  1 sar.appendFeature(sb, 1, fr, sf, null, 0);
150    // <br> is appended to a buffer > 6 in length
151  1 assertEquals("METAL 1 3; Fe2-S<br>METAL 1 3; Fe2-S Score=1.3",
152    sb.toString());
153   
154    /*
155    * map has min == max for this feature type - score is not shown:
156    */
157  1 minmax.put("METAL", new float[][] { { 2f, 2f }, null });
158  1 sb.setLength(0);
159  1 sar.appendFeature(sb, 1, fr, sf, null, 0);
160  1 assertEquals("METAL 1 3; Fe2-S", sb.toString());
161    }
162   
 
163  1 toggle @Test(groups = "Functional")
164    public void testAppendFeature_noScore()
165    {
166  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
167  1 StringBuilder sb = new StringBuilder();
168  1 SequenceFeature sf = new SequenceFeature("METAL", "Fe2-S", 1, 3,
169    Float.NaN, "group");
170   
171  1 sar.appendFeature(sb, 1, null, sf, null, 0);
172  1 assertEquals("METAL 1 3; Fe2-S", sb.toString());
173    }
174   
175    /**
176    * A specific attribute value is included if it is used to colour the feature
177    */
 
178  1 toggle @Test(groups = "Functional")
179    public void testAppendFeature_colouredByAttribute()
180    {
181  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
182  1 StringBuilder sb = new StringBuilder();
183  1 SequenceFeature sf = new SequenceFeature("METAL", "Fe2-S", 1, 3,
184    Float.NaN, "group");
185  1 sf.setValue("clinical_significance", "Benign");
186   
187    /*
188    * first with no colour by attribute
189    */
190  1 FeatureRendererModel fr = new FeatureRenderer(null);
191  1 sar.appendFeature(sb, 1, fr, sf, null, 0);
192  1 assertEquals("METAL 1 3; Fe2-S", sb.toString());
193   
194    /*
195    * then with colour by an attribute the feature lacks
196    */
197  1 FeatureColourI fc = new FeatureColour(null, Color.white, Color.black,
198    null, 5, 10);
199  1 fc.setAttributeName("Pfam");
200  1 fr.setColour("METAL", fc);
201  1 sb.setLength(0);
202  1 sar.appendFeature(sb, 1, fr, sf, null, 0);
203  1 assertEquals("METAL 1 3; Fe2-S", sb.toString()); // no change
204   
205    /*
206    * then with colour by an attribute the feature has
207    */
208  1 fc.setAttributeName("clinical_significance");
209  1 sb.setLength(0);
210  1 sar.appendFeature(sb, 1, fr, sf, null, 0);
211  1 assertEquals("METAL 1 3; Fe2-S; clinical_significance=Benign",
212    sb.toString());
213    }
214   
 
215  1 toggle @Test(groups = "Functional")
216    public void testAppendFeature_withScoreStatusAttribute()
217    {
218  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
219  1 StringBuilder sb = new StringBuilder();
220  1 SequenceFeature sf = new SequenceFeature("METAL", "Fe2-S", 1, 3, 1.3f,
221    "group");
222  1 sf.setStatus("Confirmed");
223  1 sf.setValue("clinical_significance", "Benign");
224   
225  1 FeatureRendererModel fr = new FeatureRenderer(null);
226  1 Map<String, float[][]> minmax = fr.getMinMax();
227  1 FeatureColourI fc = new FeatureColour(null, Color.white, Color.blue,
228    null, 12, 22);
229  1 fc.setAttributeName("clinical_significance");
230  1 fr.setColour("METAL", fc);
231  1 minmax.put("METAL", new float[][] { { 0f, 1f }, null });
232  1 sar.appendFeature(sb, 1, fr, sf, null, 0);
233   
234  1 assertEquals(
235    "METAL 1 3; Fe2-S Score=1.3; (Confirmed); clinical_significance=Benign",
236    sb.toString());
237    }
238   
 
239  1 toggle @Test(groups = "Functional")
240    public void testAppendFeature_DescEqualsType()
241    {
242  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
243  1 StringBuilder sb = new StringBuilder();
244  1 SequenceFeature sf = new SequenceFeature("METAL", "METAL", 1, 3,
245    Float.NaN, "group");
246   
247    // description is not included if it duplicates type:
248  1 sar.appendFeature(sb, 1, null, sf, null, 0);
249  1 assertEquals("METAL 1 3", sb.toString());
250   
251  1 sb.setLength(0);
252  1 sf.setDescription("Metal");
253    // test is case-sensitive:
254  1 sar.appendFeature(sb, 1, null, sf, null, 0);
255  1 assertEquals("METAL 1 3; Metal", sb.toString());
256    }
257   
 
258  1 toggle @Test(groups = "Functional")
259    public void testAppendFeature_stripHtml()
260    {
261  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
262  1 StringBuilder sb = new StringBuilder();
263  1 SequenceFeature sf = new SequenceFeature("METAL",
264    "<html><body>hello<em>world</em></body></html>", 1, 3,
265    Float.NaN, "group");
266   
267  1 sar.appendFeature(sb, 1, null, sf, null, 0);
268    // !! strips off </body> but not <body> ??
269  1 assertEquals("METAL 1 3; <body>hello<em>world</em>", sb.toString());
270   
271  1 sb.setLength(0);
272  1 sf.setDescription("<br>&kHD>6");
273  1 sar.appendFeature(sb, 1, null, sf, null, 0);
274    // if no <html> tag, html-encodes > and < (only):
275  1 assertEquals("METAL 1 3; &lt;br&gt;&kHD&gt;6", sb.toString());
276    }
277   
 
278  1 toggle @Test(groups = "Functional")
279    public void testCreateSequenceAnnotationReport()
280    {
281  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
282  1 StringBuilder sb = new StringBuilder();
283   
284  1 SequenceI seq = new Sequence("s1", "MAKLKRFQSSTLL");
285  1 seq.setDescription("SeqDesc");
286   
287    /*
288    * positional features are ignored
289    */
290  1 seq.addSequenceFeature(
291    new SequenceFeature("Domain", "Ferredoxin", 5, 10, 1f, null));
292  1 sar.createSequenceAnnotationReport(sb, seq, true, true, null);
293  1 assertEquals("<i>SeqDesc\n" + "\n" + "</i>", sb.toString());
294   
295    /*
296    * non-positional feature
297    */
298  1 seq.addSequenceFeature(
299    new SequenceFeature("Type1", "Nonpos", 0, 0, 1f, null));
300  1 sb.setLength(0);
301  1 sar.createSequenceAnnotationReport(sb, seq, true, true, null);
302  1 String expected = "<i>SeqDesc\n" + "\n"
303    + "<br>Type1 ; Nonpos Score=1.0</i>";
304  1 assertEquals(expected, sb.toString());
305   
306    /*
307    * non-positional features not wanted
308    */
309  1 sb.setLength(0);
310  1 sar.createSequenceAnnotationReport(sb, seq, true, false, null);
311  1 assertEquals("<i>SeqDesc\n\n</i>", sb.toString());
312   
313    /*
314    * add non-pos feature with score inside min-max range for feature type
315    * minmax holds { [positionalMin, positionalMax], [nonPosMin, nonPosMax] }
316    * score is only appended for positional features so ignored here!
317    * minMax are not recorded for non-positional features
318    */
319  1 seq.addSequenceFeature(
320    new SequenceFeature("Metal", "Desc", 0, 0, 5f, null));
321   
322  1 FeatureRendererModel fr = new FeatureRenderer(null);
323  1 Map<String, float[][]> minmax = fr.getMinMax();
324  1 minmax.put("Metal", new float[][] { null, new float[] { 2f, 5f } });
325   
326  1 sb.setLength(0);
327  1 sar.createSequenceAnnotationReport(sb, seq, true, true, fr);
328  1 expected = "<i>SeqDesc\n" + "\n"
329    + "<br>Metal ; Desc<br>Type1 ; Nonpos</i>";
330  1 assertEquals(expected, sb.toString());
331   
332    /*
333    * 'linkonly' features are ignored; this is obsolete, as linkonly
334    * is only set by DasSequenceFetcher, and DAS is history
335    */
336  1 SequenceFeature sf = new SequenceFeature("Metal", "Desc", 0, 0, 5f,
337    null);
338  1 sf.setValue("linkonly", Boolean.TRUE);
339  1 seq.addSequenceFeature(sf);
340  1 sb.setLength(0);
341  1 sar.createSequenceAnnotationReport(sb, seq, true, true, fr);
342  1 assertEquals(expected, sb.toString()); // unchanged!
343   
344    /*
345    * 'clinical_significance' attribute is only included in description
346    * when used for feature colouring
347    */
348  1 SequenceFeature sf2 = new SequenceFeature("Variant", "Havana", 0, 0, 5f,
349    null);
350  1 sf2.setValue(GffConstants.CLINICAL_SIGNIFICANCE, "benign");
351  1 seq.addSequenceFeature(sf2);
352  1 sb.setLength(0);
353  1 sar.createSequenceAnnotationReport(sb, seq, true, true, fr);
354  1 expected = "<i>SeqDesc\n" + "\n"
355    + "<br>Metal ; Desc<br>Type1 ; Nonpos<br>Variant ; Havana</i>";
356  1 assertEquals(expected, sb.toString());
357   
358    /*
359    * add dbrefs
360    */
361  1 seq.addDBRef(new DBRefEntry("PDB", "0", "3iu1"));
362  1 seq.addDBRef(new DBRefEntry("Uniprot", "1", "P30419"));
363   
364    // with showDbRefs = false
365  1 sb.setLength(0);
366  1 sar.createSequenceAnnotationReport(sb, seq, false, true, fr);
367  1 assertEquals(expected, sb.toString()); // unchanged
368   
369    // with showDbRefs = true, colour Variant features by clinical_significance
370  1 sb.setLength(0);
371  1 FeatureColourI fc = new FeatureColour(null, Color.green, Color.pink,
372    null, 2, 3);
373  1 fc.setAttributeName("clinical_significance");
374  1 fr.setColour("Variant", fc);
375  1 sar.createSequenceAnnotationReport(sb, seq, true, true, fr);
376  1 expected = "<i>SeqDesc\n" + "<br>\n" + "UNIPROT P30419<br>\n"
377    + "PDB 3iu1\n"
378    + "<br>Metal ; Desc<br>Type1 ; Nonpos<br>Variant ; Havana; clinical_significance=benign</i>";
379  1 assertEquals(expected, sb.toString());
380    // with showNonPositionalFeatures = false
381  1 sb.setLength(0);
382  1 sar.createSequenceAnnotationReport(sb, seq, true, false, fr);
383  1 expected = "<i>SeqDesc\n" + "<br>\n" + "UNIPROT P30419<br>\n"
384    + "PDB 3iu1\n" + "</i>";
385  1 assertEquals(expected, sb.toString());
386   
387    /*
388    * long feature description is truncated with ellipsis
389    */
390  1 sb.setLength(0);
391  1 sf2.setDescription(
392    "This is a very long description which should be truncated");
393  1 sar.createSequenceAnnotationReport(sb, seq, false, true, fr);
394  1 expected = "<i>SeqDesc\n" + "\n"
395    + "<br>Metal ; Desc<br>Type1 ; Nonpos<br>Variant ; This is a very long description which sh...; clinical_significance=benign</i>";
396  1 assertEquals(expected, sb.toString());
397   
398    // see other tests for treatment of status and html
399    }
400   
401    /**
402    * Test that exercises an abbreviated sequence details report, with ellipsis
403    * where there are more than 40 different sources, or more than 4 dbrefs for a
404    * single source
405    */
 
406  1 toggle @Test(groups = "Functional")
407    public void testCreateSequenceAnnotationReport_withEllipsis()
408    {
409  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
410  1 StringBuilder sb = new StringBuilder();
411   
412  1 SequenceI seq = new Sequence("s1", "ABC");
413   
414  1 int maxSources = (int) PA.getValue(sar, "MAX_SOURCES");
415  7 for (int i = 0; i <= maxSources; i++)
416    {
417  6 seq.addDBRef(new DBRefEntry("PDB" + i, "0", "3iu1"));
418    }
419   
420  1 int maxRefs = (int) PA.getValue(sar, "MAX_REFS_PER_SOURCE");
421  6 for (int i = 0; i <= maxRefs; i++)
422    {
423  5 seq.addDBRef(new DBRefEntry("Uniprot", "0", "P3041" + i));
424    }
425   
426  1 sar.createSequenceAnnotationReport(sb, seq, true, true, null, true);
427  1 String report = sb.toString();
428  1 assertTrue(report.startsWith("<i>\n" + "<br>\n" + "UNIPROT P30410,\n"
429    + " P30411,\n" + " P30412,\n" + " P30413,...<br>\n"
430    + "PDB0 3iu1<br>\n" + "PDB1 3iu1<br>"));
431  1 assertTrue(report.endsWith("PDB3 3iu1<br>\n" + "PDB4,...<br>\n"
432    + "(Output Sequence Details to list all database references)\n"
433    + "</i>"));
434    }
435   
436    /**
437    * Test adding a linked feature to the tooltip
438    */
 
439  1 toggle @Test(groups = "Functional")
440    public void testAppendFeature_virtualFeature()
441    {
442    /*
443    * map CDS to peptide sequence
444    */
445  1 SequenceI cds = new Sequence("Cds/101-121", "CCTttgAGAtttCAAatgGAT");
446  1 SequenceI peptide = new Sequence("Peptide/8-14", "PLRFQMD");
447  1 MapList map = new MapList(new int[] { 101, 118 }, new int[] { 8, 13 },
448    3, 1);
449  1 Mapping mapping = new Mapping(peptide, map);
450   
451    /*
452    * assume variant feature found at CDS position 106 G>C
453    */
454  1 List<SequenceFeature> features = new ArrayList<>();
455    // vary ttg (Leu) to ttc (Phe)
456  1 SequenceFeature sf = new SequenceFeature("variant", "G,C", 106, 106,
457    Float.NaN, null);
458  1 features.add(sf);
459  1 MappedFeatures mf = new MappedFeatures(mapping, cds, 9, 'L', features);
460   
461  1 StringBuilder sb = new StringBuilder();
462  1 SequenceAnnotationReport sar = new SequenceAnnotationReport(false);
463  1 sar.appendFeature(sb, 1, null, sf, mf, 0);
464   
465    /*
466    * linked feature shown in tooltip in protein coordinates
467    */
468  1 assertEquals("variant 9; G,C", sb.toString());
469   
470    /*
471    * adding "alleles" attribute to variant allows peptide consequence
472    * to be calculated and added to the tooltip
473    */
474  1 sf.setValue("alleles", "G,C");
475  1 sb = new StringBuilder();
476  1 sar.appendFeature(sb, 1, null, sf, mf, 0);
477  1 assertEquals("variant 9; G,C p.Leu9Phe", sb.toString());
478   
479    /*
480    * now a virtual peptide feature on CDS
481    * feature at 11-12 on peptide maps to 110-115 on CDS
482    * here we test for tooltip at 113 (t)
483    */
484  1 SequenceFeature sf2 = new SequenceFeature("metal", "Fe", 11, 12, 2.3f,
485    "Uniprot");
486  1 features.clear();
487  1 features.add(sf2);
488  1 mapping = new Mapping(peptide, map);
489  1 mf = new MappedFeatures(mapping, peptide, 113, 't', features);
490  1 sb = new StringBuilder();
491  1 sar.appendFeature(sb, 1, null, sf2, mf, 0);
492  1 assertEquals("metal 110 115; Fe Score=2.3", sb.toString());
493    }
494    }