JAL-2154 additional test that removing a duplicate sequence reinstates dataset refere...
[jalview.git] / test / jalview / datamodel / AlignmentTest.java
index 09645fd..fe76df5 100644 (file)
@@ -27,6 +27,7 @@ import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping;
 import jalview.io.AppletFormatAdapter;
 import jalview.io.FormatAdapter;
 import jalview.util.MapList;
@@ -103,6 +104,29 @@ public class AlignmentTest
   }
 
   /**
+   * assert wrapper: tests all references in the given alignment are consistent
+   * 
+   * @param alignment
+   */
+  public static void assertAlignmentDatasetRefs(AlignmentI alignment)
+  {
+    verifyAlignmentDatasetRefs(alignment, true, null);
+  }
+
+  /**
+   * assert wrapper: tests all references in the given alignment are consistent
+   * 
+   * @param alignment
+   * @param message
+   *          - prefixed to any assert failed messages
+   */
+  public static void assertAlignmentDatasetRefs(AlignmentI alignment,
+          String message)
+  {
+    verifyAlignmentDatasetRefs(alignment, true, message);
+  }
+
+  /**
    * verify sequence and dataset references are properly contained within
    * dataset
    * 
@@ -110,11 +134,22 @@ public class AlignmentTest
    *          - the alignmentI object to verify (either alignment or dataset)
    * @param raiseAssert
    *          - when set, testng assertions are raised.
+   *          @param message
+   *          - null or a string message to prepend to the assert failed messages.
    * @return true if alignment references were in order, otherwise false.
    */
   public static boolean verifyAlignmentDatasetRefs(AlignmentI alignment,
-          boolean raiseAssert)
+          boolean raiseAssert, String message)
   {
+    if (message==null) { message = ""; }
+    if (alignment == null)
+    {
+      if (raiseAssert)
+      {
+        Assert.fail(message+"Alignment for verification was null.");
+      }
+      return false;
+    }
     if (alignment.getDataset() != null)
     {
       AlignmentI dataset = alignment.getDataset();
@@ -126,7 +161,7 @@ public class AlignmentTest
         {
           if (raiseAssert)
           {
-            Assert.fail("Alignment contained a sequence who's dataset sequence has a second dataset reference.");
+            Assert.fail(message+" Alignment contained a sequence who's dataset sequence has a second dataset reference.");
           }
           return false;
         }
@@ -134,23 +169,37 @@ public class AlignmentTest
         {
           if (raiseAssert)
           {
-            Assert.fail("Alignment contained a sequence who's dataset sequence was not in the dataset.");
+            Assert.fail(message+" Alignment contained a sequence who's dataset sequence was not in the dataset.");
           }
           return false;
         }
       }
-      return verifyAlignmentDatasetRefs(alignment.getDataset(), raiseAssert);
+      return verifyAlignmentDatasetRefs(alignment.getDataset(), raiseAssert, message);
     }
     else
     {
+      int dsp = -1;
       // verify all dataset sequences
       for (SequenceI seqds : alignment.getSequences())
       {
+        dsp++;
         if (seqds.getDatasetSequence() != null)
         {
           if (raiseAssert)
           {
-            Assert.fail("Dataset contained a sequence with non-null dataset reference (ie not a dataset sequence!)");
+            Assert.fail(message+" Dataset contained a sequence with non-null dataset reference (ie not a dataset sequence!)");
+          }
+          return false;
+        }
+        int foundp = alignment.findIndex(seqds);
+        if (foundp != dsp)
+        {
+          if (raiseAssert)
+          {
+            Assert.fail(message
+                    + " Dataset sequence array contains a reference at "
+                    + dsp + " to a sequence first seen at " + foundp + " ("
+                    + seqds.toString() + ")");
           }
           return false;
         }
@@ -167,7 +216,7 @@ public class AlignmentTest
                 {
                   if (raiseAssert)
                   {
-                    Assert.fail("DBRefEntry for sequence in alignment had map to sequence which was not a dataset sequence");
+                    Assert.fail(message+" DBRefEntry for sequence in alignment had map to sequence which was not a dataset sequence");
                   }
                   return false;
 
@@ -176,7 +225,7 @@ public class AlignmentTest
                 {
                   if (raiseAssert)
                   {
-                    Assert.fail("DBRefEntry for sequence in alignment had map to sequence not in dataset");
+                    Assert.fail(message+" DBRefEntry for sequence in alignment had map to sequence not in dataset");
                   }
                   return false;
                 }
@@ -185,6 +234,50 @@ public class AlignmentTest
           }
         }
       }
+      // finally, verify codonmappings involve only dataset sequences.
+      if (alignment.getCodonFrames() != null)
+      {
+        for (AlignedCodonFrame alc : alignment.getCodonFrames())
+        {
+          for (SequenceToSequenceMapping ssm : alc.getMappings())
+          {
+            if (ssm.getFromSeq().getDatasetSequence() != null)
+            {
+              if (raiseAssert)
+              {
+                Assert.fail(message+" CodonFrame-SSM-FromSeq is not a dataset sequence");
+              }
+              return false;
+            }
+            if (alignment.findIndex(ssm.getFromSeq()) == -1)
+            {
+
+              if (raiseAssert)
+              {
+                Assert.fail(message+" CodonFrame-SSM-FromSeq is not contained in dataset");
+              }
+              return false;
+            }
+            if (ssm.getMapping().getTo().getDatasetSequence() != null)
+            {
+              if (raiseAssert)
+              {
+                Assert.fail(message+" CodonFrame-SSM-Mapping-ToSeq is not a dataset sequence");
+              }
+              return false;
+            }
+            if (alignment.findIndex(ssm.getMapping().getTo()) == -1)
+            {
+
+              if (raiseAssert)
+              {
+                Assert.fail(message+" CodonFrame-SSM-Mapping-ToSeq is not contained in dataset");
+              }
+              return false;
+            }
+          }
+        }
+      }
     }
     return true; // all relationships verified!
   }
