JAL-3438 spotless for 2.11.2.0
[jalview.git] / test / jalview / datamodel / ResidueCountTest.java
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.datamodel;
22
23 import java.util.Locale;
24
25 import static org.testng.Assert.assertEquals;
26 import static org.testng.Assert.assertFalse;
27 import static org.testng.Assert.assertTrue;
28
29 import jalview.datamodel.ResidueCount.SymbolCounts;
30 import jalview.gui.JvOptionPane;
31
32 import org.junit.Assert;
33 import org.testng.annotations.BeforeClass;
34 import org.testng.annotations.Test;
35
36 public class ResidueCountTest
37 {
38
39   @BeforeClass(alwaysRun = true)
40   public void setUpJvOptionPane()
41   {
42     JvOptionPane.setInteractiveMode(false);
43     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
44   }
45
46   /**
47    * Test a mix of add and put for nucleotide counting
48    */
49   @Test(groups = "Functional")
50   public void test_countNucleotide()
51   {
52     ResidueCount rc = new ResidueCount(true);
53     assertEquals(rc.getCount('A'), 0);
54     assertEquals(rc.getGapCount(), 0);
55     // add then add
56     assertEquals(rc.add('A'), 1);
57     assertEquals(rc.add('a'), 2);
58     // put then add
59     rc.put('g', 3);
60     assertEquals(rc.add('G'), 4);
61     // add then put
62     assertEquals(rc.add('c'), 1);
63     rc.put('C', 4);
64     assertEquals(rc.add('N'), 1);
65
66     assertEquals(rc.getCount('a'), 2);
67     assertEquals(rc.getCount('A'), 2);
68     assertEquals(rc.getCount('G'), 4);
69     assertEquals(rc.getCount('c'), 4);
70     assertEquals(rc.getCount('T'), 0); // never seen
71     assertEquals(rc.getCount('N'), 1);
72     assertEquals(rc.getCount('?'), 0);
73     assertEquals(rc.getCount('-'), 0);
74
75     assertFalse(rc.isCountingInts());
76     assertFalse(rc.isUsingOtherData());
77   }
78
79   /**
80    * Test adding to gap count (either using addGap or add)
81    */
82   @Test(groups = "Functional")
83   public void testAddGap()
84   {
85     ResidueCount rc = new ResidueCount(true);
86     rc.addGap();
87     rc.add('-');
88     rc.add('.');
89     rc.add(' ');
90
91     assertEquals(rc.getGapCount(), 4);
92     assertEquals(rc.getCount(' '), 4);
93     assertEquals(rc.getCount('-'), 4);
94     assertEquals(rc.getCount('.'), 4);
95     assertFalse(rc.isUsingOtherData());
96     assertFalse(rc.isCountingInts());
97
98     rc.set(ResidueCount.GAP_COUNT, Short.MAX_VALUE - 2);
99     assertEquals(rc.getGapCount(), Short.MAX_VALUE - 2);
100     assertFalse(rc.isCountingInts());
101     rc.addGap();
102     assertEquals(rc.getGapCount(), Short.MAX_VALUE - 1);
103     assertFalse(rc.isCountingInts());
104     rc.addGap();
105     assertEquals(rc.getGapCount(), Short.MAX_VALUE);
106     rc.addGap();
107     assertTrue(rc.isCountingInts());
108     assertEquals(rc.getGapCount(), Short.MAX_VALUE + 1);
109   }
110
111   @Test(groups = "Functional")
112   public void testOverflow()
113   {
114     /*
115      * overflow from add
116      */
117     ResidueCount rc = new ResidueCount(true);
118     rc.addGap();
119     rc.put('A', Short.MAX_VALUE - 1);
120     assertFalse(rc.isCountingInts());
121     rc.add('A');
122     assertFalse(rc.isCountingInts());
123     rc.add('A');
124     assertTrue(rc.isCountingInts());
125     assertEquals(rc.getCount('a'), Short.MAX_VALUE + 1);
126     rc.add('A');
127     assertTrue(rc.isCountingInts());
128     assertEquals(rc.getCount('a'), Short.MAX_VALUE + 2);
129     assertEquals(rc.getGapCount(), 1);
130     rc.addGap();
131     assertEquals(rc.getGapCount(), 2);
132
133     /*
134      * overflow from put
135      */
136     rc = new ResidueCount(true);
137     rc.put('G', Short.MAX_VALUE + 1);
138     assertTrue(rc.isCountingInts());
139     assertEquals(rc.getCount('g'), Short.MAX_VALUE + 1);
140     rc.put('G', 1);
141     assertTrue(rc.isCountingInts());
142     assertEquals(rc.getCount('g'), 1);
143
144     /*
145      * underflow from put
146      */
147     rc = new ResidueCount(true);
148     rc.put('G', Short.MIN_VALUE - 1);
149     assertTrue(rc.isCountingInts());
150     assertEquals(rc.getCount('g'), Short.MIN_VALUE - 1);
151   }
152
153   /**
154    * Test a mix of add and put for peptide counting
155    */
156   @Test(groups = "Functional")
157   public void test_countPeptide()
158   {
159     ResidueCount rc = new ResidueCount(false);
160     rc.put('q', 4);
161     rc.add('Q');
162     rc.add('X');
163     rc.add('x');
164     rc.add('W');
165     rc.put('w', 7);
166     rc.put('m', 12);
167     rc.put('M', 13);
168
169     assertEquals(rc.getCount('q'), 5);
170     assertEquals(rc.getCount('X'), 2);
171     assertEquals(rc.getCount('W'), 7);
172     assertEquals(rc.getCount('m'), 13);
173     assertEquals(rc.getCount('G'), 0);
174     assertEquals(rc.getCount('-'), 0);
175
176     assertFalse(rc.isCountingInts());
177     assertFalse(rc.isUsingOtherData());
178   }
179
180   @Test(groups = "Functional")
181   public void test_unexpectedPeptide()
182   {
183     ResidueCount rc = new ResidueCount(false);
184     // expected characters (upper or lower case):
185     String aas = "ACDEFGHIKLMNPQRSTVWXY";
186     String lower = aas.toLowerCase(Locale.ROOT);
187     for (int i = 0; i < aas.length(); i++)
188     {
189       rc.put(aas.charAt(i), i);
190       rc.add(lower.charAt(i));
191     }
192     for (int i = 0; i < aas.length(); i++)
193     {
194       assertEquals(rc.getCount(aas.charAt(i)), i + 1);
195     }
196     assertFalse(rc.isUsingOtherData());
197
198     rc.put('J', 4);
199     assertTrue(rc.isUsingOtherData());
200     assertEquals(rc.getCount('J'), 4);
201     rc.add('j');
202     assertEquals(rc.getCount('J'), 5);
203   }
204
205   @Test(groups = "Functional")
206   public void test_unexpectedNucleotide()
207   {
208     ResidueCount rc = new ResidueCount(true);
209     // expected characters (upper or lower case):
210     String nucs = "ACGTUN";
211     String lower = nucs.toLowerCase(Locale.ROOT);
212     for (int i = 0; i < nucs.length(); i++)
213     {
214       rc.put(nucs.charAt(i), i);
215       rc.add(lower.charAt(i));
216     }
217     for (int i = 0; i < nucs.length(); i++)
218     {
219       assertEquals(rc.getCount(nucs.charAt(i)), i + 1);
220     }
221     assertFalse(rc.isUsingOtherData());
222
223     rc.add('J');
224     assertTrue(rc.isUsingOtherData());
225   }
226
227   @Test(groups = "Functional")
228   public void testGetModalCount()
229   {
230     ResidueCount rc = new ResidueCount(true);
231     rc.add('c');
232     rc.add('g');
233     rc.add('c');
234     assertEquals(rc.getModalCount(), 2);
235
236     // modal count is in the 'short overflow' counts
237     rc = new ResidueCount();
238     rc.add('c');
239     rc.put('g', Short.MAX_VALUE);
240     rc.add('G');
241     assertEquals(rc.getModalCount(), Short.MAX_VALUE + 1);
242
243     // modal count is in the 'other data' counts
244     rc = new ResidueCount(false);
245     rc.add('Q');
246     rc.add('{');
247     rc.add('{');
248     assertEquals(rc.getModalCount(), 2);
249
250     // verify modal count excludes gap
251     rc = new ResidueCount();
252     rc.add('Q');
253     rc.add('P');
254     rc.add('Q');
255     rc.addGap();
256     rc.addGap();
257     rc.addGap();
258     assertEquals(rc.getModalCount(), 2);
259   }
260
261   @Test(groups = "Functional")
262   public void testGetResiduesForCount()
263   {
264     ResidueCount rc = new ResidueCount(true);
265     rc.add('c');
266     rc.add('g');
267     rc.add('c');
268     assertEquals(rc.getResiduesForCount(2), "C");
269     assertEquals(rc.getResiduesForCount(1), "G");
270     assertEquals(rc.getResiduesForCount(3), "");
271     assertEquals(rc.getResiduesForCount(0), "");
272     assertEquals(rc.getResiduesForCount(-1), "");
273
274     // modal count is in the 'short overflow' counts
275     rc = new ResidueCount(true);
276     rc.add('c');
277     rc.put('g', Short.MAX_VALUE);
278     rc.add('G');
279     assertEquals(rc.getResiduesForCount(Short.MAX_VALUE + 1), "G");
280     assertEquals(rc.getResiduesForCount(1), "C");
281
282     // peptide modal count is in the 'short overflow' counts
283     rc = new ResidueCount(false);
284     rc.add('c');
285     rc.put('p', Short.MAX_VALUE);
286     rc.add('P');
287     assertEquals(rc.getResiduesForCount(Short.MAX_VALUE + 1), "P");
288     assertEquals(rc.getResiduesForCount(1), "C");
289
290     // modal count is in the 'other data' counts
291     rc = new ResidueCount();
292     rc.add('Q');
293     rc.add('{');
294     rc.add('{');
295     assertEquals(rc.getResiduesForCount(1), "Q");
296     assertEquals(rc.getResiduesForCount(2), "{");
297
298     // residues share modal count
299     rc = new ResidueCount();
300     rc.add('G');
301     rc.add('G');
302     rc.add('c');
303     rc.add('C');
304     rc.add('U');
305     assertEquals(rc.getResiduesForCount(1), "U");
306     assertEquals(rc.getResiduesForCount(2), "CG");
307
308     // expected and unexpected symbols share modal count
309     rc = new ResidueCount();
310     rc.add('G');
311     rc.add('t');
312     rc.add('[');
313     rc.add('[');
314     rc.add('t');
315     rc.add('G');
316     rc.add('c');
317     rc.add('C');
318     rc.add('U');
319     assertEquals(rc.getResiduesForCount(1), "U");
320     assertEquals(rc.getResiduesForCount(2), "CGT[");
321   }
322
323   @Test(groups = "Functional")
324   public void testGetSymbolCounts_nucleotide()
325   {
326     ResidueCount rc = new ResidueCount(true);
327     rc.add('g');
328     rc.add('c');
329     rc.add('G');
330     rc.add('J'); // 'otherData'
331     rc.add('g');
332     rc.add('N');
333     rc.put('[', 0); // 'otherdata'
334
335     SymbolCounts sc = rc.getSymbolCounts();
336     Assert.assertArrayEquals(new char[] { 'C', 'G', 'N', 'J', '[' },
337             sc.symbols);
338     Assert.assertArrayEquals(new int[] { 1, 3, 1, 1, 0 }, sc.values);
339
340     // now with overflow to int counts
341     rc.put('U', Short.MAX_VALUE);
342     rc.add('u');
343     sc = rc.getSymbolCounts();
344     Assert.assertArrayEquals(new char[] { 'C', 'G', 'N', 'U', 'J', '[' },
345             sc.symbols);
346     Assert.assertArrayEquals(new int[] { 1, 3, 1, 32768, 1, 0 }, sc.values);
347   }
348
349   @Test(groups = "Functional")
350   public void testGetSymbolCounts_peptide()
351   {
352     ResidueCount rc = new ResidueCount(false);
353     rc.add('W');
354     rc.add('q');
355     rc.add('W');
356     rc.add('Z'); // 'otherData'
357     rc.add('w');
358     rc.add('L');
359
360     SymbolCounts sc = rc.getSymbolCounts();
361     Assert.assertArrayEquals(new char[] { 'L', 'Q', 'W', 'Z' }, sc.symbols);
362     Assert.assertArrayEquals(new int[] { 1, 1, 3, 1 }, sc.values);
363
364     // now with overflow to int counts
365     rc.put('W', Short.MAX_VALUE);
366     rc.add('W');
367     sc = rc.getSymbolCounts();
368     Assert.assertArrayEquals(new char[] { 'L', 'Q', 'W', 'Z' }, sc.symbols);
369     Assert.assertArrayEquals(new int[] { 1, 1, 32768, 1 }, sc.values);
370   }
371
372   @Test(groups = "Functional")
373   public void testToString()
374   {
375     ResidueCount rc = new ResidueCount();
376     rc.add('q');
377     rc.add('c');
378     rc.add('Q');
379     assertEquals(rc.toString(), "[ C:1 Q:2 ]");
380
381     // add 'other data'
382     rc.add('{');
383     assertEquals(rc.toString(), "[ C:1 Q:2 {:1 ]");
384
385     // switch from short to int counting:
386     rc.put('G', Short.MAX_VALUE);
387     rc.add('g');
388     assertEquals(rc.toString(), "[ C:1 G:32768 Q:2 {:1 ]");
389   }
390
391   @Test(groups = "Functional")
392   public void testGetTooltip()
393   {
394     ResidueCount rc = new ResidueCount();
395
396     // no counts!
397     assertEquals(rc.getTooltip(20, 1), "");
398
399     /*
400      * count 7 C, 6 K, 7 Q, 10 P, 9 W, 1 F (total 40)
401      */
402     for (int i = 0; i < 7; i++)
403     {
404       rc.add('c');
405       rc.add('q');
406     }
407     for (int i = 0; i < 10; i++)
408     {
409       rc.add('p');
410     }
411     for (int i = 0; i < 9; i++)
412     {
413       rc.add('W');
414     }
415     for (int i = 0; i < 6; i++)
416     {
417       rc.add('K');
418     }
419     rc.add('F');
420
421     /*
422      * percentages are rounded (0.5 rounded up)
423      * 10/40 9/40 7/40 6/40 1/40
424      */
425     assertEquals(rc.getTooltip(40, 0),
426             "P 25%; W 23%; C 18%; Q 18%; K 15%; F 3%");
427
428     rc.add('Q');
429     /*
430      * 10/30 9/30 8/30 7/30 6/30 1/30
431      */
432     assertEquals(rc.getTooltip(30, 1),
433             "P 33.3%; W 30.0%; Q 26.7%; C 23.3%; K 20.0%; F 3.3%");
434   }
435
436   @Test(groups = "Functional")
437   public void testPut()
438   {
439     ResidueCount rc = new ResidueCount();
440     rc.put('q', 3);
441     assertEquals(rc.getCount('Q'), 3);
442     rc.put(' ', 4);
443     assertEquals(rc.getGapCount(), 4);
444     rc.put('.', 5);
445     assertEquals(rc.getGapCount(), 5);
446     rc.put('-', 6);
447     assertEquals(rc.getGapCount(), 6);
448
449     rc.put('?', 5);
450     assertEquals(rc.getCount('?'), 5);
451     rc.put('?', 6);
452     rc.put('!', 7);
453     assertEquals(rc.getCount('?'), 6);
454     assertEquals(rc.getCount('!'), 7);
455   }
456 }