JAL-4109 examine -Djalview.loglevel on startup to decide what the default log level...
[jalview.git] / test / jalview / io / HMMFileTest.java
index f16d6f5..cf74f55 100644 (file)
@@ -1,7 +1,10 @@
 package jalview.io;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
 
 import jalview.datamodel.HMMNode;
 import jalview.datamodel.HiddenMarkovModel;
@@ -11,199 +14,242 @@ import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
-import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Scanner;
 
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-public class HMMFileTest {
-
+import junit.extensions.PA;
 
+public class HMMFileTest {
 
-  HMMFile fn3 = new HMMFile(
-          new FileParse("test/jalview/io/test_fn3_hmm.txt",
-                  DataSourceType.FILE));
+  HMMFile fn3;
 
-  HMMFile pKinase = new HMMFile(
-          new FileParse("test/jalview/io/test_PKinase_hmm.txt",
-                  DataSourceType.FILE));
+  HMMFile pKinase;
 
-  HMMFile made1 = new HMMFile(
-          new FileParse("test/jalview/io/test_MADE1_hmm.txt",
-                  DataSourceType.FILE));
+  HMMFile made1;
 
-  HMMFileTest() throws IOException
+  @BeforeClass(alwaysRun = true)
+  public void setUp() throws IOException
   {
+    fn3 = new HMMFile("test/jalview/io/test_fn3_hmm.txt",
+            DataSourceType.FILE);
 
-  }
-
-  
+    pKinase = new HMMFile("test/jalview/io/test_PKinase_hmm.txt",
+            DataSourceType.FILE);
 
+    made1 = new HMMFile("test/jalview/io/test_MADE1_hmm.txt",
+            DataSourceType.FILE);
+  }
 
-  @Test
+  @Test(groups = "Functional")
   public void testParse() throws IOException
   {
-  
-    pKinase.parse();
     HiddenMarkovModel hmm = pKinase.getHMM();
     assertEquals(hmm.getName(), "Pkinase");
-    assertEquals(hmm.getAccessionNumber(), "PF00069.17");
-    assertEquals(hmm.getDescription(), "Protein kinase domain");
-    assertEquals(hmm.getLength().intValue(), 260);
-    assertNull(hmm.getMaxInstanceLength());
+    assertEquals(hmm.getProperty(HMMFile.ACCESSION_NUMBER), "PF00069.17");
+    assertEquals(hmm.getProperty(HMMFile.DESCRIPTION),
+            "Protein kinase domain");
+    assertEquals(hmm.getLength(), 260);
+    assertNull(hmm.getProperty(HMMFile.MAX_LENGTH));
     assertEquals(hmm.getAlphabetType(), "amino");
-    assertEquals(hmm.referenceAnnotationIsActive(), false);
-    assertEquals(hmm.maskValueIsActive(), false);
-    assertEquals(hmm.consensusResidueIsActive(), true);
-    assertEquals(hmm.consensusStructureIsActive(),
-            true);
-    assertEquals(hmm.mapIsActive(), true);
-    assertEquals(hmm.getDate(), "Thu Jun 16 11:44:06 2011");
-    assertNull(hmm.getCommandLineLog());
-    assertEquals(hmm.getNumberOfSequences().intValue(), 54);
-    assertEquals(hmm.getEffectiveNumberOfSequences(), 3.358521, 4d);
-    assertEquals(hmm.getCheckSum().longValue(), 3106786190l);
-    assertEquals(hmm.getGatheringThreshold(), "70.30 70.30");
-    assertEquals(hmm.getTrustedCutoff(), "70.30 70.30");
-    assertEquals(hmm.getNoiseCutoff(), "70.20 70.20");
-  
-    List<Character> symbols = new ArrayList<>();
-    symbols.add('A');
-    symbols.add('C');
-    symbols.add('D');
-    symbols.add('E');
-    symbols.add('F');
-    symbols.add('G');
-    symbols.add('H');
-    symbols.add('I');
-    symbols.add('K');
-    symbols.add('L');
-    symbols.add('M');
-    symbols.add('N');
-    symbols.add('P');
-    symbols.add('Q');
-    symbols.add('R');
-    symbols.add('S');
-    symbols.add('T');
-    symbols.add('V');
-    symbols.add('W');
-    symbols.add('Y');
-  
-    assertEquals(hmm.getSymbols(), symbols);
-  
-    assertEquals(getMatchEmission(0, 19, hmm), 0.032298, 0.001d);
-    assertEquals(getMatchEmission(12, 12, hmm), 0.0130, 0.001d);
-    assertEquals(getMatchEmission(23, 7, hmm), 0.02583, 0.001d);
-    assertEquals(getMatchEmission(54, 1, hmm), 0.008549, 0.001d);
-    assertEquals(getMatchEmission(178, 3, hmm), 0.07998, 0.001d);
-    assertEquals(getMatchEmission(210, 2, hmm), 0.014465, 0.001d);
-    assertEquals(getMatchEmission(260, 19, hmm), 0.02213, 0.001d);
-  
-    assertEquals(getInsertEmission(2, 1, hmm), 0.012, 0.001d);
-    assertEquals(getInsertEmission(15, 6, hmm), 0.02411, 0.001d);
-    assertEquals(getInsertEmission(22, 9, hmm), 0.06764, 0.001d);
-    assertEquals(getInsertEmission(57, 2, hmm), 0.0623, 0.001d);
-    assertEquals(getInsertEmission(203, 16, hmm), 0.0623, 0.001d);
-    assertEquals(getInsertEmission(255, 12, hmm), 0.0647, 0.001d);
-  
-    assertEquals(getStateTransition(0, 6, hmm),
-            Double.NEGATIVE_INFINITY);
-    assertEquals(getStateTransition(3, 6, hmm), 0.3848, 0.001d);
-    assertEquals(getStateTransition(29, 3, hmm), 0.5382, 0.001d);
-    assertEquals(getStateTransition(169, 3, hmm), 0.2916, 0.001d);
-    assertEquals(getStateTransition(209, 0, hmm), 0.99, 0.001d);
-    assertEquals(getStateTransition(243, 1, hmm), 0.0066, 0.001d);
-  
-    assertEquals(hmm.getNodeAlignmentColumn(3).intValue(), 2);
+    assertFalse(hmm.getBooleanProperty(HMMFile.REFERENCE_ANNOTATION));
+    assertFalse(hmm.getBooleanProperty(HMMFile.MASKED_VALUE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.CONSENSUS_RESIDUE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.CONSENSUS_STRUCTURE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.MAP));
+    assertEquals(hmm.getProperty(HMMFile.DATE), "Thu Jun 16 11:44:06 2011");
+    assertNull(hmm.getProperty(HMMFile.COMMAND_LOG));
+    assertEquals(hmm.getProperty(HMMFile.NUMBER_OF_SEQUENCES), "54");
+    assertEquals(hmm.getProperty(HMMFile.EFF_NUMBER_OF_SEQUENCES),
+            "3.358521");
+    assertEquals(hmm.getProperty(HMMFile.CHECK_SUM), "3106786190");
+    assertEquals(hmm.getProperty(HMMFile.GATHERING_THRESHOLD),
+            "70.30 70.30");
+    assertEquals(hmm.getProperty(HMMFile.TRUSTED_CUTOFF), "70.30 70.30");
+    assertEquals(hmm.getProperty(HMMFile.NOISE_CUTOFF), "70.20 70.20");
+  
+    assertEquals(hmm.getSymbols(), "ACDEFGHIKLMNPQRSTVWY");
+  
+    assertEquals(hmm.getMatchEmissionProbability(0, 'Y'), 0.16102, 0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(11, 'P'), 0.0130, 0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(24, 'I'), 0.02583, 0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(83, 'C'), 0.008549,
+            0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(332, 'E'), 0.07998,
+            0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(381, 'D'), 0.014465,
+            0.001d);
+    assertEquals(hmm.getMatchEmissionProbability(475, 'Y'), 0.02213,
+            0.001d);
+  
+    assertEquals(hmm.getInsertEmissionProbability(1, 'C'), 0.012, 0.001d);
+    assertEquals(hmm.getInsertEmissionProbability(14, 'H'), 0.02411,
+            0.001d);
+    assertEquals(hmm.getInsertEmissionProbability(23, 'L'), 0.06764,
+            0.001d);
+    assertEquals(hmm.getInsertEmissionProbability(90, 'D'), 0.0623, 0.001d);
+    assertEquals(hmm.getInsertEmissionProbability(374, 'T'), 0.0623,
+            0.001d);
+    assertEquals(hmm.getInsertEmissionProbability(470, 'P'), 0.0647,
+            0.001d);
+  
+    assertEquals(hmm.getStateTransitionProbability(2, 6), 0.3848, 0.001d);
+    assertEquals(hmm.getStateTransitionProbability(38, 3), 0.5382, 0.001d);
+    assertEquals(hmm.getStateTransitionProbability(305, 3), 0.2916, 0.001d);
+    assertEquals(hmm.getStateTransitionProbability(380, 0), 0.99, 0.001d);
+    assertEquals(hmm.getStateTransitionProbability(453, 1), 0.0066, 0.001d);
+  
+    assertEquals(hmm.getNodeMapPosition(3), 3);
     assertEquals(hmm.getReferenceAnnotation(7), '-');
     assertEquals(hmm.getConsensusResidue(23), 't');
     assertEquals(hmm.getMaskedValue(30), '-');
     assertEquals(hmm.getConsensusStructure(56), 'S');
   
-    assertEquals(hmm.getNodeAlignmentColumn(78).intValue(), 135);
+    assertEquals(hmm.getNodeMapPosition(78), 136);
     assertEquals(hmm.getReferenceAnnotation(93), '-');
     assertEquals(hmm.getConsensusResidue(145), 'a');
     assertEquals(hmm.getMaskedValue(183), '-');
     assertEquals(hmm.getConsensusStructure(240), 'H');
-  
+  }
+
+  /**
+   * Test that Jalview can parse an HMM file even with a bunch of 'mandatory'
+   * fields missing (including no MAP annotation or // terminator line)
+   * 
+   * @throws IOException
+   */
+  @Test(groups = "Functional")
+  public void testParse_minimalFile() throws IOException
+  {
+    /*
+     * ALPH is absent, alphabet inferred from HMM header line
+     * Optional COMPO line is absent
+     * first line after HMM is a guide line for readability
+     * next line is BEGIN node insert emissions
+     * next line is BEGIN node transitions
+     * next line is first sequence node match emissions 1.1 1.2 1.3
+     * next line is first sequence node insert emissions 1.4 1.5 1.6
+     * last line is first sequence node transitions
+     */
+    //@formatter:off
+    String hmmData = 
+            "HMMER3\n" +
+            "HMM P M J\n" +
+            // both spec and parser require a line after the HMM line
+            " m->m m->i m->d i->m i->i d->m d->d\n" +
+            " 0.1 0.2 0.3\n" + 
+            " 0.4 0.5 0.6 0.7 0.8 0.9 0.95\n" + 
+            " 1 1.1 1.2 1.3 - - - - -\n" + 
+            " 1.4 1.5 1.6\n" + 
+            " 1.7 1.8 1.9 2.0 2.1 2.2 2.3\n" +
+            " 2 1.01 1.02 1.03 - - - - -\n" + 
+            " 1.04 1.05 1.06\n" + 
+            " 1.7 1.8 1.9 2.0 2.1 2.2 2.3\n";
+    //@formatter:on
+    HMMFile parser = new HMMFile(hmmData, DataSourceType.PASTE);
+    HiddenMarkovModel hmm = parser.getHMM();
+    assertNotNull(hmm);
+    assertEquals(hmm.getSymbols(), "PMJ");
+    // no LENG property: this should return node count excluding BEGIN node
+    assertEquals(hmm.getLength(), 2);
+
+    // node 1 (implicitly mapped to column 0)
+    double prob = hmm.getMatchEmissionProbability(0, 'p');
+    assertEquals(prob, Math.pow(Math.E, -1.1));
+    prob = hmm.getInsertEmissionProbability(0, 'J');
+    assertEquals(prob, Math.pow(Math.E, -1.6));
+
+    // node 2 (implicitly mapped to column 1)
+    prob = hmm.getMatchEmissionProbability(1, 'M');
+    assertEquals(prob, Math.pow(Math.E, -1.02));
+    prob = hmm.getInsertEmissionProbability(1, 'm');
+    assertEquals(prob, Math.pow(Math.E, -1.05));
   }
   
-  @Test
-  public void testParseFileProperties() throws IOException
+  @Test(groups = "Functional")
+  public void testParseHeaderLines_amino() throws IOException
   {
     FileReader fr = new FileReader(
             new File("test/jalview/io/test_fn3_hmm.txt"));
     BufferedReader br = new BufferedReader(fr);
-    fn3.parseFileProperties(br);
-    fn3.parseModel(br); // this is for a later test
-    HiddenMarkovModel testHMM = new HiddenMarkovModel();
-    testHMM = fn3.getHMM();
+    HiddenMarkovModel hmm = new HiddenMarkovModel();
+    HMMFile testee = new HMMFile();
+    PA.setValue(testee, "hmm", hmm);
+    testee.parseHeaderLines(br);
     br.close();
     fr.close();
   
-    assertEquals(testHMM.getName(), "fn3");
-    assertEquals(testHMM.getAccessionNumber(), "PF00041.13");
-    assertEquals(testHMM.getDescription(),
+    assertEquals(hmm.getName(), "fn3");
+    assertEquals(hmm.getProperty(HMMFile.ACCESSION_NUMBER), "PF00041.13");
+    assertEquals(hmm.getProperty(HMMFile.DESCRIPTION),
             "Fibronectin type III domain");
-    assertEquals(testHMM.getLength().intValue(), 86);
-    assertNull(testHMM.getMaxInstanceLength());
-    assertEquals(testHMM.getAlphabetType(), "amino");
-    assertEquals(testHMM.referenceAnnotationIsActive(), false);
-    assertEquals(testHMM.maskValueIsActive(), false);
-    assertEquals(testHMM.consensusResidueIsActive(), true);
-    assertEquals(testHMM.consensusStructureIsActive(), true);
-    assertEquals(testHMM.mapIsActive(), true);
-    assertEquals(testHMM.getDate(), "Fri Jun 20 08:22:31 2014");
-    assertNull(testHMM.getCommandLineLog());
-    assertEquals(testHMM.getNumberOfSequences().intValue(), 106);
-    assertEquals(testHMM.getEffectiveNumberOfSequences(), 11.415833, 4d);
-    assertEquals(testHMM.getCheckSum().longValue(), 3564431818l);
-    assertEquals(testHMM.getGatheringThreshold(), "8.00 7.20");
-    assertEquals(testHMM.getTrustedCutoff(), "8.00 7.20");
-    assertEquals(testHMM.getNoiseCutoff(), "7.90 7.90");
-    assertEquals(testHMM.getViterbi(), "-9.7737  0.71847");
-    assertEquals(testHMM.getMSV(), "-9.4043  0.71847");
-    assertEquals(testHMM.getForward(), "-3.8341  0.71847");
-  
-  
-    FileReader fr3 = new FileReader(
+    assertEquals(hmm.getProperty(HMMFile.LENGTH), "86");
+    assertNull(hmm.getProperty(HMMFile.MAX_LENGTH));
+    assertEquals(hmm.getAlphabetType(), "amino");
+    assertFalse(hmm.getBooleanProperty(HMMFile.REFERENCE_ANNOTATION));
+    assertFalse(hmm.getBooleanProperty(HMMFile.MASKED_VALUE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.CONSENSUS_RESIDUE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.CONSENSUS_STRUCTURE));
+
+    assertTrue(hmm.getBooleanProperty(HMMFile.MAP));
+    assertEquals(hmm.getProperty(HMMFile.DATE), "Fri Jun 20 08:22:31 2014");
+    assertNull(hmm.getProperty(HMMFile.COMMAND_LOG));
+    assertEquals(hmm.getProperty(HMMFile.NUMBER_OF_SEQUENCES), "106");
+    assertEquals(hmm.getProperty(HMMFile.EFF_NUMBER_OF_SEQUENCES),
+            "11.415833");
+    assertEquals(hmm.getProperty(HMMFile.CHECK_SUM), "3564431818");
+    assertEquals(hmm.getProperty(HMMFile.GATHERING_THRESHOLD), "8.00 7.20");
+    assertEquals(hmm.getProperty(HMMFile.TRUSTED_CUTOFF), "8.00 7.20");
+    assertEquals(hmm.getProperty(HMMFile.NOISE_CUTOFF), "7.90 7.90");
+    assertEquals(hmm.getViterbi(), "-9.7737  0.71847");
+    assertEquals(hmm.getMSV(), "-9.4043  0.71847");
+    assertEquals(hmm.getForward(), "-3.8341  0.71847");
+  }
+  
+  @Test(groups = "Functional")
+  public void testParseHeaderLines_dna() throws IOException
+  {
+    FileReader fr = new FileReader(
             new File("test/jalview/io/test_MADE1_hmm.txt"));
-    BufferedReader br3 = new BufferedReader(fr3);
-    made1.parseFileProperties(br3);
-    testHMM = made1.getHMM();
-    br3.close();
-    fr3.close();
+    BufferedReader br = new BufferedReader(fr);
+    HiddenMarkovModel hmm = new HiddenMarkovModel();
+    HMMFile testee = new HMMFile();
+    PA.setValue(testee, "hmm", hmm);
+    testee.parseHeaderLines(br);
+    br.close();
+    fr.close();
   
-    assertEquals(testHMM.getName(), "MADE1");
-    assertEquals(testHMM.getAccessionNumber(), "DF0000629.2");
-    assertEquals(testHMM.getDescription(),
+    assertEquals(hmm.getName(), "MADE1");
+    assertEquals(hmm.getProperty(HMMFile.ACCESSION_NUMBER),
+            "DF0000629.2");
+    assertEquals(hmm.getProperty(HMMFile.DESCRIPTION),
             "MADE1 (MAriner Derived Element 1), a TcMar-Mariner DNA transposon");
-    assertEquals(testHMM.getLength().intValue(), 80);
-    assertEquals(testHMM.getMaxInstanceLength().intValue(), 426);
-    assertEquals(testHMM.getAlphabetType(), "DNA");
-    assertEquals(testHMM.referenceAnnotationIsActive(), true);
-    assertEquals(testHMM.maskValueIsActive(), false);
-    assertEquals(testHMM.consensusResidueIsActive(), true);
-    assertEquals(testHMM.consensusStructureIsActive(), false);
-    assertEquals(testHMM.mapIsActive(), true);
-    assertEquals(testHMM.getDate(), "Tue Feb 19 20:33:41 2013");
-    assertNull(testHMM.getCommandLineLog());
-    assertEquals(testHMM.getNumberOfSequences().intValue(), 1997);
-    assertEquals(testHMM.getEffectiveNumberOfSequences(), 3.911818, 4d);
-    assertEquals(testHMM.getCheckSum().longValue(), 3015610723l);
-    assertEquals(testHMM.getGatheringThreshold(), "2.324 4.234");
-    assertEquals(testHMM.getTrustedCutoff(), "2.343 1.212");
-    assertEquals(testHMM.getNoiseCutoff(), "2.354 5.456");
-    assertEquals(testHMM.getViterbi(), "-9.3632  0.71858");
-    assertEquals(testHMM.getMSV(), "-8.5786  0.71858");
-    assertEquals(testHMM.getForward(), "-3.4823  0.71858");
-  
-  
+    assertEquals(hmm.getProperty(HMMFile.LENGTH), "80");
+    assertEquals(hmm.getProperty(HMMFile.MAX_LENGTH), "426");
+    assertEquals(hmm.getAlphabetType(), "DNA");
+    assertTrue(hmm.getBooleanProperty(HMMFile.REFERENCE_ANNOTATION));
+    assertFalse(hmm.getBooleanProperty(HMMFile.MASKED_VALUE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.CONSENSUS_RESIDUE));
+    assertFalse(hmm.getBooleanProperty(HMMFile.CONSENSUS_STRUCTURE));
+    assertTrue(hmm.getBooleanProperty(HMMFile.MAP));
+    assertEquals(hmm.getProperty(HMMFile.DATE), "Tue Feb 19 20:33:41 2013");
+    assertNull(hmm.getProperty(HMMFile.COMMAND_LOG));
+    assertEquals(hmm.getProperty(HMMFile.NUMBER_OF_SEQUENCES), "1997");
+    assertEquals(hmm.getProperty(HMMFile.EFF_NUMBER_OF_SEQUENCES), "3.911818");
+    assertEquals(hmm.getProperty(HMMFile.CHECK_SUM), "3015610723");
+    assertEquals(hmm.getProperty(HMMFile.GATHERING_THRESHOLD),
+            "2.324 4.234");
+    assertEquals(hmm.getProperty(HMMFile.TRUSTED_CUTOFF), "2.343 1.212");
+    assertEquals(hmm.getProperty(HMMFile.NOISE_CUTOFF), "2.354 5.456");
+    assertEquals(hmm.getViterbi(), "-9.3632  0.71858");
+    assertEquals(hmm.getMSV(), "-8.5786  0.71858");
+    assertEquals(hmm.getForward(), "-3.4823  0.71858");
   }
   
-  @Test
+  @Test(groups = "Functional")
   public void testFillList() throws IOException
   {
     Scanner scanner1 = new Scanner("1.3 2.4 5.3 3.9 9.8 4.7 4.3 2.3 6.9");
@@ -219,12 +265,11 @@ public class HMMFileTest {
     filledArray.add(0.10026);
     filledArray.add(0.001);
   
-    List<Double> testList = HMMFile.fillList(scanner1, 9);
+    double[] testList = HMMFile.parseDoubles(scanner1, 9);
 
     for (int i = 0; i < 9; i++)
     {
-      assertEquals(testList.get(i), filledArray.get(i), 0.001d);
-
+      assertEquals(testList[i], filledArray.get(i), 0.001d);
     }
 
     filledArray.clear();
@@ -238,90 +283,105 @@ public class HMMFileTest {
     filledArray.add(0.00355);
     filledArray.add(0.2466);
   
-    testList = HMMFile.fillList(scanner2, 5);
+    testList = HMMFile.parseDoubles(scanner2, 5);
 
     for (int i = 0; i < 5; i++)
     {
-      assertEquals(testList.get(i), filledArray.get(i), 0.001d);
+      assertEquals(testList[i], filledArray.get(i), 0.001d);
     }
-  
   }
   
-  @Test
+  @Test(groups = "Functional")
   public void testParseModel() throws IOException
   {
     FileReader fr = new FileReader(
             new File("test/jalview/io/test_MADE1_hmm.txt"));
     BufferedReader br = new BufferedReader(fr);
     HiddenMarkovModel testHMM = new HiddenMarkovModel();
-    for (int i = 0; i < 24; i++)
+    String line = null;
+    do
     {
-      br.readLine();
-    }
+      line = br.readLine(); // skip header lines up to HMM plus one
+    } while (!line.startsWith("HMM "));
+    br.readLine();
+
     made1.parseModel(br);
     testHMM = made1.getHMM();
+
     br.close();
     fr.close();
   
-    assertEquals(getMatchEmission(0, 2, testHMM), 0.1961, 0.001d);
-    assertEquals(getMatchEmission(2, 1, testHMM), 0.09267, 0.001d);
-    assertEquals(getMatchEmission(12, 2, testHMM), 0.07327, 0.001d);
-    assertEquals(getMatchEmission(69, 1, testHMM), 0.04184, 0.001d);
-    assertEquals(getMatchEmission(76, 2, testHMM), 0.07, 0.001d);
-  
-    assertEquals(getInsertEmission(0, 1, testHMM), 0.25, 0.001d);
-    assertEquals(getInsertEmission(1, 2, testHMM), 0.25, 0.001d);
-    assertEquals(getInsertEmission(31, 3, testHMM), 0.2776, 0.001d);
-    assertEquals(getInsertEmission(70, 3, testHMM), 0.25, 0.001d);
-    assertEquals(getInsertEmission(80, 3, testHMM), 0.25, 0.001d);
+    assertEquals(testHMM.getMatchEmissionProbability(1, 'C'), 0.09267,
+            0.001d);
+    assertEquals(testHMM.getMatchEmissionProbability(25, 'G'), 0.07327,
+            0.001d);
+    assertEquals(testHMM.getMatchEmissionProbability(1092, 'C'), 0.04184,
+            0.001d);
+    assertEquals(testHMM.getMatchEmissionProbability(1107, 'G'), 0.07,
+            0.001d);
+  
+    assertEquals(testHMM.getInsertEmissionProbability(0, 'G'), 0.25,
+            0.001d);
+    assertEquals(testHMM.getInsertEmissionProbability(247, 'T'), 0.2776,
+            0.001d);
+    assertEquals(testHMM.getInsertEmissionProbability(1096, 'T'), 0.25,
+            0.001d);
+    assertEquals(testHMM.getInsertEmissionProbability(1111, 'T'), 0.25,
+            0.001d);
 
-    assertEquals(getStateTransition(2, 0, testHMM), 0.9634, 0.001d);
-    assertEquals(getStateTransition(6, 1, testHMM), 0.0203, 0.001d);
-    assertEquals(getStateTransition(9, 3, testHMM), 0.2515, 0.001d);
-    assertEquals(getStateTransition(20, 4, testHMM), 0.78808, 0.001d);
-    assertEquals(getStateTransition(68, 2, testHMM), 0.01845, 0.001d);
-    assertEquals(getStateTransition(80, 6, testHMM),
+    assertEquals(testHMM.getStateTransitionProbability(1, 0), 0.9634,
+            0.001d);
+    assertEquals(testHMM.getStateTransitionProbability(5, 1), 0.0203,
+            0.001d);
+    assertEquals(testHMM.getStateTransitionProbability(14, 3), 0.2515,
+            0.001d);
+    assertEquals(testHMM.getStateTransitionProbability(65, 4), 0.78808,
+            0.001d);
+    assertEquals(testHMM.getStateTransitionProbability(1080, 2), 0.01845,
+            0.001d);
+    assertEquals(testHMM.getStateTransitionProbability(1111, 6),
             Double.NEGATIVE_INFINITY);
-  
   }
   
-  @Test
+  @Test(groups = "Functional")
   public void testParseAnnotations()
   {
     HMMFile testFile = new HMMFile();
-    testFile.getHMM().getNodes().add(new HMMNode());
-    testFile.getHMM().getNodes().add(new HMMNode());
-    testFile.getHMM().getNodes().add(new HMMNode());
-  
-  
-    testFile.getHMM().setConsensusResidueStatus(true);
-    testFile.getHMM().setMAPStatus(true);
-    testFile.getHMM().setReferenceAnnotationStatus(true);
-    testFile.getHMM().setConsensusStructureStatus(true);
-    testFile.getHMM().setMaskedValueStatus(true);
+    HiddenMarkovModel hmm = new HiddenMarkovModel();
+    PA.setValue(testFile, "hmm", hmm);
+
+    List<HMMNode> nodes = new ArrayList<>();
+    nodes.add(new HMMNode()); // BEGIN node
+  
+    hmm.setProperty(HMMFile.CONSENSUS_RESIDUE, "yes");
+    hmm.setProperty(HMMFile.MAP, "yes");
+    hmm.setProperty(HMMFile.REFERENCE_ANNOTATION, "yes");
+    hmm.setProperty(HMMFile.CONSENSUS_STRUCTURE, "yes");
+    hmm.setProperty(HMMFile.MASKED_VALUE, "yes");
     Scanner scanner = new Scanner("1345 t t t t");
-    testFile.parseAnnotations(scanner, 1);
-  
-    testFile.getHMM().setConsensusResidueStatus(true);
-    testFile.getHMM().setMAPStatus(false);
-    testFile.getHMM().setReferenceAnnotationStatus(true);
-    testFile.getHMM().setConsensusStructureStatus(false);
-    testFile.getHMM().setMaskedValueStatus(false);
+    HMMNode node = new HMMNode();
+    nodes.add(node);
+    testFile.parseAnnotations(scanner, node);
+  
+    hmm.setProperty(HMMFile.CONSENSUS_RESIDUE, "yes");
+    hmm.setProperty(HMMFile.MAP, "no");
+    hmm.setProperty(HMMFile.REFERENCE_ANNOTATION, "yes");
+    hmm.setProperty(HMMFile.CONSENSUS_STRUCTURE, "no");
+    hmm.setProperty(HMMFile.MASKED_VALUE, "no");
     Scanner scanner2 = new Scanner("- y x - -");
-    testFile.parseAnnotations(scanner2, 2);
-  
-    HiddenMarkovModel hmm = testFile.getHMM();
-  
-    assertEquals(hmm.getNodeAlignmentColumn(1).intValue(), 1344);
+    node = new HMMNode();
+    nodes.add(node);
+    testFile.parseAnnotations(scanner2, node);
+
+    hmm.setNodes(nodes);
+
+    assertEquals(hmm.getNodeMapPosition(1), 1345);
     assertEquals(hmm.getConsensusResidue(1), 't');
     assertEquals(hmm.getReferenceAnnotation(1), 't');
     assertEquals(hmm.getMaskedValue(1), 't');
     assertEquals(hmm.getConsensusStructure(1), 't');
   
-    assertEquals(hmm.findNodeIndex(1344).intValue(), 1);
-  
     scanner.close();
-  
   }
   
   /**
@@ -329,85 +389,58 @@ public class HMMFileTest {
    * 
    * @throws IOException
    */
-
-
-  @Test(priority = 3)
-  public void testExportFile() throws IOException
+  @Test(groups = "Functional")
+  public void testPrint_roundTrip() throws IOException
   {
-    pKinase.exportFile("test/jalview/io/test_export_hmm.txt");
+    String output = pKinase.print();
     HMMFile pKinaseClone = new HMMFile(
-            new FileParse("test/jalview/io/test_export_hmm.txt",
-                    DataSourceType.FILE));
-    pKinaseClone.parse();
-    HiddenMarkovModel pKinaseHMM = new HiddenMarkovModel();
-    HiddenMarkovModel pKinaseCloneHMM = new HiddenMarkovModel();
-    pKinaseHMM = pKinase.getHMM();
-    pKinaseCloneHMM = pKinaseClone.getHMM();
-  
-    for (int i = 0; i < pKinaseHMM.getLength(); i++)
-    {
-      List<Double> list1;
-      List<Double> list2;
-      boolean result;
-  
-      list1 = pKinaseHMM.getNode(i).getMatchEmissions();
-      list2 = pKinaseCloneHMM.getNode(i).getMatchEmissions();
-  
-      result = checkIfListsAreIdentical(list1, list2);
-      assertEquals(result, true);
-  
-      list1 = pKinaseHMM.getNode(i).getInsertEmissions();
-      list2 = pKinaseCloneHMM.getNode(i).getInsertEmissions();
-  
-      result = checkIfListsAreIdentical(list1, list2);
-      assertEquals(result, true);
-  
-      list1 = pKinaseHMM.getNode(i).getStateTransitions();
-      list2 = pKinaseCloneHMM.getNode(i).getStateTransitions();
+            new FileParse(output, DataSourceType.PASTE));
+    HiddenMarkovModel pKinaseHMM = pKinase.getHMM();
+    HiddenMarkovModel pKinaseCloneHMM = pKinaseClone.getHMM();
   
-      result = checkIfListsAreIdentical(list1, list2);
-      assertEquals(result, true);
+    checkModelsMatch(pKinaseHMM, pKinaseCloneHMM);
+  }
+
+  /**
+   * A helper method to check two HMM models have the same values
+   * 
+   * @param model1
+   * @param model2
+   */
+  protected void checkModelsMatch(HiddenMarkovModel model1,
+          HiddenMarkovModel model2)
+  {
+    assertEquals(model1.getLength(), model2.getLength());
+
+    for (int i = 0; i < model1.getLength(); i++)
+    {
+      String msg = "For Node" + i;
+      assertEquals(model1.getNode(i).getMatchEmissions(),
+              model2.getNode(i).getMatchEmissions(), msg);
+      assertEquals(model1.getNode(i).getInsertEmissions(),
+              model2.getNode(i).getInsertEmissions(), msg);
+      assertEquals(model1.getNode(i).getStateTransitions(),
+              model2.getNode(i).getStateTransitions(), msg);
   
       if (i > 0)
       {
-        int alignColumn1;
-        int alignColumn2;
-  
-        alignColumn1 = pKinaseHMM.getNodeAlignmentColumn(i);
-        alignColumn2 = pKinaseCloneHMM.getNodeAlignmentColumn(i);
-  
-        assertEquals(alignColumn1, alignColumn2);
-  
-        char annotation1;
-        char annotation2;
-  
-        annotation1 = pKinaseHMM.getReferenceAnnotation(i);
-        annotation2 = pKinaseCloneHMM.getReferenceAnnotation(i);
-  
-        assertEquals(annotation1, annotation2);
-  
-        annotation1 = pKinaseHMM.getConsensusResidue(i);
-        annotation2 = pKinaseCloneHMM.getConsensusResidue(i);
-  
-        assertEquals(annotation1, annotation2);
+        assertEquals(model1.getNodeMapPosition(i),
+                model2.getNodeMapPosition(i), msg);
+        assertEquals(model1.getReferenceAnnotation(i),
+                model2.getReferenceAnnotation(i), msg);
+        assertEquals(model1.getConsensusResidue(i),
+                model2.getConsensusResidue(i), msg);
       }
-  
     }
-  
   }
   
-  @Test(priority = 1)
-  public void testAppendFileProperties() throws FileNotFoundException
+  @Test(groups = "Functional")
+  public void testAppendProperties() throws FileNotFoundException
   {
-    PrintWriter writer = new PrintWriter(
-            "test/jalview/io/test_export_hmm.txt");
-    fn3.appendFileProperties(writer);
-    writer.close();
-    String content;
-    File file = new File("test/jalview/io/test_export_hmm.txt");
+    StringBuilder sb = new StringBuilder();
+    fn3.appendProperties(sb);
 
-    Scanner testScanner = new Scanner(file);
-    testScanner.useDelimiter("\\Z");
+    Scanner testScanner = new Scanner(sb.toString());
   
     String[] expected = new String[] { "HMMER3/f [3.1b1 | May 2013]",
         "NAME  fn3", "ACC   PF00041.13",
@@ -427,18 +460,12 @@ public class HMMFileTest {
     testScanner.close();
   }
   
-  @Test(priority = 2)
-  public void testAppendModel() throws FileNotFoundException
+  @Test(groups = "Functional")
+  public void testAppendModelAsString() throws FileNotFoundException
   {
-    PrintWriter writer = new PrintWriter(
-            "test/jalview/io/test_export_hmm.txt");
-    fn3.appendModel(writer);
-    writer.close();
-    String string;
-    File file = new File("test/jalview/io/test_export_hmm.txt");
-    Scanner scanner = new Scanner(file);
-    scanner.useDelimiter("\\Z");
-    string = scanner.next();
+    StringBuilder sb = new StringBuilder();
+    fn3.appendModelAsString(sb);
+    String string = sb.toString();
 
     assertEquals(findValue(2, 2, 2, string), "4.42225");
     assertEquals(findValue(12, 14, 1, string), "2.79307");
@@ -450,10 +477,10 @@ public class HMMFileTest {
     assertEquals(findValue(16, 65, 1, string), "2.81003");
     assertEquals(findValue(14, 3, 1, string), "2.69012");
     assertEquals(findValue(11, 32, 1, string), "4.34805");
-  
   }
   
   /**
+   * A helper method to find a token in the model string
    * 
    * @param symbolIndex
    *          index of symbol being searched. First symbol has index 1.
@@ -466,11 +493,9 @@ public class HMMFileTest {
    *          string model being searched
    * @return value at specified position
    */
-
-  public String findValue(int symbolIndex, int nodeIndex, int line,
+  private String findValue(int symbolIndex, int nodeIndex, int line,
           String model)
   {
-  
     String value = "";
     Scanner scanner = new Scanner(model);
     scanner.nextLine();
@@ -498,80 +523,9 @@ public class HMMFileTest {
       {
         scanner.next();
       }
-  
     }
-    return value;
-  
-  }
-  
-  public boolean checkIfListsAreIdentical(List<Double> list1,
-          List<Double> list2)
-  {
-    boolean isDifferent = false;
-    for (int i = 0; i < list1.size(); i++)
-    {
-      Double entry1;
-      Double entry2;
-      entry1 = list1.get(i);
-      entry2 = list2.get(i);
-      if (!(entry1 == entry2))
-      {
-        isDifferent = true;
-      }
-    }
-    return isDifferent;
-  }
-
-  /**
-   * gets the match emission at a node for a symbol
-   * 
-   * @param nodeIndex
-   *          position of node in model
-   * @param symbolIndex
-   *          index of symbol being searched
-   * @return negative log probability of a match emission of the given symbol
-   */
-  public double getMatchEmission(int nodeIndex, int symbolIndex,
-          HiddenMarkovModel hmm)
-  {
-    double value = hmm.getNodes().get(nodeIndex).getMatchEmissions()
-            .get(symbolIndex);
-    return value;
-  }
-
-  /**
-   * gets the insert emission at a node for a symbol
-   * 
-   * @param nodeIndex
-   *          position of node in model
-   * @param symbolIndex
-   *          index of symbol being searched
-   * @return negative log probability of an insert emission of the given symbol
-   */
-  public double getInsertEmission(int nodeIndex, int symbolIndex,
-          HiddenMarkovModel hmm)
-  {
-    double value = hmm.getNodes().get(nodeIndex).getInsertEmissions()
-            .get(symbolIndex);
-    return value;
-  }
-
-  /**
-   * gets the state transition at a node for a specific transition
-   * 
-   * @param nodeIndex
-   *          position of node in model
-   * @param transitionIndex
-   *          index of stransition being searched
-   * @return negative log probability of a state transition of the given type
-   */
-  public double getStateTransition(int nodeIndex, int transitionIndex,
-          HiddenMarkovModel hmm)
-  {
-    double value = hmm.getNodes().get(nodeIndex).getStateTransitions()
-            .get(transitionIndex);
+    scanner.close();
     return value;
   }
-
 }