X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=test%2Fjalview%2Fdatamodel%2Ffeatures%2FNCListTest.java;h=3561a78c2e8b62197a328ae77435119f0adbf33a;hb=e957648a02a9e25795a92a4f39a5e3c1e7adb230;hp=e0d3659e7e37498c3953eccd239dbc301144c412;hpb=fdea751663ec46a587cfdf45bfae9ec667043efb;p=jalview.git diff --git a/test/jalview/datamodel/features/NCListTest.java b/test/jalview/datamodel/features/NCListTest.java index e0d3659..3561a78 100644 --- a/test/jalview/datamodel/features/NCListTest.java +++ b/test/jalview/datamodel/features/NCListTest.java @@ -12,6 +12,8 @@ import java.util.Comparator; import java.util.List; import java.util.Random; +import junit.extensions.PA; + import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -164,45 +166,137 @@ public class NCListTest } /** - * Do a number of pseudo-random (reproducible) builds of an NCList, with - * checks for validity of the data structure, and searches for (pseudo-random) - * overlap ranges, for greater (but not perfect!) confidence that corner cases - * are being handled correctly. + * Do a number of pseudo-random (reproducible) builds of an NCList, to + * exercise as many methods of the class as possible while generating the + * range of possible structure topologies + * */ @Test(groups = "Functional", dataProvider = "scalesOfLife") - public void testAdd_FindOverlaps_pseudoRandom(Integer scale) + public void test_pseudoRandom(Integer scale) { - NCList ncl = new NCList(); - int count = 0; - List ranges = new ArrayList(scale); + NCList ncl = new NCList(); + List features = new ArrayList(scale); - for (int i = 0; i < scale; i++) + testAdd_pseudoRandom(scale, ncl, features); + + /* + * sort the list of added ranges - this doesn't affect the test, + * just makes it easier to inspect the data in the debugger + */ + Collections.sort(features, sorter); + + testFindOverlaps_pseudoRandom(ncl, scale, features); + + testDelete_pseudoRandom(ncl, features); + } + + /** + * Pick randomly selected entries to delete in turn, checking the NCList size + * and validity at each stage, until it is empty + * + * @param ncl + * @param features + */ + protected void testDelete_pseudoRandom(NCList ncl, + List features) + { + int deleted = 0; + + while (!features.isEmpty()) + { + assertEquals(ncl.size(), features.size()); + int toDelete = random.nextInt(features.size()); + SequenceFeature entry = features.get(toDelete); + assertTrue(ncl.contains(entry), String.format( + "NCList doesn't contain entry [%d] '%s'!", deleted, + entry.toString())); + + ncl.delete(entry); + assertFalse(ncl.contains(entry), String.format( + "NCList still contains deleted entry [%d] '%s'!", deleted, + entry.toString())); + features.remove(toDelete); + deleted++; + + assertTrue(ncl.isValid(), String.format( + "NCList invalid after %d deletions, last deleted was '%s'", + deleted, entry.toString())); + + /* + * brute force check that deleting one entry didn't delete any others + */ + for (int i = 0; i < features.size(); i++) + { + SequenceFeature sf = features.get(i); + assertTrue(ncl.contains(sf), String.format( + "NCList doesn't contain entry [%d] %s after deleting '%s'!", + i, sf.toString(), entry.toString())); + } + } + assertEquals(ncl.size(), 0); // all gone + } + + /** + * Randomly generate entries and add them to the NCList, checking its validity + * and size at each stage. A few entries should be duplicates (by equals test) + * so not get added. + * + * @param scale + * @param ncl + * @param features + */ + protected void testAdd_pseudoRandom(Integer scale, + NCList ncl, + List features) + { + int count = 0; + final int size = 50; + + for (int i = 0; i < size; i++) { int r1 = random.nextInt(scale + 1); int r2 = random.nextInt(scale + 1); int from = Math.min(r1, r2); int to = Math.max(r1, r2); - Range range = new Range(from, to); - ncl.add(range); - ranges.add(range); + + /* + * choice of two feature values means that occasionally an identical + * feature may be generated, in which case it should not be added + */ + float value = (float) i % 2; + SequenceFeature feature = new SequenceFeature("Pfam", "", from, to, + value, "group"); + + /* + * add to NCList - with duplicate entries (by equals) disallowed + */ + ncl.add(feature, false); + if (features.contains(feature)) + { + System.out.println("Duplicate feature generated " + + feature.toString()); + } + else + { + features.add(feature); + count++; + } /* * check list format is valid at each stage of its construction */ assertTrue(ncl.isValid(), String.format("Failed for scale = %d, i=%d", scale, i)); - count++; - assertEquals(ncl.getSize(), count); + assertEquals(ncl.size(), count); } // System.out.println(ncl.prettyPrint()); - - /* - * sort the list of added ranges - this doesn't affect the test, - * just makes it easier to inspect the data in the debugger - */ - Collections.sort(ranges, sorter); - - testFindOverlaps(ncl, scale, ranges); } /** @@ -213,11 +307,12 @@ public class NCListTest * the NCList to query * @param scale * ncl maximal range is [0, scale] - * @param ranges + * @param features * a list of the ranges stored in ncl */ - protected void testFindOverlaps(NCList ncl, int scale, - List ranges) + protected void testFindOverlaps_pseudoRandom(NCList ncl, + int scale, + List features) { int halfScale = scale / 2; int minIterations = 20; @@ -273,7 +368,7 @@ public class NCListTest } } - verifyFindOverlaps(ncl, from, to, ranges); + verifyFindOverlaps(ncl, from, to, features); } } @@ -284,20 +379,20 @@ public class NCListTest * @param ncl * @param from * @param to - * @param ranges + * @param features */ - protected void verifyFindOverlaps(NCList ncl, int from, int to, - List ranges) + protected void verifyFindOverlaps(NCList ncl, int from, + int to, List features) { - List overlaps = ncl.findOverlaps(from, to); + List overlaps = ncl.findOverlaps(from, to); /* * check returned entries do indeed overlap from-to range */ - for (Range r : overlaps) + for (ContiguousI sf : overlaps) { - int begin = r.getBegin(); - int end = r.getEnd(); + int begin = sf.getBegin(); + int end = sf.getEnd(); assertTrue(begin <= to && end >= from, String.format( "[%d, %d] does not overlap query range [%d, %d]", begin, end, from, to)); @@ -307,13 +402,13 @@ public class NCListTest * check overlapping ranges are included in the results * (the test above already shows non-overlapping ranges are not) */ - for (Range r : ranges) + for (ContiguousI sf : features) { - int begin = r.getBegin(); - int end = r.getEnd(); + int begin = sf.getBegin(); + int end = sf.getEnd(); if (begin <= to && end >= from) { - boolean found = overlaps.contains(r); + boolean found = overlaps.contains(sf); assertTrue(found, String.format( "[%d, %d] missing in query range [%d, %d]", begin, end, from, to)); @@ -396,7 +491,7 @@ public class NCListTest assertSame(features.getEntries().get(1), sf1); assertTrue(features.delete(sf1)); // first match only is deleted assertTrue(features.contains(sf1)); - assertEquals(features.getSize(), 1); + assertEquals(features.size(), 1); assertTrue(features.delete(sf1)); assertTrue(features.getEntries().isEmpty()); } @@ -468,4 +563,118 @@ public class NCListTest assertTrue(ncl.contains(sf5)); assertTrue(ncl.contains(sf6)); } + + @Test(groups = "Functional") + public void testIsValid() + { + List ranges = new ArrayList(); + Range r1 = new Range(40, 50); + ranges.add(r1); + NCList ncl = new NCList(ranges); + assertTrue(ncl.isValid()); + + Range r2 = new Range(42, 44); + ncl.add(r2); + assertTrue(ncl.isValid()); + Range r3 = new Range(46, 48); + ncl.add(r3); + assertTrue(ncl.isValid()); + Range r4 = new Range(43, 43); + ncl.add(r4); + assertTrue(ncl.isValid()); + + assertEquals(ncl.toString(), "[40-50 [42-44 [43-43], 46-48]]"); + assertTrue(ncl.isValid()); + + PA.setValue(r1, "start", 43); + assertFalse(ncl.isValid()); // r2 not inside r1 + PA.setValue(r1, "start", 40); + assertTrue(ncl.isValid()); + + PA.setValue(r3, "start", 41); + assertFalse(ncl.isValid()); // r3 should precede r2 + PA.setValue(r3, "start", 46); + assertTrue(ncl.isValid()); + + PA.setValue(r4, "start", 41); + assertFalse(ncl.isValid()); // r4 not inside r2 + PA.setValue(r4, "start", 43); + assertTrue(ncl.isValid()); + + PA.setValue(r4, "start", 44); + assertFalse(ncl.isValid()); // r4 has reverse range + } + + @Test(groups = "Functional") + public void testPrettyPrint() + { + /* + * construct NCList from a list of ranges + * they are sorted then assembled into NCList subregions + * notice that 42-42 end up inside 41-46 + */ + List ranges = new ArrayList(); + ranges.add(new Range(40, 50)); + ranges.add(new Range(45, 55)); + ranges.add(new Range(40, 45)); + ranges.add(new Range(41, 46)); + ranges.add(new Range(42, 42)); + ranges.add(new Range(42, 42)); + NCList ncl = new NCList(ranges); + assertTrue(ncl.isValid()); + assertEquals(ncl.toString(), + "[40-50 [40-45], 41-46 [42-42 [42-42]], 45-55]"); + String expected = "40-50\n 40-45\n41-46\n 42-42\n 42-42\n45-55\n"; + assertEquals(ncl.prettyPrint(), expected); + + /* + * repeat but now add ranges one at a time + * notice that 42-42 end up inside 40-50 so we get + * a different but equal valid NCList structure + */ + ranges.clear(); + ncl = new NCList(ranges); + ncl.add(new Range(40, 50)); + ncl.add(new Range(45, 55)); + ncl.add(new Range(40, 45)); + ncl.add(new Range(41, 46)); + ncl.add(new Range(42, 42)); + ncl.add(new Range(42, 42)); + assertTrue(ncl.isValid()); + assertEquals(ncl.toString(), + "[40-50 [40-45 [42-42 [42-42]], 41-46], 45-55]"); + expected = "40-50\n 40-45\n 42-42\n 42-42\n 41-46\n45-55\n"; + assertEquals(ncl.prettyPrint(), expected); + } + + /** + * A test that shows different valid trees can be constructed from the same + * set of ranges, depending on the order of construction + */ + @Test(groups = "Functional") + public void testConstructor_alternativeTrees() + { + List ranges = new ArrayList(); + ranges.add(new Range(10, 60)); + ranges.add(new Range(20, 30)); + ranges.add(new Range(40, 50)); + + /* + * constructor with greedy traversal of sorted ranges to build nested + * containment lists results in 20-30 inside 10-60, 40-50 a sibling + */ + NCList ncl = new NCList(ranges); + assertEquals(ncl.toString(), "[10-60 [20-30], 40-50]"); + assertTrue(ncl.isValid()); + + /* + * adding ranges one at a time results in 40-50 + * a sibling of 20-30 inside 10-60 + */ + ncl = new NCList(new Range(10, 60)); + ncl.add(new Range(20, 30)); + ncl.add(new Range(40, 50)); + assertEquals(ncl.toString(), "[10-60 [20-30, 40-50]]"); + assertTrue(ncl.isValid()); + } }