You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by re...@apache.org on 2020/11/09 07:37:07 UTC

[uima-uimaj] branch bugfix/UIMA-6294-SelectFS-at-annotation-does-not-return-the-correct-result created (now d031cce)

This is an automated email from the ASF dual-hosted git repository.

rec pushed a change to branch bugfix/UIMA-6294-SelectFS-at-annotation-does-not-return-the-correct-result
in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git.


      at d031cce  [UIMA-6294] SelectFS.at(annotation) does not return the correct result

This branch includes the following new commits:

     new d8ae851  [NO JIRA] Remove warning about two SLF4J backends when running tests.
     new d031cce  [UIMA-6294] SelectFS.at(annotation) does not return the correct result

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[uima-uimaj] 02/02: [UIMA-6294] SelectFS.at(annotation) does not return the correct result

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rec pushed a commit to branch bugfix/UIMA-6294-SelectFS-at-annotation-does-not-return-the-correct-result
in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git

commit d031cceefcafe351ed27adce29a922f5a0688816
Author: Richard Eckart de Castilho <re...@apache.org>
AuthorDate: Mon Nov 9 08:37:06 2020 +0100

    [UIMA-6294] SelectFS.at(annotation) does not return the correct result
    
    - Added random type hierarchies to the random tests
    - Added overlapping-right / overlapping-left as valid results for T6 and T7 respectively because that makes sense and aligns with the behavior of "includeAnnotationsEndingBeyondBounds" in SelectFS
    - Added more tests
    - Change random tests that are using an annotation as anchor (i.e. not offsets) to ensure that the annotation itself is not returned by SelectFS by adjusting the filter condition adding "x != y"
    - Increase number of random test iterations to 100
    - Reset the CAS at the beginning of a random test iteration (had forgotted to do this before....)
    - Consolidate code in the Subiterator, merging the new code that was added to moveToStart "case coveredBy" with the code in "adjustForStrictOrCoveringAndBoundSkip_backwards" and moved other common code to maybeAdjustForAmbiguityAndIgnoringTypePriorities_forward - use the code also when doing a "select at" which eventually fixes UIMA-6294
---
 .../annotation_predicates/annotation-relations.ods | Bin 18737 -> 67477 bytes
 .../annotation_predicates/annotation-relations.png | Bin 375980 -> 328700 bytes
 .../java/org/apache/uima/cas/impl/Subiterator.java | 165 ++++++++++------
 .../apache/uima/cas/text/AnnotationPredicates.java |  12 +-
 .../org/apache/uima/cas/impl/SelectFsAssert.java   |  73 +++++--
 .../cas/impl/SelectFsPredicateAlignmentTest.java   | 209 ++++++++++----------
 .../org/apache/uima/cas/impl/SelectFsTest.java     | 211 ++++++++++++++++++++-
 .../uima/cas/text/AnnotationPredicateTestData.java |  56 +++---
 8 files changed, 514 insertions(+), 212 deletions(-)

diff --git a/uima-docbook-v3-users-guide/src/docbook/images/version_3_users_guide/annotation_predicates/annotation-relations.ods b/uima-docbook-v3-users-guide/src/docbook/images/version_3_users_guide/annotation_predicates/annotation-relations.ods
index 3d06ddb..572913e 100644
Binary files a/uima-docbook-v3-users-guide/src/docbook/images/version_3_users_guide/annotation_predicates/annotation-relations.ods and b/uima-docbook-v3-users-guide/src/docbook/images/version_3_users_guide/annotation_predicates/annotation-relations.ods differ
diff --git a/uima-docbook-v3-users-guide/src/docbook/images/version_3_users_guide/annotation_predicates/annotation-relations.png b/uima-docbook-v3-users-guide/src/docbook/images/version_3_users_guide/annotation_predicates/annotation-relations.png
index eb44015..ed0ff6d 100644
Binary files a/uima-docbook-v3-users-guide/src/docbook/images/version_3_users_guide/annotation_predicates/annotation-relations.png and b/uima-docbook-v3-users-guide/src/docbook/images/version_3_users_guide/annotation_predicates/annotation-relations.png differ
diff --git a/uimaj-core/src/main/java/org/apache/uima/cas/impl/Subiterator.java b/uimaj-core/src/main/java/org/apache/uima/cas/impl/Subiterator.java
index 6f49798..84e7e4f 100644
--- a/uimaj-core/src/main/java/org/apache/uima/cas/impl/Subiterator.java
+++ b/uimaj-core/src/main/java/org/apache/uima/cas/impl/Subiterator.java
@@ -19,6 +19,10 @@
 
 package org.apache.uima.cas.impl;
 
+import static org.apache.uima.cas.impl.Subiterator.BoundsUse.coveredBy;
+import static org.apache.uima.cas.impl.Subiterator.BoundsUse.covering;
+import static org.apache.uima.cas.impl.Subiterator.BoundsUse.sameBeginEnd;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -292,8 +296,8 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
         
     this.jcas = (JCasImpl) ll_getIndex().getCasImpl().getJCas();
 
