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