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