Random DNA generator tarted up
[jalview.git] / test / jalview / analysis / DnaTest.java
index 49a544d..01ed183 100644 (file)
@@ -4,13 +4,15 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import jalview.api.AlignViewportI;
+import jalview.datamodel.AlignedCodon;
+import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.SequenceI;
 import jalview.gui.AlignViewport;
 import jalview.io.FormatAdapter;
 
 import java.io.IOException;
-import java.util.Arrays;
 
 import org.junit.Test;
 
@@ -171,7 +173,7 @@ public class DnaTest
    * @throws IOException
    */
   @Test
-  public void testTranslatCdna_hiddenColumns() throws IOException
+  public void testTranslateCdna_hiddenColumns() throws IOException
   {
     AlignmentI alf = new FormatAdapter().readFile(fasta,
             FormatAdapter.PASTE, "FASTA");
@@ -188,6 +190,15 @@ public class DnaTest
   }
 
   /**
+   * Use this test to help debug into any cases of interest.
+   */
+  @Test
+  public void testCompareCodonPos_oneOnly()
+  {
+    assertFollows("-AA--A", "G--GG"); // 2 shifted seq2, 3 shifted seq1
+  }
+
+  /**
    * Tests for method that compares 'alignment' of two codon position triplets.
    */
   @Test
@@ -196,10 +207,8 @@ public class DnaTest
     /*
      * Returns 0 for any null argument
      */
-    assertEquals(0, Dna.compareCodonPos(new int[]
-      { 1, 2, 3 }, null));
-    assertEquals(0, Dna.compareCodonPos(null, new int[]
-      { 1, 2, 3 }));
+    assertEquals(0, Dna.compareCodonPos(new AlignedCodon(1, 2, 3), null));
+    assertEquals(0, Dna.compareCodonPos(null, new AlignedCodon(1, 2, 3)));
 
     /*
      * Work through 27 combinations. First 9 cases where first position matches.
@@ -209,10 +218,9 @@ public class DnaTest
     assertPrecedes("AAA", "GG-G"); // 2 matches, 3 shifted seq2
     assertFollows("A-AA", "GG-G"); // 2 shifted seq1, 3 matches
     assertFollows("A-A-A", "GG-G"); // 2 shifted seq1, 3 shifted seq1
-    // TODO is this right?
     assertPrecedes("A-AA", "GG--G"); // 2 shifted seq1, 3 shifted seq2
     assertPrecedes("AA-A", "G-GG"); // 2 shifted seq2, 3 matches
-    assertPrecedes("AA--A", "G-GG"); // 2 shifted seq2, 3 shifted seq1
+    assertFollows("AA--A", "G-GG"); // 2 shifted seq2, 3 shifted seq1
     assertPrecedes("AAA", "G-GG"); // 2 shifted seq2, 3 shifted seq2
 
     /*
@@ -220,13 +228,14 @@ public class DnaTest
      */
     assertFollows("-AAA", "G-GG"); // 2 and 3 match
     assertFollows("-AA-A", "G-GG"); // 2 matches, 3 shifted seq1
-    assertPrecedes("-AAA", "G-G-G"); // 2 matches, 3 shifted seq2
+    // 'enclosing' case: pick first to start precedes
+    assertFollows("-AAA", "G-G-G"); // 2 matches, 3 shifted seq2
     assertFollows("-A-AA", "G-G-G"); // 2 shifted seq1, 3 matches
     assertFollows("-A-A-A", "G-G-G"); // 2 shifted seq1, 3 shifted seq1
