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