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