-    // is this right? codon2 ends after codon1
-    assertPrecedes("-A-AA", "G-G--G"); // 2 shifted seq1, 3 shifted seq2
-    assertPrecedes("-AA-A", "G--GG"); // 2 shifted seq2, 3 matches
-    assertPrecedes("-AA--A", "G--GG"); // 2 shifted seq2, 3 shifted seq1
+    // 'enclosing' case: pick first to start precedes
+    assertFollows("-A-AA", "G-G--G"); // 2 shifted seq1, 3 shifted seq2
+    assertFollows("-AA-A", "G--GG"); // 2 shifted seq2, 3 matches
+    assertFollows("-AA--A", "G--GG"); // 2 shifted seq2, 3 shifted seq1
     assertPrecedes("-AAA", "G--GG"); // 2 shifted seq2, 3 shifted seq2
 
     /*
@@ -236,31 +245,126 @@ public class DnaTest
     assertPrecedes("A-A-A", "-GGG"); // 2 matches, 3 shifted seq1
     assertPrecedes("A-AA", "-GG-G"); // 2 matches, 3 shifted seq2
     assertPrecedes("A--AA", "-GG-G"); // 2 shifted seq1, 3 matches
-    assertPrecedes("A--AA", "-GGG"); // 2 shifted seq1, 3 shifted seq1
+    // 'enclosing' case with middle base deciding:
+    assertFollows("A--AA", "-GGG"); // 2 shifted seq1, 3 shifted seq1
     assertPrecedes("A--AA", "-GG--G"); // 2 shifted seq1, 3 shifted seq2
     assertPrecedes("AA-A", "-GGG"); // 2 shifted seq2, 3 matches
     assertPrecedes("AA--A", "-GGG"); // 2 shifted seq2, 3 shifted seq1
     assertPrecedes("AAA", "-GGG"); // 2 shifted seq2, 3 shifted seq2
+  }
+
+  /**
+   * This test generates a random cDNA alignment and its translation, then
+   * reorders the cDNA and retranslates, and verifies that the translations are
+   * the same (apart from ordering).
+   */
+  @Test
+  public void testTranslateCdna_sequenceOrderIndependent()
+  {
+    /*
+     * Generate cDNA - 8 sequences of 12 bases each.
+     */
+    AlignmentI cdna = new DnaAlignmentGenerator().generate(12, 8, 97, 5, 5);
+    ColumnSelection cs = new ColumnSelection();
+    AlignViewportI av = new AlignViewport(cdna, cs);
+    Dna dna = new Dna(av, new int[]
+    { 0, cdna.getWidth() - 1 });
+    AlignmentI translated = dna.translateCdna();
 
     /*
-     * two codon positions can each 'precede' the other! the comparison is
-     * biased to the first sequence
+     * Jumble the cDNA sequences and translate.
      */
-    // TODO is this correct?
-    assertPrecedes("-A--AA", "--GGG");
-    assertPrecedes("--AAA", "-A--AA");
+    SequenceI[] sorted = new SequenceI[cdna.getHeight()];
+    final int[] jumbler = new int[]
+    { 6, 7, 3, 4, 2, 0, 1, 5 };
+    int seqNo = 0;
+    for (int i : jumbler)
+    {
+      sorted[seqNo++] = cdna.getSequenceAt(i);
+    }
+    AlignmentI cdnaReordered = new Alignment(sorted);
+    av = new AlignViewport(cdnaReordered, cs);
+    dna = new Dna(av, new int[]
+    { 0, cdna.getWidth() - 1 });
+    AlignmentI translated2 = dna.translateCdna();
+
+    /*
+     * Check translated sequences are the same in both alignments.
+     */
+    System.out.println("Original");
+    System.out.println(translated.toString());
+    System.out.println("Sorted");
+    System.out.println(translated2.toString());
+
+    int sortedSequenceIndex = 0;
+    for (int originalSequenceIndex : jumbler)
+    {
+      final String translation1 = translated.getSequenceAt(
+              originalSequenceIndex).getSequenceAsString();
+      final String translation2 = translated2.getSequenceAt(sortedSequenceIndex)
+              .getSequenceAsString();
+      assertEquals(translation2, translation1);
+      sortedSequenceIndex++;
+    }
+  }
+
+  /**
+   * Test that all the cases in testCompareCodonPos have a 'symmetric'
+   * comparison (without checking the actual comparison result).
+   */
+  @Test
+  public void testCompareCodonPos_isSymmetric()
+  {
+    assertSymmetric("AAA", "GGG");
+    assertSymmetric("AA-A", "GGG");
+    assertSymmetric("AAA", "GG-G");
+    assertSymmetric("A-AA", "GG-G");
+    assertSymmetric("A-A-A", "GG-G");
+    assertSymmetric("A-AA", "GG--G");
+    assertSymmetric("AA-A", "G-GG");
+    assertSymmetric("AA--A", "G-GG");
+    assertSymmetric("AAA", "G-GG");
+    assertSymmetric("-AAA", "G-GG");
+    assertSymmetric("-AA-A", "G-GG");
+    assertSymmetric("-AAA", "G-G-G");
+    assertSymmetric("-A-AA", "G-G-G");
+    assertSymmetric("-A-A-A", "G-G-G");
+    assertSymmetric("-A-AA", "G-G--G");
+    assertSymmetric("-AA-A", "G--GG");
+    assertSymmetric("-AA--A", "G--GG");
+    assertSymmetric("-AAA", "G--GG");
+    assertSymmetric("A-AA", "-GGG");
+    assertSymmetric("A-A-A", "-GGG");
+    assertSymmetric("A-AA", "-GG-G");
+    assertSymmetric("A--AA", "-GG-G");
+    assertSymmetric("A--AA", "-GGG");
+    assertSymmetric("A--AA", "-GG--G");
+    assertSymmetric("AA-A", "-GGG");
+    assertSymmetric("AA--A", "-GGG");
+    assertSymmetric("AAA", "-GGG");
+  }
+
+  private void assertSymmetric(String codon1, String codon2)
+  {
+    assertEquals("Comparison of '" + codon1 + "' and '" + codon2
+            + " not symmetric", Integer.signum(compare(codon1, codon2)),
+            -Integer.signum(compare(codon2, codon1)));
   }
 
   /**
    * Assert that the first sequence should map to the same position as the
-   * second in a translated alignment
+   * second in a translated alignment. Also checks that this is true if the
+   * order of the codons is reversed.
    * 
    * @param codon1
    * @param codon2
    */
   private void assertMatches(String codon1, String codon2)
   {
-    assertEquals("Expected match (0)", 0, compare(codon1, codon2));
+    assertEquals("Expected '" + codon1 + "' matches '" + codon2 + "'", 0,
+            compare(codon1, codon2));
+    assertEquals("Expected '" + codon2 + "' matches '" + codon1 + "'", 0,
+            compare(codon2, codon1));
   }
 
   /**
@@ -272,7 +376,8 @@ public class DnaTest
    */
   private void assertPrecedes(String codon1, String codon2)
   {
-    assertEquals("Expected precedes (-1)", -1, compare(codon1, codon2));
+    assertEquals("Expected '" + codon1 + "'  precedes '" + codon2 + "'",
+            -1, compare(codon1, codon2));
   }
 
   /**
@@ -284,7 +389,8 @@ public class DnaTest
    */
   private void assertFollows(String codon1, String codon2)
   {
-    assertEquals("Expected follows (1)", 1, compare(codon1, codon2));
+    assertEquals("Expected '" + codon1 + "'  follows '" + codon2 + "'", 1,
+            compare(codon1, codon2));
   }
 
   /**
@@ -297,10 +403,10 @@ public class DnaTest
    */
   private int compare(String s1, String s2)
   {
-    final int[] cd1 = convertCodon(s1);
-    final int[] cd2 = convertCodon(s2);
-    System.out.println("K: " + s1 + "  " + Arrays.toString(cd1));
-    System.out.println("G: " + s2 + "  " + Arrays.toString(cd2));
+    final AlignedCodon cd1 = convertCodon(s1);
+    final AlignedCodon cd2 = convertCodon(s2);
+    System.out.println("K: " + s1 + "  " + cd1.toString());
+    System.out.println("G: " + s2 + "  " + cd2.toString());
     System.out.println();
     return Dna.compareCodonPos(cd1, cd2);
   }
@@ -312,18 +418,18 @@ public class DnaTest
    * @param s
    * @return
    */
-  private int[] convertCodon(String s)
+  private AlignedCodon convertCodon(String s)
   {
-    int[] result = new int[3];
+    int[] codon = new int[3];
     int i = 0;
     for (int j = 0; j < s.length(); j++)
     {
       if (s.charAt(j) != '-')
       {
-        result[i++] = j;
+        codon[i++] = j;
       }
     }
-    return result;
+    return new AlignedCodon(codon[0], codon[1], codon[2]);
   }
 
   /**
@@ -332,8 +438,8 @@ public class DnaTest
   @Test
   public void testConvertCodon()
   {
-    assertEquals("[0, 1, 2]", Arrays.toString(convertCodon("AAA")));
-    assertEquals("[0, 2, 5]", Arrays.toString(convertCodon("A-A--A")));
-    assertEquals("[1, 3, 4]", Arrays.toString(convertCodon("-A-AA-")));
+    assertEquals("[0, 1, 2]", convertCodon("AAA").toString());
+    assertEquals("[0, 2, 5]", convertCodon("A-A--A").toString());
+    assertEquals("[1, 3, 4]", convertCodon("-A-AA-").toString());
   }
 }