@@ -205,7 +298,7 @@ public class AlignmentTest
       try
       {
 
-        Assert.assertTrue(verifyAlignmentDatasetRefs(al, true),
+        Assert.assertTrue(verifyAlignmentDatasetRefs(al, true, null),
                 "Valid test alignment failed when raiseAsserts enabled:"
                         + msg);
       } catch (AssertionError ae)
@@ -216,24 +309,29 @@ public class AlignmentTest
                         + msg, ae);
       }
       // also check validation passes with asserts disabled
-      Assert.assertTrue(verifyAlignmentDatasetRefs(al, false),
-              "Valid test alignment failed when raiseAsserts disabled:"
+      Assert.assertTrue(verifyAlignmentDatasetRefs(al, false, null),
+              "Valid test alignment tested false when raiseAsserts disabled:"
                       + msg);
     }
     else
     {
+      boolean assertRaised = false;
       try
       {
-        Assert.assertFalse(verifyAlignmentDatasetRefs(al, true));
-        Assert.fail("Invalid test alignment passed but no assertion raised when raiseAsserts enabled:"
-                + msg);
+        verifyAlignmentDatasetRefs(al, true, null);
       } catch (AssertionError ae)
       {
         // expected behaviour
+        assertRaised = true;
+      }
+      if (!assertRaised)
+      {
+        Assert.fail("Invalid test alignment passed when raiseAsserts enabled:"
+                + msg);
       }
       // also check validation passes with asserts disabled
-      Assert.assertFalse(verifyAlignmentDatasetRefs(al, false),
-              "Invalid test alignment passed when raiseAsserts disabled:"
+      Assert.assertFalse(verifyAlignmentDatasetRefs(al, false, null),
+              "Invalid test alignment tested true when raiseAsserts disabled:"
                       + msg);
     }
   }
@@ -293,6 +391,38 @@ public class AlignmentTest
             al,
             false,
             "verify passed when a dbref with map to sequence outside of dataset was added");
+    // make the verify pass by adding the outsider back in
+    al.getDataset().addSequence(sqout);
+    assertVerifyAlignment(al, true,
+            "verify should have passed after adding dbref->to sequence in to dataset");
+    // and now the same for a codon mapping...
+    SequenceI sqanotherout = new Sequence("sqanotherout",
+            "aggtutaggcagcagcag");
+
+    AlignedCodonFrame alc = new AlignedCodonFrame();
+    alc.addMap(sqanotherout, sqnew, new MapList(new int[] { 1, 6 },
+            new int[] { 1, 18 }, 3, 1));
+
+    al.addCodonFrame(alc);
+    Assert.assertEquals(al.getDataset().getCodonFrames().size(), 1);
+
+    assertVerifyAlignment(
+            al,
+            false,
+            "verify passed when alCodonFrame mapping to sequence outside of dataset was added");
+    // make the verify pass by adding the outsider back in
+    al.getDataset().addSequence(sqanotherout);
+    assertVerifyAlignment(
+            al,
+            true,
+            "verify should have passed once all sequences involved in alCodonFrame were added to dataset");
+    al.getDataset().addSequence(sqanotherout);
+    assertVerifyAlignment(al, false,
+            "verify should have failed when a sequence was added twice to the dataset");
+    al.getDataset().deleteSequence(sqanotherout);
+    assertVerifyAlignment(al, true,
+            "verify should have passed after duplicate entry for sequence was removed");
+
   }
   /*
    * Read in Stockholm format test data including secondary structure
@@ -730,6 +860,27 @@ public class AlignmentTest
   }
 
   @Test(groups = "Functional")
+  public void testAddSequencePreserveDatasetIntegrity()
+  {
+    Sequence seq = new Sequence("testSeq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+    Alignment align = new Alignment(new SequenceI[] { seq });
+    align.createDatasetAlignment();
+    AlignmentI ds = align.getDataset();
+    SequenceI copy = new Sequence(seq);
+    copy.insertCharAt(3, 5, '-');
+    align.addSequence(copy);
+    Assert.assertEquals(align.getDataset().getHeight(), 1,
+            "Dataset shouldn't have more than one sequence.");
+
+    Sequence seq2 = new Sequence("newtestSeq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+    align.addSequence(seq2);
+    Assert.assertEquals(align.getDataset().getHeight(), 2,
+            "Dataset should now have two sequences.");
+
+    assertAlignmentDatasetRefs(align,
+            "addSequence broke dataset reference integrity");
+  }
+  @Test(groups = "Functional")
   public void getVisibleStartAndEndIndexTest()
   {
     Sequence seq = new Sequence("testSeq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");