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