-    isDoEqualsTest = (boundsUse == BoundsUse.coveredBy || boundsUse == BoundsUse.sameBeginEnd) && 
-        this.boundingAnnot._inSetSortedIndex();
+    isDoEqualsTest = (boundsUse == coveredBy || boundsUse == sameBeginEnd || boundsUse == covering)
+        && this.boundingAnnot._inSetSortedIndex();
 
     if (boundsUse == BoundsUse.covering) {
       // compute start position and isEmpty setting
@@ -428,6 +432,9 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
       
     case sameBeginEnd:
       it.moveToNoReinit(boundingAnnot);
+
+      maybeAdjustForAmbiguityAndIgnoringTypePriorities_forward();
+      
       if (it.isValid()) {
         // no need for mimic position if type priorities are in effect; moveTo will either
         //   find equal match and position to left most of the equal, including types, or
@@ -449,42 +456,7 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
     case coveredBy:
       it.moveToNoReinit(boundingAnnot);
       
-      // If the iterator is unambiguous, we must respect the index order. It really depends on the
-      // exact entry point into the index that the bounding annotation gives us which annotations
-      // will be returned and which are skipped. But if the iterator is unambiguous and if we also
-      // ignore the type priorities, then we need to seek backwards in the index as to not skip any
-      // potentially relevant annotations at the same position as the bounding annotation but 
-      // (randomly) appearing before the bounding annotation in the index.
-      if (!isUnambiguous && !isUseTypePriority) {
-        // If the bounding annotation evaluates to being "greater" than any of the annotation in the
-        // index according to the index order, then the iterator comes back invalid. 
-        boolean wasValid = it.isValid();
-        if (!wasValid) {
-            it.moveToLastNoReinit();
-        }
-        
-        // We need to try seeking backwards because we may have skipped covered annotations which
-        // start within the selection range but do not end within it.
-        boolean wentBack = false;
-        while (it.isValid() && it.getNvc().getBegin() >= boundingAnnot.getBegin()) {
-          it.moveToPreviousNvc();
-          wentBack = true;
-        }
-        
-        if (wentBack) {
-          if (!it.isValid()) {
-            it.moveToFirstNoReinit();
-          }
-          else if (it.getNvc().getBegin() < boundingAnnot.getBegin()) {
-            it.moveToNextNvc();
-          }
-        }
-        else if (!wasValid) {
-          // No backwards seeking was performed and the iterator was initially invalid, so we 
-          // invalidate it again
-          makeInvalid();
-        }
-      }
+      maybeAdjustForAmbiguityAndIgnoringTypePriorities_forward();
       
       // If an annotation is present (found), position is on it, and if not,
       // position is at the next annotation that is higher than (or invalid, if there
@@ -898,9 +870,9 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
              // condition true if need to move forwards because current spot is invalid
       while ((begin <= this.boundBegin &&  // stop if go too far
              a._id != boundingAnnot._id &&                                 // stop if hit bounding annot
-             ((end = a.getEnd()) < this.boundEnd || 
+             ((end = a.getEnd()) < this.boundEnd ||
               (end == this.boundEnd && 
-               (lto != null && lto.lessThan(a._getTypeImpl(), this.boundType)))))) {               
+               (lto != null && lto.lessThan(a._getTypeImpl(), this.boundType)))))) {
         it.moveToNextNvc();
         if (! it.isValid()) {
           return;
@@ -949,7 +921,7 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
       //              no type order, bounding annot == same begin end: means all
       //              with type order, bounding annot == same begin end:  some might be in front
       //                with ! isSameBeginEndType  (skip over bounds with == id) <<< no need to test, is done later by caller
-      if (begin < boundBegin || 
+      if (begin < boundBegin ||
           (begin == boundBegin && 
            ((end = a.getEnd()) > boundEnd ||
             (end == boundEnd && lto != null && lto.lessThan(a._getTypeImpl(), boundType))))) {
@@ -1002,7 +974,6 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
    *              or iterator is invalid to start with
    */
   private boolean is_beyond_bounds_chk_coveredByNvc() {
-    
     if (it.getNvc().getBegin() > boundEnd) {
       makeInvalid();
       return true;
@@ -1044,20 +1015,51 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
     }
   }
   
-  private void adjustForStrictOrCoveringAndBoundSkip_backwards() {
-    switch(boundsUse) {
+  private void maybeAdjustForAmbiguityAndIgnoringTypePriorities_forward() {
+    // If the iterator is unambiguous, we must respect the index order. It really depends on the
+    // exact entry point into the index that the bounding annotation gives us which annotations
+    // will be returned and which are skipped.
+    if (isUnambiguous) {
+      return;
+    }
+    
+    // But if the iterator is ambiguous and if we also ignore the type priorities, then we need to
+    // seek backwards in the index as to not skip any potentially relevant annotations at the same
+    // position as the bounding annotation but (randomly) appearing before the bounding annotation
+    // in the index.
+    if (!isUseTypePriority) {
+      boolean wasValid = it.isValid();
 
-    case coveredBy:
-      // handle strict
-      if (isStrict) {
-        while (it.isValid() && it.getNvc().getEnd() > boundEnd) {
-          maybeMoveToPrevBounded();
-        }        
+      // CASE: Previously called "it.moveToNoReinit(boundingAnnot)" moved beyond end of the index
+      //
+      // If the bounding annotation evaluates to being "greater" than any of the annotation in the
+      // index according to the index order, then the iterator comes back invalid. 
+      if (!wasValid) {
+          it.moveToLastNoReinit();
       }
-      // skip over original bound if found
-      while (it.isValid() && equalToBounds(it.getNvc())) {
-        maybeMoveToPrevBounded();   // can be multiple equal to bounds
+      
+      boolean wentBack = adjustForStrictOrCoveringAndBoundSkip_backwards();
+      
+      if (!wentBack && !wasValid) {
+        // No backwards seeking was performed and the iterator was initially invalid, so we 
+        // invalidate it again
+        makeInvalid();
+      }
+    }
+  }
+
+  
+  private boolean adjustForStrictOrCoveringAndBoundSkip_backwards() {
+    boolean wentBack = false;
+    switch(boundsUse) {
+    case coveredBy:
+      // We need to try seeking backwards because we may have skipped covered annotations which
+      // start within the selection range but do not end within it.
+      while (it.isValid() && (it.getNvc().getBegin() >= boundingAnnot.getBegin() || equalToBounds(it.getNvc()))) {
+        it.moveToPreviousNvc();
+        wentBack = true;
       }
+      
       break;
       
     case covering:
@@ -1065,17 +1067,45 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
       // handle skipping cases where the end is < boundEnd
       while (it.isValid() && it.getNvc().getEnd() < boundEnd) {
         maybeMoveToPrevBounded();
+        wentBack = true;
       }
       break;
       
     case sameBeginEnd:
-      while (it.isValid() && equalToBounds(it.getNvc())) {
+      while (
+          it.isValid() && (
+              equalToBounds(it.getNvc()) || 
+              it.getNvc().getBegin() > boundBegin || 
+              (it.getNvc().getBegin() == boundBegin && it.getNvc().getEnd() <= boundEnd)
+          )
+      ) {
         maybeMoveToPrevBounded(); // can be multiple equal to bounds
+        wentBack = true;
       }
       break;
       
-      default:  // same as no bounds case
-    }    
+    default:  // same as no bounds case
+    }
+    
+    if (wentBack) {
+      if (!it.isValid()) {
+        it.moveToFirstNoReinit();
+      }
+      else { 
+        if (it.getNvc().getBegin() < boundingAnnot.getBegin()) {
+          it.moveToNextNvc();
+        }
+        else if (
+            boundsUse == BoundsUse.sameBeginEnd && 
+            it.getNvc().getBegin() == boundingAnnot.getBegin() &&
+            it.getNvc().getEnd() != boundingAnnot.getEnd()
+        ) {
+          it.moveToNextNvc();
+        }
+      }
+    }
+    
+    return wentBack;
   }
   
   /**
@@ -1094,7 +1124,7 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
             fs.getEnd() == boundEnd &&
             fs.getType() == boundType); 
   }
-    
+
   private void maybeSetPrevEnd() {
     if (isUnambiguous && it.isValid()) {
       this.prevEnd = it.getNvc().getEnd();
@@ -1107,6 +1137,19 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
    */
   private boolean adjustForStrictNvc_forward() {
     if (!isStrict) {
+      Annotation item = it.getNvc();
+      while ((item.getBegin() == this.boundEnd && item.getBegin() < item.getEnd()) || equalToBounds(item)) {
+        it.moveToNextNvc();
+        if (!isValid()) {
+          return false;
+        }
+        
+        item = it.getNvc();
+        if (item.getBegin() > this.boundEnd) { // not >= because could of 0 length annot at end
+          makeInvalid();
+          return false;
+        }
+      }
       return true;
     }
     
@@ -1150,8 +1193,6 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
    *     position of end is < span end:
    *       if backward: moveToPrev until get valid position or run out.  if run out, mark invalid
    *       if forward: move to next while position of begin is <= span begin.
-   *      
-   * @param forward true if moving forward
    */
   private void adjustForCovering_forward() {
     if (!it.isValid()) {
@@ -1173,10 +1214,10 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T>
     // skip until get an FS whose end >= boundEnd, it is a candidate.
     //   stop if begin gets too large (going forwards)
     // while test: is true if need to move to skip over a too-small "end"
-    while (it.isValid() && 
-           (begin = (a = it.getNvc()).getBegin()) <= this.boundBegin &&
+    while (it.isValid() && (equalToBounds(a = it.getNvc()) ||
+           (begin = a.getBegin()) <= this.boundBegin &&
            ( (end = a.getEnd()) < this.boundEnd || 
-             (end == this.boundEnd && lto != null && lto.lessThan(a._getTypeImpl(), this.boundType)))) {
+             (end == this.boundEnd && lto != null && lto.lessThan(a._getTypeImpl(), this.boundType))))) {
       it.moveToNextNvc();
     }
   }
diff --git a/uimaj-core/src/main/java/org/apache/uima/cas/text/AnnotationPredicates.java b/uimaj-core/src/main/java/org/apache/uima/cas/text/AnnotationPredicates.java
index 9bf8da1..9b672b7 100644
--- a/uimaj-core/src/main/java/org/apache/uima/cas/text/AnnotationPredicates.java
+++ b/uimaj-core/src/main/java/org/apache/uima/cas/text/AnnotationPredicates.java
@@ -114,12 +114,12 @@ public final class AnnotationPredicates {
   }
 
   public static boolean overlappingAtBegin(int aXBegin, int aXEnd, int aYBegin, int aYEnd) {
-    return aXBegin < aYBegin && aYBegin < aXEnd && aXEnd < aYEnd;
+    return aXBegin < aYBegin && aYBegin < aXEnd && aXEnd <= aYEnd;
   }
 
   public static boolean overlappingAtBegin(AnnotationFS aX, int aYBegin, int aYEnd) {
     int xEnd = aX.getEnd();
-    return aYBegin < xEnd && xEnd < aYEnd && aX.getBegin() < aYBegin;
+    return aYBegin < xEnd && xEnd <= aYEnd && aX.getBegin() < aYBegin;
   }
 
   /**
@@ -134,16 +134,16 @@ public final class AnnotationPredicates {
   public static boolean overlappingAtBegin(AnnotationFS aX, AnnotationFS aY) {
     int xEnd = aX.getEnd();
     int yBegin = aY.getBegin();
-    return yBegin < xEnd && xEnd < aY.getEnd() && aX.getBegin() < yBegin;
+    return yBegin < xEnd && xEnd <= aY.getEnd() && aX.getBegin() < yBegin;
   }
 
   public static boolean overlappingAtEnd(int aXBegin, int aXEnd, int aYBegin, int aYEnd) {
-    return aYBegin < aXBegin && aXBegin < aYEnd && aYEnd < aXEnd;
+    return aYBegin <= aXBegin && aXBegin < aYEnd && aYEnd < aXEnd;
   }
 
   public static boolean overlappingAtEnd(AnnotationFS aX, int aYBegin, int aYEnd) {
     int xBegin = aX.getBegin();
-    return aYBegin < xBegin && xBegin < aYEnd && aYEnd < aX.getEnd();
+    return aYBegin <= xBegin && xBegin < aYEnd && aYEnd < aX.getEnd();
   }
 
   /**
@@ -158,7 +158,7 @@ public final class AnnotationPredicates {
   public static boolean overlappingAtEnd(AnnotationFS aX, AnnotationFS aY) {
     int xBegin = aX.getBegin();
     int yEnd = aY.getEnd();
-    return xBegin < yEnd && aY.getBegin() < xBegin && yEnd < aX.getEnd();
+    return xBegin < yEnd && aY.getBegin() <= xBegin && yEnd < aX.getEnd();
   }
 
   public static boolean following(int aXBegin, int aXEnd, int aYBegin, int aYEnd) {
diff --git a/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsAssert.java b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsAssert.java
index 0a11134..5eea6f7 100644
--- a/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsAssert.java
+++ b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsAssert.java
@@ -29,6 +29,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
+import java.util.function.IntFunction;
 
 import org.apache.uima.cas.CAS;
 import org.apache.uima.cas.Type;
@@ -41,6 +42,9 @@ import org.apache.uima.util.CasCreationUtils;
 import org.assertj.core.api.AutoCloseableSoftAssertions;
 
 public class SelectFsAssert {
+  private static final long RANDOM_SEED = System.nanoTime();
+//  private static final long RANDOM_SEED = 1123487858940988l;
+  
   public static void assertSelectFS(RelativePosition aCondition, RelativeAnnotationPredicate aPredicate, 
       List<TestCase> aTestCases)
       throws Exception {
@@ -72,14 +76,26 @@ public class SelectFsAssert {
     }
   }
 
-  public static void assertSelectionIsEqualOnRandomData(int aIterations, int aTypes, 
+  public static void assertSelectionIsEqualOnRandomData(String xRelToY, int aIterations, int aTypes, 
       TypeByContextSelector aExpected, TypeByContextSelector aActual) throws Exception {
     TypeSystemDescription tsd = getResourceSpecifierFactory().createTypeSystemDescription();
     
+    IntFunction<Integer> annotationsPerIteration = iteration -> iteration * 3;
+    // Quick overrides for debugging
+//    annotationsPerIteration = iteration -> 10;
+//    aIterations = 1_000;
+//    aTypes = 4;
+
+    Random rnd = new Random(RANDOM_SEED);
     Map<String, Type> types = new LinkedHashMap<>();
     for (int i = 0; i < aTypes; i++) {
       String typeName = "test.Type" + (i + 1);
-      tsd.addType(typeName, "", CAS.TYPE_NAME_ANNOTATION);
+      if (rnd.nextInt() % 2 == 0 || types.size() == 0) {
+        tsd.addType(typeName, "", CAS.TYPE_NAME_ANNOTATION);
+      }
+      else {
+        tsd.addType(typeName, "", new ArrayList<>(types.keySet()).get(rnd.nextInt(types.size())));
+      }
       types.put(typeName, null);
     }
     
@@ -93,34 +109,52 @@ public class SelectFsAssert {
     try {
       Iterator<Type> ti = types.values().iterator();
       Type type1 = ti.next();
-      Type type2 = ti.next();
+      Type type2 = ti.hasNext() ? ti.next() : type1;
       
       long timeExpected = 0;
       long timeActual = 0;
       for (int i = 0; i < aIterations; i++) {
+        randomCas.reset();
+        
         if (i % 10 == 0) {
           System.out.print(i);
+          if (i > 0 && i % 100 == 0) {
+            System.out.println();
+          }
         }
         else {
           System.out.print(".");
         }
+          
+        initRandomCas(randomCas, annotationsPerIteration.apply(i), 0, types.values().toArray(new Type[types.size()]));
   
-        initRandomCas(randomCas, 3 * i, 0, types.values().toArray(new Type[types.size()]));
-  
-        for (Annotation context : randomCas.<Annotation>select(type1)) {
+        for (Annotation y : randomCas.<Annotation>select(type1)) {
           long t1 = System.currentTimeMillis();
-          List<AnnotationFS> expected = aExpected.select(randomCas, type2, context);
+          List<AnnotationFS> expected = aExpected.select(randomCas, type2, y);
           timeExpected += System.currentTimeMillis() - t1;
           
           long t2 = System.currentTimeMillis();
-          List<AnnotationFS> actual = aActual.select(randomCas, type2, context);
+          List<AnnotationFS> actual = aActual.select(randomCas, type2, y);
           timeActual += System.currentTimeMillis() - t2;
   
-          assertThat(actual)
-              .as("Selected [%s] with context [%s]@[%d..%d]%n%s%n", type2.getShortName(), 
-                  type1.getShortName(), context.getBegin(), context.getEnd(), 
-                  casToString(randomCas))
-              .containsExactlyElementsOf(expected);
+//          try {
+            assertThat(actual)
+                .as("Selecting X of type [%s] %s [%s]@[%d-%d]%n%s%n", type2.getName(), xRelToY,
+                    y.getType().getShortName(), y.getBegin(), y.getEnd(),
+                    casToString(randomCas))
+                .containsExactlyElementsOf(expected);
+//          }
+//          catch (Throwable e) {
+//            // Set a breakpoint here to halt when an assert above fails. The select triggering the
+//            // assert is then re-executed below and you can look into its details. To allow
+//            // stopping and re-executing the test, you need to put the displayed random seed into
+//            // the static variable RANDOM_SEED at the beginning of the file. Don't forget to
+//            // Comment this out again and to re-set the RANDOM_SEED to timer-based when you are
+//            // done with debugging.
+//            System.out.printf("RANDOM SEED: %d%n", RANDOM_SEED);
+//            aActual.select(randomCas, type2, y);
+//            throw e;
+//          }
         }
       }
       System.out.print(aIterations);
@@ -132,24 +166,25 @@ public class SelectFsAssert {
   }
   
   private static String casToString(CAS aCas) {
-    if (aCas.select().count() > 10) {
-      return "CAS contains more than 10 annotations - try tweaking the test parameters to reproduce"
-          + " the isssue with a smaller CAS.";
+    int MAX_ANNOTATIONS = 100;
+    if (aCas.select().count() > MAX_ANNOTATIONS) {
+      return "CAS contains more than " + MAX_ANNOTATIONS
+          + " annotations - try tweaking the test parameters to reproduce" + " the isssue with a smaller CAS.";
     }
     
     StringBuilder sb = new StringBuilder();
     aCas.select().forEach(fs -> {
       if (fs instanceof AnnotationFS) {
         AnnotationFS ann = (AnnotationFS) fs;
-        sb.append(format("%s@[%d-%d]%n", ann.getType().getShortName(), ann.getBegin(), 
-            ann.getEnd()));
+        sb.append(format("%s@[%d-%d] (parent type: %s)%n", ann.getType().getShortName(), ann.getBegin(), 
+            ann.getEnd(), ann.getCAS().getTypeSystem().getParent(ann.getType())));
       }
     });
     return sb.toString();
   }
 
   private static void initRandomCas(CAS aCas, int aSize, int aMinimumWidth, Type... aTypes) {
-    Random rnd = new Random();
+    Random rnd = new Random(RANDOM_SEED);
 
     List<Type> types = new ArrayList<>(asList(aTypes));
 
diff --git a/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsPredicateAlignmentTest.java b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsPredicateAlignmentTest.java
index 2a596b8..c23a245 100644
--- a/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsPredicateAlignmentTest.java
+++ b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsPredicateAlignmentTest.java
@@ -32,6 +32,7 @@ import static org.apache.uima.cas.text.AnnotationPredicates.colocated;
 import static org.apache.uima.cas.text.AnnotationPredicates.coveredBy;
 import static org.apache.uima.cas.text.AnnotationPredicates.covering;
 import static org.apache.uima.cas.text.AnnotationPredicates.following;
+import static org.apache.uima.cas.text.AnnotationPredicates.overlappingAtEnd;
 import static org.apache.uima.cas.text.AnnotationPredicates.preceding;
 
 import java.util.ArrayList;
@@ -45,6 +46,9 @@ import org.junit.Test;
 public class SelectFsPredicateAlignmentTest {
   private List<TestCase> defaultPredicatesTestCases = union(NON_ZERO_WIDTH_TEST_CASES, ZERO_WIDTH_TEST_CASES);
   
+  private static final int DEFAULT_RANDOM_ITERATIONS = 100;
+  private static final int DEFAULT_RANDOM_TYPE_COUNT = 10;
+  
   @Test
   public void thatSelectFsBehaviorAlignsWithPrecedingPredicate() throws Exception {
     // In order to find annotations that X is preceding, we select the following annotations
@@ -64,13 +68,13 @@ public class SelectFsPredicateAlignmentTest {
   public void thatCasSelectFsBehaviorAlignsWithPrecedingPredicateOnRandomData() throws Exception
   {
     System.out.print("Preceding (CAS select by annotation)   -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> preceding(candidate, context))
+    assertSelectionIsEqualOnRandomData("preceding", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> x != y && preceding(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.<Annotation>select(type)
-            .preceding(context)
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.<Annotation>select(type)
+            .preceding(y)
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
   }
 
@@ -78,13 +82,13 @@ public class SelectFsPredicateAlignmentTest {
   public void thatIndexSelectFsBehaviorAlignsWithPrecedingPredicateOnRandomData() throws Exception
   {
     System.out.print("Preceding (Index select by annotation) -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> preceding(candidate, context))
+    assertSelectionIsEqualOnRandomData("preceding", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> x != y && preceding(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .preceding(context)
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .preceding(y)
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
   }
 
@@ -107,13 +111,13 @@ public class SelectFsPredicateAlignmentTest {
   public void thatCasSelectFsBehaviorAlignsWithFollowingPredicateOnRandomData() throws Exception
   {
     System.out.print("Following (CAS select by annotation)   -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> following(candidate, context))
+    assertSelectionIsEqualOnRandomData("following", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> x != y && following(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.<Annotation>select(type)
-            .following(context)
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.<Annotation>select(type)
+            .following(y)
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
   }
 
@@ -121,13 +125,13 @@ public class SelectFsPredicateAlignmentTest {
   public void thatIndexSelectFsBehaviorAlignsWithFollowingPredicateOnRandomData() throws Exception
   {
     System.out.print("Following (Index select by annotation) -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> following(candidate, context))
+    assertSelectionIsEqualOnRandomData("following", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> x != y && following(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .following(context)
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .following(y)
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
   }
 
@@ -156,23 +160,23 @@ public class SelectFsPredicateAlignmentTest {
   public void thatCasSelectFsBehaviorAlignsWithCoveredByPredicateOnRandomData() throws Exception
   {
     System.out.print("CoveredBy (CAS select by annotation)   -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> coveredBy(candidate, context))
+    assertSelectionIsEqualOnRandomData("covered by", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> x != y && coveredBy(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.<Annotation>select(type)
-            .coveredBy(context)
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.<Annotation>select(type)
+            .coveredBy(y)
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
 
     System.out.print("CoveredBy (CAS select by offsets)      -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> coveredBy(candidate, context))
+    assertSelectionIsEqualOnRandomData("covered by", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> coveredBy(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.<Annotation>select(type)
-            .coveredBy(context.getBegin(), context.getEnd())
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.<Annotation>select(type)
+            .coveredBy(y.getBegin(), y.getEnd())
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
   }
 
@@ -180,27 +184,42 @@ public class SelectFsPredicateAlignmentTest {
   public void thatIndexSelectFsBehaviorAlignsWithCoveredByPredicateOnRandomData() throws Exception
   {
     System.out.print("CoveredBy (Index select by annotation) -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> coveredBy(candidate, context))
+    assertSelectionIsEqualOnRandomData("covered by", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> x != y && coveredBy(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .coveredBy(context)
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .coveredBy(y)
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
 
     System.out.print("CoveredBy (Index select by offsets)    -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> coveredBy(candidate, context))
+    assertSelectionIsEqualOnRandomData("covered by", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> coveredBy(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .coveredBy(context.getBegin(), context.getEnd())
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .coveredBy(y.getBegin(), y.getEnd())
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
   }
   
   @Test
+  public void thatCasSelectFsNonStrictBehaviorAlignsWithCoveredByPredicateOnRandomData() throws Exception
+  {
+    System.out.print("CoveredBy* (CAS select by annotation)  -- ");
+    assertSelectionIsEqualOnRandomData("covered by (non-strict)", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> x != y && (coveredBy(x, y) || overlappingAtEnd(x, y)))
+            .collect(toList()),
+        (cas, type, y) -> cas.<Annotation>select(type)
+            .coveredBy(y)
+            .includeAnnotationsWithEndBeyondBounds()
+            .map(x -> (AnnotationFS) x)
+            .collect(toList()));
+  }  
+
+  @Test
   public void thatSelectFsBehaviorAlignsWithCoveringPredicate() throws Exception {
     // X covering Y means that Y is covered by Y, so we need to select the covered by annotations
     // below.
@@ -225,23 +244,23 @@ public class SelectFsPredicateAlignmentTest {
   public void thatCasSelectFsBehaviorAlignsWithCoveringPredicateOnRandomData() throws Exception
   {
     System.out.print("Covering  (CAS select by annotation)   -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> covering(candidate, context))
+    assertSelectionIsEqualOnRandomData("covering", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> x != y && covering(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.<Annotation>select(type)
-            .covering(context)
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.<Annotation>select(type)
+            .covering(y)
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
     
     System.out.print("Covering  (CAS select by offsets)      -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> covering(candidate, context))
+    assertSelectionIsEqualOnRandomData("covering", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> covering(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.<Annotation>select(type)
-            .covering(context.getBegin(), context.getEnd())
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.<Annotation>select(type)
+            .covering(y.getBegin(), y.getEnd())
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
   }
   
@@ -249,23 +268,23 @@ public class SelectFsPredicateAlignmentTest {
   public void thatIndexSelectFsBehaviorAlignsWithCoveringPredicateOnRandomData() throws Exception
   {
     System.out.print("Covering  (Index select by annotation) -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> covering(candidate, context))
+    assertSelectionIsEqualOnRandomData("covering", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> x != y && covering(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .covering(context)
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .covering(y)
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
     
     System.out.print("Covering  (Index select by offsets)    -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> covering(candidate, context))
+    assertSelectionIsEqualOnRandomData("covering", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> covering(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .covering(context.getBegin(), context.getEnd())
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .covering(y.getBegin(), y.getEnd())
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
   }
   
@@ -288,29 +307,29 @@ public class SelectFsPredicateAlignmentTest {
         (cas, type, x, y) -> cas.select(type).filter((a) -> 
                 colocated(x, (Annotation) a)).collect(toList()).contains(y),
         defaultPredicatesTestCases);
-  }  
+  }
 
   @Test
   public void thatCasSelectFsBehaviorAlignsWithColocatedPredicateOnRandomData() throws Exception
   {
     System.out.print("Colocated (CAS select by annotation)   -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> colocated(candidate, context))
+    assertSelectionIsEqualOnRandomData("colocated at", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> x != y && colocated(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.<Annotation>select(type)
-            .at(context)
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.<Annotation>select(type)
+            .at(y)
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
 
     System.out.print("Colocated (CAS select by offsets)      -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> colocated(candidate, context))
+    assertSelectionIsEqualOnRandomData("colocated at", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> colocated(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.<Annotation>select(type)
-            .at(context.getBegin(), context.getEnd())
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.<Annotation>select(type)
+            .at(y.getBegin(), y.getEnd())
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
   }
 
@@ -318,23 +337,23 @@ public class SelectFsPredicateAlignmentTest {
   public void thatIndexSelectFsBehaviorAlignsWithColocatedPredicateOnRandomData() throws Exception
   {
     System.out.print("Colocated (Index select by annotation) -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> colocated(candidate, context))
+    assertSelectionIsEqualOnRandomData("colocated at", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> x != y && colocated(x, y))
             .collect(toList()),
         (cas, type, context) -> cas.getAnnotationIndex(type).select()
             .at(context)
-            .map(a -> (AnnotationFS) a)
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
 
     System.out.print("Colocated (Index select by offsets)    -- ");
-    assertSelectionIsEqualOnRandomData(30, 10,
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .filter(candidate -> colocated(candidate, context))
+    assertSelectionIsEqualOnRandomData("colocated at", DEFAULT_RANDOM_ITERATIONS, DEFAULT_RANDOM_TYPE_COUNT,
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .filter(x -> colocated(x, y))
             .collect(toList()),
-        (cas, type, context) -> cas.getAnnotationIndex(type).select()
-            .at(context.getBegin(), context.getEnd())
-            .map(a -> (AnnotationFS) a)
+        (cas, type, y) -> cas.getAnnotationIndex(type).select()
+            .at(y.getBegin(), y.getEnd())
+            .map(x -> (AnnotationFS) x)
             .collect(toList()));
   }
   
diff --git a/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsTest.java b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsTest.java
index d7fa61a..b277551 100644
--- a/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsTest.java
+++ b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsTest.java
@@ -20,8 +20,11 @@ package org.apache.uima.cas.impl;
 
 import static java.lang.Integer.MAX_VALUE;
 import static java.util.Arrays.asList;
+import static java.util.stream.Collectors.toList;
 import static org.apache.uima.UIMAFramework.getResourceSpecifierFactory;
 import static org.apache.uima.cas.CAS.TYPE_NAME_ANNOTATION;
+import static org.apache.uima.cas.text.AnnotationPredicates.coveredBy;
+import static org.apache.uima.cas.text.AnnotationPredicates.overlappingAtEnd;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.io.File;
@@ -29,6 +32,10 @@ import java.util.Arrays;
 import java.util.Collection;
 
 import org.apache.uima.UIMAFramework;
+import org.apache.uima.cas.CAS;
+import org.apache.uima.cas.FeatureStructure;
+import org.apache.uima.cas.Type;
+import org.apache.uima.cas.text.AnnotationFS;
 import org.apache.uima.jcas.JCas;
 import org.apache.uima.jcas.cas.TOP;
 import org.apache.uima.jcas.tcas.Annotation;
@@ -225,6 +232,100 @@ public class SelectFsTest {
   }
   
   @Test
+  public void thatCoveredByWithBeyondEndsDoesNotReturnOverlapAtStart() throws Exception
+  {
+    Annotation y;
+    addToIndexes(
+        new Annotation(cas.getJCas(), 64, 90),
+        y = new Annotation(cas.getJCas(), 68, 86),
+        new Annotation(cas.getJCas(), 95, 98));
+
+    assertThat(cas.select(Annotation.class)
+        .filter(x -> x != y && (coveredBy(x, y) || overlappingAtEnd(x, y)))
+        .collect(toList()))
+        .isEmpty();
+
+    assertThat(cas.select(Annotation.class).coveredBy(y)
+        .includeAnnotationsWithEndBeyondBounds().asList())
+        .isEmpty();    
+  }
+
+  @Test
+  public void thatCoveredByWithBeyondEndsDoesNotReturnAnnotationStartingAtEnd() throws Exception
+  {
+    Annotation y;
+    Annotation[] a = addToIndexes(
+        y = new Annotation(cas.getJCas(), 68, 79),
+        new Annotation(cas.getJCas(), 77, 78),
+        new Annotation(cas.getJCas(), 79, 103));
+
+    assertThat(cas.select(Annotation.class)
+        .filter(x -> x != y && (coveredBy(x, y) || overlappingAtEnd(x, y)))
+        .collect(toList()))
+        .containsExactly(a[1]);
+
+    assertThat(cas.select(Annotation.class).coveredBy(y)
+        .includeAnnotationsWithEndBeyondBounds().asList())
+        .containsExactly(a[1]);
+  }
+
+  @Test
+  public void thatCoveredByWithBeyondEndsDoesNotReturnAnnotationStartingAtEnd2() throws Exception
+  {
+    Annotation y;
+    addToIndexes(
+        y = new Annotation(cas.getJCas(), 68, 79),
+        new Annotation(cas.getJCas(), 79, 103));
+
+    assertThat(cas.select(Annotation.class)
+        .filter(x -> x != y && (coveredBy(x, y) || overlappingAtEnd(x, y)))
+        .collect(toList()))
+        .isEmpty();
+
+    assertThat(cas.select(Annotation.class).coveredBy(y)
+        .includeAnnotationsWithEndBeyondBounds().asList())
+        .isEmpty();
+  }
+
+  @Test
+  public void thatCoveredByWithBeyondEndsFindsAnnotationCoStartingAndExtendingBeyond() throws Exception
+  {
+    Annotation y;
+    Annotation[] a = addToIndexes(
+        new Annotation(cas.getJCas(), 44, 68),
+        y = new Annotation(cas.getJCas(), 44, 60));
+
+    assertThat(cas.select(Annotation.class)
+        .filter(x -> x != y && (coveredBy(x, y) || overlappingAtEnd(x, y)))
+        .collect(toList()))
+        .containsExactly(a[0]);
+
+    assertThat(cas.select(Annotation.class).coveredBy(y)
+        .includeAnnotationsWithEndBeyondBounds().asList())
+    .containsExactly(a[0]);
+  }
+
+  @Test
+  public void thatCoveredByWithBeyondEndsFindsZeroWidthAtEnd() throws Exception
+  {
+    Annotation y;
+    Annotation[] a = addToIndexes(
+        y = new Annotation(cas.getJCas(), 8, 33),
+        new Annotation(cas.getJCas(), 33, 60),
+        new Annotation(cas.getJCas(), 33, 33));
+
+    assertThat(cas.select(Annotation.class)
+        .filter(x -> x != y && (coveredBy(x, y) || overlappingAtEnd(x, y)))
+        .collect(toList()))
+        .containsExactly(a[2]);
+
+    assertThat(cas.select(Annotation.class).coveredBy(y)
+        .includeAnnotationsWithEndBeyondBounds()
+        .asList())
+        .containsExactly(a[2]);
+  }
+
+  @Test
   public void thatCoveredByWithBeyondEndsCanSelectAnnotationsStartingAtSelectPosition() throws Exception
   {
     Annotation a = addToIndexes(new Annotation(cas.getJCas(), 0, 2));
@@ -488,14 +589,116 @@ public class SelectFsTest {
         .containsExactly(a[0], a[7], a[4], a[6], a[5]);
   }
 
-  private static <T extends TOP> T addToIndexes(T fses) {
-    asList(fses).forEach(TOP::addToIndexes);
+  @Test
+  public void thatSelectAtFindsSupertype() throws Exception {
+    Annotation[] a = addToIndexes(
+        new Annotation(cas.getJCas(), 5, 10),
+        new Token(cas.getJCas(), 5, 10));
+
+    assertThat(cas.select(Annotation.class).at(a[1]).asList())
+        .containsExactly(a[0]);
+  }
+
+  @Test
+  public void thatSelectBetweenWorks() throws Exception {
+    Annotation[] a = addToIndexes(
+        new Sentence(cas.getJCas(), 47, 67),
+        new Sentence(cas.getJCas(), 55, 66),
+        new Token(cas.getJCas(), 24, 29),
+        new Token(cas.getJCas(), 66, 92));
+
+    assertThat(cas.select(Sentence.class).between(a[2], a[3]).asList())
+        .containsExactly((Sentence) a[1]);
+  }
+
+  @Test
+  public void thatSelectColocatedFindsOtherAnnotation() throws Exception {
+    Annotation x, y;
+    addToIndexes(
+        new Annotation(cas.getJCas(), 66, 84),
+        y = new Annotation(cas.getJCas(), 66, 70),
+        x = new Annotation(cas.getJCas(), 66, 70));
+
+    assertThat(cas.select(Annotation.class).at(y).asList())
+        .containsExactly(x);
+  }
+
+  @Test
+  public void thatSelectColocatedFindsSiblingType() throws Exception {
+    TypeSystemDescription tsd = getResourceSpecifierFactory().createTypeSystemDescription();
+    tsd.addType("test.Type1", "", CAS.TYPE_NAME_ANNOTATION);
+    tsd.addType("test.Type2", "", "test.Type1");
+    tsd.addType("test.Type3", "", "test.Type1");
+    
+    CAS cas = CasCreationUtils.createCas(tsd, null, null, null);
+    
+    Type type2 = cas.getTypeSystem().getType("test.Type2");
+    Type type3 = cas.getTypeSystem().getType("test.Type3");
+    
+    AnnotationFS x, y;
+    addToIndexes(
+        x = cas.createAnnotation(type2, 16, 42),
+        y = cas.createAnnotation(type3, 16, 42)
+        );
+
+    assertThat(cas.select(type2).at(y).asList())
+        .containsExactly((Annotation) x);
+  }
+
+  @Test
+  public void thatSelectColocatedFindsSiblingType2() throws Exception {
+    TypeSystemDescription tsd = getResourceSpecifierFactory().createTypeSystemDescription();
+    tsd.addType("test.Type1", "", CAS.TYPE_NAME_ANNOTATION);
+    tsd.addType("test.Type2", "", "test.Type1");
+    tsd.addType("test.Type3", "", "test.Type1");
+    tsd.addType("test.Type4", "", "test.Type2");
+    
+    CAS cas = CasCreationUtils.createCas(tsd, null, null, null);
+    
+//    Type type1 = cas.getTypeSystem().getType("test.Type1");
+    Type type2 = cas.getTypeSystem().getType("test.Type2");
+    Type type3 = cas.getTypeSystem().getType("test.Type3");
+    Type type4 = cas.getTypeSystem().getType("test.Type4");
+    
+    AnnotationFS x, y;
+    addToIndexes(
+        x = cas.createAnnotation(type2, 16, 42),
+        cas.createAnnotation(type4, 16, 41),
+        y = cas.createAnnotation(type3, 16, 42)
+        );
+
+    assertThat(cas.select(type2).at(y).asList())
+        .containsExactly((Annotation) x);
+  }
+  
+  @Test
+  public void thatSelectCoveringDoesNotFindItselfWhenSelectingSupertype() throws Exception {
+    TypeSystemDescription tsd = getResourceSpecifierFactory().createTypeSystemDescription();
+    tsd.addType("test.Type1", "", CAS.TYPE_NAME_ANNOTATION);
+    tsd.addType("test.Type2", "", "test.Type1");
+    tsd.addType("test.Type3", "", "test.Type2");
+    
+    CAS cas = CasCreationUtils.createCas(tsd, null, null, null);
+    
+    Type type2 = cas.getTypeSystem().getType("test.Type2");
+    Type type3 = cas.getTypeSystem().getType("test.Type3");
+    
+    AnnotationFS y;
+    addToIndexes(
+        y = cas.createAnnotation(type3, 4, 33));
+
+    assertThat(cas.select(type2).covering(y).asList())
+        .isEmpty();
+  }
+  
+  private static <T extends FeatureStructure> T addToIndexes(T fses) {
+    asList(fses).forEach(fs -> fs.getCAS().addFsToIndexes(fs));
     return fses;
   }
 
   @SafeVarargs
-  private static <T extends TOP> T[] addToIndexes(T... fses) {
-    asList(fses).forEach(TOP::addToIndexes);
+  private static <T extends FeatureStructure> T[] addToIndexes(T... fses) {
+    asList(fses).forEach(fs -> fs.getCAS().addFsToIndexes(fs));
     return fses;
   }
 }
diff --git a/uimaj-core/src/test/java/org/apache/uima/cas/text/AnnotationPredicateTestData.java b/uimaj-core/src/test/java/org/apache/uima/cas/text/AnnotationPredicateTestData.java
index 41e4395..7d8cfb5 100644
--- a/uimaj-core/src/test/java/org/apache/uima/cas/text/AnnotationPredicateTestData.java
+++ b/uimaj-core/src/test/java/org/apache/uima/cas/text/AnnotationPredicateTestData.java
@@ -52,79 +52,83 @@ public class AnnotationPredicateTestData {
   private static final int END = 20;
   private static final int Z_POS = 10;
 
+  public static final TestCase T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13;
+  
   public static final List<TestCase> NON_ZERO_WIDTH_TEST_CASES = asList(
-      new TestCase("1) Y begins and ends after X (### [---])", 
+      T1 = new TestCase("1) Y begins and ends after X (### [---])", 
           p -> p.apply(BEGIN, END, END + 1, MAX_VALUE),
           asList(PRECEDING)),
-      new TestCase("2) Y begins at X's end and ends after X (###[---])", 
+      T2 = new TestCase("2) Y begins at X's end and ends after X (###[---])", 
           p -> p.apply(BEGIN, END, END, MAX_VALUE),
           asList(PRECEDING)),
-      new TestCase("3) Y begins within and ends after X (##[#--])", 
+      T3 = new TestCase("3) Y begins within and ends after X (##[#--])", 
           p -> p.apply(BEGIN, END, END - 1 , MAX_VALUE),
           asList(OVERLAPPING, OVERLAPPING_AT_BEGIN)),
-      new TestCase("4) Y begins and ends at X's boundries ([###])", 
+      T4 = new TestCase("4) Y begins and ends at X's boundries ([###])", 
           p -> p.apply(BEGIN, END, BEGIN, END),
           asList(OVERLAPPING, COLOCATED, COVERED_BY, COVERING, BEGINNING_WITH, ENDING_WITH)),
-      new TestCase("5) Y begins and ends within X (#[#]#)", 
+      T5 = new TestCase("5) Y begins and ends within X (#[#]#)", 
           p -> p.apply(BEGIN, END, BEGIN + 1, END - 1),
           asList(OVERLAPPING, COVERING)),
-      new TestCase("6) Y begins at and ends before X's boundries ([##]#)", 
+      T6 = new TestCase("6) Y begins at and ends before X's boundries ([##]#)", 
           p -> p.apply(BEGIN, END, BEGIN, END - 1),
-          asList(OVERLAPPING, COVERING, BEGINNING_WITH)),
-      new TestCase("7) Y begins after and ends at X's boundries (#[##])", 
+          asList(OVERLAPPING, COVERING, BEGINNING_WITH, OVERLAPPING_AT_END)),
+      T7 = new TestCase("7) Y begins after and ends at X's boundries (#[##])", 
           p -> p.apply(BEGIN, END, BEGIN + 1, END),
-          asList(OVERLAPPING, COVERING, ENDING_WITH)),
-      new TestCase("8) Y begins before and ends after X's boundries ([-###-])", 
+          asList(OVERLAPPING, COVERING, ENDING_WITH, OVERLAPPING_AT_BEGIN)),
+      T8 = new TestCase("8) Y begins before and ends after X's boundries ([-###-])", 
           p -> p.apply(BEGIN, END, BEGIN - 1, END + 1),
           asList(OVERLAPPING, COVERED_BY)),
-      new TestCase("9) X starts where Y begins and ends within Y ([##-])", 
+      T9 = new TestCase("9) X starts where Y begins and ends within Y ([##-])", 
           p -> p.apply(BEGIN, END, BEGIN, END + 1),
           asList(OVERLAPPING, COVERED_BY, BEGINNING_WITH)),
-      new TestCase("10) X starts within Y and ends where Y ends ([-##])", 
+      T10 = new TestCase("10) X starts within Y and ends where Y ends ([-##])", 
           p -> p.apply(BEGIN, END, BEGIN - 1, END),
           asList(OVERLAPPING, COVERED_BY, ENDING_WITH)),
-      new TestCase("11) Y begins before and ends within X ([--#]##)", 
+      T11 = new TestCase("11) Y begins before and ends within X ([--#]##)", 
           p -> p.apply(BEGIN, END, 0, BEGIN + 1),
           asList(OVERLAPPING, OVERLAPPING_AT_END)),
-      new TestCase("12) Y begins before and ends where X begins ([---]###)", 
+      T12 = new TestCase("12) Y begins before and ends where X begins ([---]###)", 
           p -> p.apply(BEGIN, END, 0, BEGIN),
           asList(FOLLOWING)),
-      new TestCase("13) Y begins and ends before X begins ([---] ###)", 
+      T13 = new TestCase("13) Y begins and ends before X begins ([---] ###)", 
           p -> p.apply(BEGIN, END, 0, BEGIN - 1),
           asList(FOLLOWING)));
 
+  public static final TestCase TZ1, TZ2, TZ3, TZ4, TZ5, TZ6, TZ7, TZ8, TZ9, TZ10, TZ11;
+
   public static final List<TestCase> ZERO_WIDTH_TEST_CASES = asList(
-      new TestCase("Z1) Zero-width X before Y start (# [---])", 
+      TZ1 = new TestCase("Z1) Zero-width X before Y start (# [---])", 
           p -> p.apply(Z_POS, Z_POS, Z_POS + 10, Z_POS + 20),
           asList(PRECEDING)),
-      new TestCase("Z2) Zero-width Y after X's end (### |)", 
+      TZ2 = new TestCase("Z2) Zero-width Y after X's end (### |)", 
           p -> p.apply(BEGIN, END, END + 1, END + 1),
           asList(PRECEDING)),
-      new TestCase("Z3) Zero-width X at Y's start (#---])", 
+      TZ3 = new TestCase("Z3) Zero-width X at Y's start (#---])", 
           p -> p.apply(Z_POS, Z_POS, Z_POS, Z_POS + 10),
           asList(OVERLAPPING, COVERED_BY, BEGINNING_WITH)),
-      new TestCase("Z4) Zero-width X at Y's end ([---#)", 
+      TZ4 = new TestCase("Z4) Zero-width X at Y's end ([---#)", 
           p -> p.apply(Z_POS, Z_POS, Z_POS-10, Z_POS),
           asList(OVERLAPPING, COVERED_BY, ENDING_WITH)),
-      new TestCase("Z5) Zero-width Y where X begins (|###)", 
+      TZ5 = new TestCase("Z5) Zero-width Y where X begins (|###)", 
           p -> p.apply(BEGIN, END, BEGIN, BEGIN),
           asList(OVERLAPPING, COVERING, BEGINNING_WITH)),
-      new TestCase("Z6) Zero-width Y within X (#|#)", 
+      TZ6 = new TestCase("Z6) Zero-width Y within X (#|#)", 
           p -> p.apply(BEGIN, END, BEGIN + 1, BEGIN + 1),
           asList(OVERLAPPING, COVERING)),
-      new TestCase("Z7) Zero-width Y at X's end (###|)", 
+      TZ7 = new TestCase("Z7) Zero-width Y at X's end (###|)", 
           p -> p.apply(BEGIN, END, END, END),
           asList(OVERLAPPING, COVERING, ENDING_WITH)),
-      new TestCase("Z8) Zero-width X with Y (-|-)", 
+      TZ8 = new TestCase("Z8) Zero-width X with Y (-|-)", 
           p -> p.apply(Z_POS, Z_POS, Z_POS - 5, Z_POS + 5),
           asList(OVERLAPPING, COVERED_BY)),
-      new TestCase("Z9) Zero-width X after Y's end ([---] #)", 
+      TZ9 = new TestCase("Z9) Zero-width X after Y's end ([---] #)", 
           p -> p.apply(Z_POS, Z_POS, Z_POS - 10, Z_POS - 5),
           asList(FOLLOWING)),
-      new TestCase("Z10) Zero-width Y before X begins (| ###)", 
+      TZ10 = new TestCase("Z10) Zero-width Y before X begins (| ###)", 
           p -> p.apply(BEGIN, END, BEGIN - 1, BEGIN - 1),
           asList(FOLLOWING)),
-      new TestCase("Z11) Zero-width X matches zero-width Y start/end (#)", 
+      TZ11 = new TestCase("Z11) Zero-width X matches zero-width Y start/end (#)", 
           p -> p.apply(Z_POS, Z_POS, Z_POS, Z_POS),
           asList(OVERLAPPING, COVERED_BY, COVERING, COLOCATED, BEGINNING_WITH, ENDING_WITH)));
 }


[uima-uimaj] 01/02: [NO JIRA] Remove warning about two SLF4J backends when running tests.

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rec pushed a commit to branch bugfix/UIMA-6294-SelectFS-at-annotation-does-not-return-the-correct-result
in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git

commit d8ae851141ef9a591cf65f98be5f7be6794d1eb9
Author: Richard Eckart de Castilho <re...@apache.org>
AuthorDate: Mon Nov 9 08:21:16 2020 +0100

    [NO JIRA] Remove warning about two SLF4J backends when running tests.
---
 uimaj-core/pom.xml | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/uimaj-core/pom.xml b/uimaj-core/pom.xml
index f1cb74d..63a848c 100644
--- a/uimaj-core/pom.xml
+++ b/uimaj-core/pom.xml
@@ -127,11 +127,6 @@
       <scope>test</scope>
     </dependency>
                      -->
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-jdk14</artifactId>
-      <scope>test</scope>
-    </dependency>
     
 		<dependency>
 			<groupId>org.apache.uima</groupId>