JAL-2403 JAL-2416 pairwise score unexpected symbols as minimum matrix
[jalview.git] / test / jalview / io / ScoreMatrixFileTest.java
1 package jalview.io;
2
3 import static org.testng.Assert.assertEquals;
4 import static org.testng.Assert.assertFalse;
5 import static org.testng.Assert.assertNotNull;
6 import static org.testng.Assert.assertNull;
7 import static org.testng.Assert.assertTrue;
8 import static org.testng.Assert.fail;
9
10 import jalview.analysis.scoremodels.ScoreMatrix;
11 import jalview.analysis.scoremodels.ScoreModels;
12
13 import java.io.IOException;
14 import java.net.MalformedURLException;
15
16 import org.testng.annotations.Test;
17
18 public class ScoreMatrixFileTest
19 {
20
21   /**
22    * Test a successful parse of a (small) score matrix file
23    * 
24    * @throws IOException
25    * @throws MalformedURLException
26    */
27   @Test(groups = "Functional")
28   public void testParseMatrix_ncbiMixedDelimiters()
29           throws MalformedURLException,
30           IOException
31   {
32     /*
33      * some messy but valid input data, with comma, space
34      * or tab (or combinations) as score value delimiters
35      * this example includes 'guide' symbols on score rows
36      */
37     String data = "ScoreMatrix MyTest (example)\n" + "A\tT\tU\tt\tx\t-\n"
38             + "A,1.1,1.2,1.3,1.4, 1.5, 1.6\n"
39             + "T,2.1 2.2 2.3 2.4 2.5 2.6\n"
40             + "U\t3.1\t3.2\t3.3\t3.4\t3.5\t3.6\t\n"
41             + "t, 5.1,5.3,5.3,5.4,5.5, 5.6\n"
42             + "x\t6.1, 6.2 6.3 6.4 6.5 6.6\n"
43             + "-, \t7.1\t7.2 7.3, 7.4, 7.5\t,7.6\n";
44     FileParse fp = new FileParse(data, DataSourceType.PASTE);
45     ScoreMatrixFile parser = new ScoreMatrixFile(fp);
46     ScoreMatrix sm = parser.parseMatrix();
47
48     assertNotNull(sm);
49     assertEquals(sm.getName(), "MyTest (example)");
50     assertEquals(sm.getSize(), 6);
51     assertNull(sm.getDescription());
52     assertTrue(sm.isDNA());
53     assertFalse(sm.isProtein());
54     assertEquals(sm.getMinimumScore(), 1.1f);
55     assertEquals(sm.getPairwiseScore('A', 'A'), 1.1f);
56     assertEquals(sm.getPairwiseScore('A', 'T'), 1.2f);
57     assertEquals(sm.getPairwiseScore('a', 'T'), 1.2f); // A/a equivalent
58     assertEquals(sm.getPairwiseScore('A', 't'), 1.4f); // T/t not equivalent
59     assertEquals(sm.getPairwiseScore('a', 't'), 1.4f);
60     assertEquals(sm.getPairwiseScore('U', 'x'), 3.5f);
61     assertEquals(sm.getPairwiseScore('u', 'x'), 3.5f);
62     // X (upper) and '.' unmapped - get minimum score
63     assertEquals(sm.getPairwiseScore('U', 'X'), 1.1f);
64     assertEquals(sm.getPairwiseScore('A', '.'), 1.1f);
65     assertEquals(sm.getPairwiseScore('-', '-'), 7.6f);
66     assertEquals(sm.getPairwiseScore('A', (char) 128), 0f); // out of range
67   }
68
69   @Test(groups = "Functional")
70   public void testParseMatrix_headerMissing()
71   {
72     String data;
73
74     data = "X Y\n1 2\n3 4\n";
75     try
76     {
77       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
78               .parseMatrix();
79       fail("expected exception");
80     } catch (IOException e)
81     {
82       assertEquals(e.getMessage(),
83               "Format error: 'ScoreMatrix <name>' should be the first non-comment line");
84     }
85   }
86
87   @Test(groups = "Functional")
88   public void testParseMatrix_ncbiNotEnoughRows()
89   {
90     String data = "ScoreMatrix MyTest\nX Y Z\n1 2 3\n4 5 6\n";
91     try
92     {
93       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
94               .parseMatrix();
95       fail("expected exception");
96     } catch (IOException e)
97     {
98       assertEquals(e.getMessage(),
99               "Expected 3 rows of score data in score matrix but only found 2");
100     }
101   }
102
103   @Test(groups = "Functional")
104   public void testParseMatrix_ncbiNotEnoughColumns()
105   {
106     String data = "ScoreMatrix MyTest\nX Y Z\n1 2 3\n4 5\n7 8 9\n";
107     try
108     {
109       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
110               .parseMatrix();
111       fail("expected exception");
112     } catch (IOException e)
113     {
114       assertEquals(e.getMessage(),
115               "Expected 3 scores at line 4: '4 5' but found 2");
116     }
117   }
118
119   @Test(groups = "Functional")
120   public void testParseMatrix_ncbiTooManyColumns()
121   {
122     /*
123      * with two too many columns:
124      */
125     String data = "ScoreMatrix MyTest\nX\tY\tZ\n1 2 3\n4 5 6 7\n8 9 10\n";
126     try
127     {
128       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
129               .parseMatrix();
130       fail("expected exception");
131     } catch (IOException e)
132     {
133       assertEquals(e.getMessage(),
134               "Expected 3 scores at line 4: '4 5 6 7' but found 4");
135     }
136
137     /*
138      * with guide character and one too many columns:
139      */
140     data = "ScoreMatrix MyTest\nX Y\nX 1 2\nY 3 4 5\n";
141     try
142     {
143       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
144               .parseMatrix();
145       fail("expected exception");
146     } catch (IOException e)
147     {
148       assertEquals(e.getMessage(),
149               "Expected 2 scores at line 4: 'Y 3 4 5' but found 3");
150     }
151
152     /*
153      * with no guide character and one too many columns
154      */
155     data = "ScoreMatrix MyTest\nX Y\n1 2\n3 4 5\n";
156     try
157     {
158       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
159               .parseMatrix();
160       fail("expected exception");
161     } catch (IOException e)
162     {
163       assertEquals(e.getMessage(),
164               "Expected 2 scores at line 4: '3 4 5' but found 3");
165     }
166   }
167
168   @Test(groups = "Functional")
169   public void testParseMatrix_ncbiTooManyRows()
170   {
171     String data = "ScoreMatrix MyTest\n\tX\tY\tZ\n1 2 3\n4 5 6\n7 8 9\n10 11 12\n";
172     try
173     {
174       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
175               .parseMatrix();
176       fail("expected exception");
177     } catch (IOException e)
178     {
179       assertEquals(e.getMessage(),
180               "Unexpected extra input line in score model file: '10 11 12'");
181     }
182   }
183
184   @Test(groups = "Functional")
185   public void testParseMatrix_ncbiBadDelimiter()
186   {
187     String data = "ScoreMatrix MyTest\n X Y Z\n1|2|3\n4|5|6\n";
188     try
189     {
190       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
191               .parseMatrix();
192       fail("expected exception");
193     } catch (IOException e)
194     {
195       assertEquals(e.getMessage(),
196               "Invalid score value '1|2|3' at line 3 column 0");
197     }
198   }
199
200   @Test(groups = "Functional")
201   public void testParseMatrix_ncbiBadFloat()
202   {
203     String data = "ScoreMatrix MyTest\n\tX\tY\tZ\n1 2 3\n4 five 6\n7 8 9\n";
204     try
205     {
206       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
207               .parseMatrix();
208       fail("expected exception");
209     } catch (IOException e)
210     {
211       assertEquals(e.getMessage(),
212               "Invalid score value 'five' at line 4 column 1");
213     }
214   }
215
216   @Test(groups = "Functional")
217   public void testParseMatrix_ncbiBadGuideCharacter()
218   {
219     String data = "ScoreMatrix MyTest\n\tX Y\nX 1 2\ny 3 4\n";
220     try
221     {
222       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
223               .parseMatrix();
224       fail("expected exception");
225     } catch (IOException e)
226     {
227       assertEquals(e.getMessage(),
228               "Error parsing score matrix at line 4, expected 'Y' but found 'y'");
229     }
230
231     data = "ScoreMatrix MyTest\n\tX Y\nXX 1 2\nY 3 4\n";
232     try
233     {
234       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
235               .parseMatrix();
236       fail("expected exception");
237     } catch (IOException e)
238     {
239       assertEquals(e.getMessage(),
240               "Error parsing score matrix at line 3, expected 'X' but found 'XX'");
241     }
242   }
243
244   @Test(groups = "Functional")
245   public void testParseMatrix_ncbiNameMissing()
246   {
247     /*
248      * Name missing on ScoreMatrix header line
249      */
250     String data = "ScoreMatrix\nX Y\n1 2\n3 4\n";
251     try
252     {
253       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
254               .parseMatrix();
255       fail("expected exception");
256     } catch (IOException e)
257     {
258       assertEquals(
259               e.getMessage(),
260               "Format error: expected 'ScoreMatrix <name>', found 'ScoreMatrix' at line 1");
261     }
262   }
263
264   /**
265    * Test a successful parse of a (small) score matrix file
266    * 
267    * @throws IOException
268    * @throws MalformedURLException
269    */
270   @Test(groups = "Functional")
271   public void testParseMatrix_ncbiFormat() throws MalformedURLException,
272           IOException
273   {
274     // input including comment and blank lines
275     String data = "ScoreMatrix MyTest\n#comment\n\n" + "\tA\tB\tC\n"
276             + "A\t1.0\t2.0\t3.0\n" + "B\t4.0\t5.0\t6.0\n"
277             + "C\t7.0\t8.0\t9.0\n";
278     FileParse fp = new FileParse(data, DataSourceType.PASTE);
279     ScoreMatrixFile parser = new ScoreMatrixFile(fp);
280     ScoreMatrix sm = parser.parseMatrix();
281   
282     assertNotNull(sm);
283     assertEquals(sm.getName(), "MyTest");
284     assertEquals(parser.getMatrixName(), "MyTest");
285     assertEquals(sm.getPairwiseScore('A', 'A'), 1.0f);
286     assertEquals(sm.getPairwiseScore('B', 'c'), 6.0f);
287     assertEquals(sm.getSize(), 3);
288   }
289
290   /**
291    * Test a successful parse of a (small) score matrix file
292    * 
293    * @throws IOException
294    * @throws MalformedURLException
295    */
296   @Test(groups = "Functional")
297   public void testParseMatrix_aaIndexBlosum80()
298           throws MalformedURLException,
299           IOException
300   {
301     FileParse fp = new FileParse("resources/scoreModel/blosum80.scm",
302             DataSourceType.FILE);
303     ScoreMatrixFile parser = new ScoreMatrixFile(fp);
304     ScoreMatrix sm = parser.parseMatrix();
305   
306     assertNotNull(sm);
307     assertEquals(sm.getName(), "HENS920103");
308     assertEquals(sm.getDescription(),
309             "BLOSUM80 substitution matrix (Henikoff-Henikoff, 1992)");
310     assertFalse(sm.isDNA());
311     assertTrue(sm.isProtein());
312     assertEquals(20, sm.getSize());
313     assertEquals(sm.getGapIndex(), -1);
314
315     assertEquals(sm.getPairwiseScore('A', 'A'), 7f);
316     assertEquals(sm.getPairwiseScore('A', 'R'), -3f);
317     assertEquals(sm.getPairwiseScore('r', 'a'), -3f); // A/a equivalent
318   }
319
320   /**
321    * Test a successful parse of a (small) score matrix file
322    * 
323    * @throws IOException
324    * @throws MalformedURLException
325    */
326   @Test(groups = "Functional")
327   public void testParseMatrix_aaindexFormat() throws MalformedURLException,
328           IOException
329   {
330     /*
331      * aaindex format has scores for diagonal and below only
332      */
333     String data = "H MyTest\n" + "D My description\n" + "R PMID:1438297\n"
334             + "A Authors, names\n" + "T Journal title\n"
335             + "J Journal reference\n" + "* matrix in 1/3 Bit Units\n"
336             + "M rows = ABC, cols = ABC\n" + "A\t1.0\n"
337             + "B\t4.0\t5.0\n"
338             + "C\t7.0\t8.0\t9.0\n";
339     FileParse fp = new FileParse(data, DataSourceType.PASTE);
340     ScoreMatrixFile parser = new ScoreMatrixFile(fp);
341     ScoreMatrix sm = parser.parseMatrix();
342   
343     assertNotNull(sm);
344     assertEquals(sm.getSize(), 3);
345     assertEquals(sm.getGapIndex(), -1);
346     assertEquals(sm.getName(), "MyTest");
347     assertEquals(sm.getDescription(), "My description");
348     assertEquals(sm.getPairwiseScore('A', 'A'), 1.0f);
349     assertEquals(sm.getPairwiseScore('A', 'B'), 4.0f);
350     assertEquals(sm.getPairwiseScore('A', 'C'), 7.0f);
351     assertEquals(sm.getPairwiseScore('B', 'A'), 4.0f);
352     assertEquals(sm.getPairwiseScore('B', 'B'), 5.0f);
353     assertEquals(sm.getPairwiseScore('B', 'C'), 8.0f);
354     assertEquals(sm.getPairwiseScore('C', 'C'), 9.0f);
355     assertEquals(sm.getPairwiseScore('C', 'B'), 8.0f);
356     assertEquals(sm.getPairwiseScore('C', 'A'), 7.0f);
357   }
358
359   @Test(groups = "Functional")
360   public void testParseMatrix_aaindex_mMissing()
361           throws MalformedURLException,
362           IOException
363   {
364     /*
365      * aaindex format but M cols=, rows= is missing
366      */
367     String data = "H MyTest\n" + "A\t1.0\n"
368             + "B\t4.0\t5.0\n"
369             + "C\t7.0\t8.0\t9.0\n";
370     FileParse fp = new FileParse(data, DataSourceType.PASTE);
371     ScoreMatrixFile parser = new ScoreMatrixFile(fp);
372     try
373     {
374       parser.parseMatrix();
375       fail("Expected exception");
376     } catch (FileFormatException e)
377     {
378       assertEquals(e.getMessage(), "No alphabet specified in matrix file");
379     }
380   }
381
382   @Test(groups = "Functional")
383   public void testParseMatrix_aaindex_rowColMismatch()
384           throws MalformedURLException,
385           IOException
386   {
387     String data = "H MyTest\n" + "M rows=ABC, cols=ABD\n" + "A\t1.0\n"
388             + "B\t4.0\t5.0\n"
389             + "C\t7.0\t8.0\t9.0\n";
390     FileParse fp = new FileParse(data, DataSourceType.PASTE);
391     ScoreMatrixFile parser = new ScoreMatrixFile(fp);
392     try
393     {
394       parser.parseMatrix();
395       fail("Expected exception");
396     } catch (FileFormatException e)
397     {
398       assertEquals(
399               e.getMessage(),
400               "Unexpected aaIndex score matrix data at line 2: M rows=ABC, cols=ABD rows != cols");
401     }
402   }
403
404   @Test(groups = "Functional")
405   public void testParseMatrix_ncbiHeaderRepeated()
406   {
407     String data = "ScoreMatrix BLOSUM\nScoreMatrix PAM250\nX Y\n1 2\n3 4\n";
408     try
409     {
410       new ScoreMatrixFile(new FileParse(data, DataSourceType.PASTE))
411               .parseMatrix();
412       fail("expected exception");
413     } catch (IOException e)
414     {
415       assertEquals(e.getMessage(),
416               "Error: 'ScoreMatrix' repeated in file at line 2");
417     }
418   }
419
420   @Test(groups = "Functional")
421   public void testParseMatrix_aaindex_tooManyRows()
422           throws MalformedURLException,
423           IOException
424   {
425     String data = "H MyTest\n" + "M rows=ABC, cols=ABC\n" + "A\t1.0\n"
426             + "B\t4.0\t5.0\n" + "C\t7.0\t8.0\t9.0\n" + "C\t7.0\t8.0\t9.0\n";
427     FileParse fp = new FileParse(data, DataSourceType.PASTE);
428     ScoreMatrixFile parser = new ScoreMatrixFile(fp);
429     try
430     {
431       parser.parseMatrix();
432       fail("Expected exception");
433     } catch (FileFormatException e)
434     {
435       assertEquals(e.getMessage(), "Too many data rows in matrix file");
436     }
437   }
438
439   @Test(groups = "Functional")
440   public void testParseMatrix_aaindex_extraDataLines()
441           throws MalformedURLException,
442           IOException
443   {
444     String data = "H MyTest\n" + "M rows=ABC, cols=ABC\n" + "A\t1.0\n"
445             + "B\t4.0\t5.0\n" + "C\t7.0\t8.0\t9.0\n" + "something extra\n";
446     FileParse fp = new FileParse(data, DataSourceType.PASTE);
447     ScoreMatrixFile parser = new ScoreMatrixFile(fp);
448     try
449     {
450       parser.parseMatrix();
451       fail("Expected exception");
452     } catch (FileFormatException e)
453     {
454       assertEquals(e.getMessage(), "Too many data rows in matrix file");
455     }
456   }
457
458   @Test(groups = "Functional")
459   public void testParseMatrix_aaindex_tooFewColumns()
460           throws MalformedURLException,
461           IOException
462   {
463     String data = "H MyTest\n" + "M rows=ABC, cols=ABC\n" + "A\t1.0\n"
464             + "B\t4.0\t5.0\n" + "C\t7.0\t8.0\n";
465     FileParse fp = new FileParse(data, DataSourceType.PASTE);
466     ScoreMatrixFile parser = new ScoreMatrixFile(fp);
467     try
468     {
469       parser.parseMatrix();
470       fail("Expected exception");
471     } catch (FileFormatException e)
472     {
473       assertEquals(
474               e.getMessage(),
475               "Expected 3 scores at line 5: 'C\t7.0\t8.0' but found 2");
476     }
477   }
478
479   /**
480    * Test a successful parse and register of a score matrix file
481    * 
482    * @throws IOException
483    * @throws MalformedURLException
484    */
485   @Test(groups = "Functional")
486   public void testParse_ncbiFormat() throws MalformedURLException,
487           IOException
488   {
489     assertNull(ScoreModels.getInstance().forName("MyNewTest"));
490
491     String data = "ScoreMatrix MyNewTest\n" + "\tA\tB\tC\n"
492             + "A\t1.0\t2.0\t3.0\n" + "B\t4.0\t5.0\t6.0\n"
493             + "C\t7.0\t8.0\t9.0\n";
494     FileParse fp = new FileParse(data, DataSourceType.PASTE);
495     ScoreMatrixFile parser = new ScoreMatrixFile(fp);
496
497     parser.parse();
498   
499     ScoreMatrix sm = (ScoreMatrix) ScoreModels.getInstance().forName(
500             "MyNewTest");
501     assertNotNull(sm);
502     assertEquals(sm.getName(), "MyNewTest");
503     assertEquals(parser.getMatrixName(), "MyNewTest");
504     assertEquals(sm.getPairwiseScore('A', 'A'), 1.0f);
505     assertEquals(sm.getPairwiseScore('B', 'c'), 6.0f);
506     assertEquals(sm.getSize(), 3);
507   }
508 }