--- /dev/null
+package jalview.testutils;
+
+import java.util.List;
+import java.util.Objects;
+import static java.util.Objects.requireNonNullElse;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+
+import jalview.datamodel.Annotation;
+
+public class AnnotationsMatcher extends TypeSafeMatcher<Annotation[]>
+{
+ final List<Annotation> annotations;
+
+ public AnnotationsMatcher(List<Annotation> annotations)
+ {
+ this.annotations = annotations;
+ }
+
+ @Override
+ public boolean matchesSafely(Annotation[] items)
+ {
+ if (annotations.size() != items.length)
+ return false;
+ for (int i = 0; i < annotations.size(); i++)
+ {
+ var actual = items[i];
+ var expected = annotations.get(i);
+ if (!annotationsEqual(actual, expected))
+ return false;
+ }
+ return true;
+ }
+
+ static boolean annotationsEqual(Annotation a, Annotation b)
+ {
+ return a.secondaryStructure == b.secondaryStructure && a.value == b.value
+ && Objects.equals(a.colour, b.colour)
+ && Objects
+ .equals(requireNonNullElse(a.displayCharacter, ""),
+ requireNonNullElse(b.displayCharacter, ""))
+ && Objects
+ .equals(requireNonNullElse(a.description, ""),
+ requireNonNullElse(b.description, ""));
+ }
+
+ @Override
+ public void describeTo(Description description)
+ {
+ description.appendText("annotations ").appendValue(annotations);
+ }
+
+ @Override
+ public void describeMismatchSafely(Annotation[] items,
+ Description description)
+ {
+ if (annotations.size() != items.length)
+ {
+ description.appendText("but had length ").appendValue(items.length);
+ return;
+ }
+ boolean first = true;
+ for (int i = 0; i < annotations.size(); i++)
+ {
+ var actual = items[i];
+ var expected = annotations.get(i);
+ if (!annotationsEqual(actual, expected))
+ {
+ description
+ .appendText(first ? "but " : ", ")
+ .appendText("element [" + i + "] was ")
+ .appendValue(items[i]);
+ first = false;
+ }
+ }
+ }
+}
--- /dev/null
+package jalview.testutils;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import jalview.datamodel.Annotation;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.awt.Color;
+import java.util.*;
+
+public class AnnotationsMatcherTest
+{
+ @DataProvider
+ public Object[][] matchingAnnotationsLists()
+ {
+ return new Object[][] {
+ {
+ new Annotation[] {
+ new Annotation("C", "", 'C', 0.0f),
+ new Annotation("C", "", 'C', 0.1f),
+ new Annotation("B", "", 'B', 0.2f) },
+ new Annotation[] {
+ new Annotation("C", "", 'C', 0.0f),
+ new Annotation("C", "", 'C', 0.1f),
+ new Annotation("B", "", 'B', 0.2f) } },
+ {
+ new Annotation[] {
+ new Annotation("X", "xxx", 'X', 1.3f),
+ new Annotation("V", "vvv", 'V', 1.5f),
+ new Annotation("B", "bbb", 'B', 2.5f) },
+ new Annotation[] {
+ new Annotation("X", "xxx", 'X', 1.3f),
+ new Annotation("V", "vvv", 'V', 1.5f),
+ new Annotation("B", "bbb", 'B', 2.5f) } },
+ {
+ new Annotation[] {
+ new Annotation("A", "", 'A', 0.2f, Color.RED),
+ new Annotation("A", "", 'A', 0.3f, Color.GREEN),
+ new Annotation("C", "", 'C', 0.4f, Color.YELLOW),
+ new Annotation("C", "", 'C', 0.5f) },
+ new Annotation[] {
+ new Annotation("A", "", 'A', 0.2f, Color.RED),
+ new Annotation("A", "", 'A', 0.3f, Color.GREEN),
+ new Annotation("C", "", 'C', 0.4f, Color.YELLOW),
+ new Annotation("C", "", 'C', 0.5f) } },
+ {
+ new Annotation[] {
+ Annotation.EMPTY_ANNOTATION, Annotation.EMPTY_ANNOTATION },
+ new Annotation[] {
+ Annotation.EMPTY_ANNOTATION, Annotation.EMPTY_ANNOTATION } } };
+ }
+
+ @Test(groups = { "Functional" }, dataProvider = "matchingAnnotationsLists")
+ public void testMatchesSafely_matchingAnnotations(Annotation[] template,
+ Annotation[] testdata)
+ {
+ assert new AnnotationsMatcher(Arrays.asList(template))
+ .matchesSafely(testdata);
+ }
+
+ @DataProvider
+ public Object[][] matchingAnnotations()
+ {
+ return new Object[][] {
+ {
+ new Annotation("A", "aaa", 'A', 0.1f),
+ new Annotation("A", "aaa", 'A', 0.1f) },
+ {
+ new Annotation("A", "abcdef", 'A', 0.1f, Color.RED),
+ new Annotation("A", "abcdef", 'A', 0.1f, Color.RED) },
+ {
+ new Annotation("", "xxxx", ' ', 0.0f),
+ new Annotation("", "xxxx", ' ', 0.0f) },
+ {
+ new Annotation("", "", ' ', 0.0f),
+ new Annotation("", "", ' ', 0.0f) },
+ {
+ new Annotation(null, null, ' ', 0.0f),
+ new Annotation(null, null, ' ', 0.0f) },
+ {
+ new Annotation(null, "", ' ', 0.0f),
+ new Annotation("", null, ' ', 0.0f) } };
+ }
+
+ @Test(groups = { "Functional" }, dataProvider = "matchingAnnotations")
+ public void testAnnotationsEqual_matchingAnnotations(Annotation first,
+ Annotation second)
+ {
+ assert AnnotationsMatcher.annotationsEqual(first, second);
+ }
+
+ @Test(groups = { "Functional" })
+ public void testAnnotationsEqual_compareNullDisplayCharToEmpty()
+ {
+ assert AnnotationsMatcher
+ .annotationsEqual(new Annotation("", "description", 'A', 0.1f),
+ new Annotation(null, "description", 'A', 0.1f));
+ assert AnnotationsMatcher
+ .annotationsEqual(new Annotation(null, "description", 'A', 0.1f),
+ new Annotation("", "description", 'A', 0.1f));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testAnnotationsEqual_compareNullDescriptionToEmpty()
+ {
+ assert AnnotationsMatcher
+ .annotationsEqual(new Annotation("A", null, 'A', 0.2f),
+ new Annotation("A", "", 'A', 0.2f));
+ assert AnnotationsMatcher
+ .annotationsEqual(new Annotation("A", "", 'A', 0.2f),
+ new Annotation("A", null, 'A', 0.2f));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testAnnotationsEqual_compareNullDisplayCharToBlank()
+ {
+ assert !AnnotationsMatcher
+ .annotationsEqual(new Annotation(" ", "aaa", 'A', 0.03f),
+ new Annotation(null, "aaa", 'A', 0.03f));
+ assert !AnnotationsMatcher
+ .annotationsEqual(new Annotation(null, "aaa", 'A', 0.04f),
+ new Annotation(" ", "aaa", 'A', 0.04f));
+ }
+
+ @Test(groups = { "Functional" })
+ public void testAnnotationsEqual_compareNullDescriptionToBlank()
+ {
+ assert !AnnotationsMatcher
+ .annotationsEqual(new Annotation("A", null, 'A', 0.0f),
+ new Annotation("A", " ", 'A', 0.0f));
+ assert !AnnotationsMatcher
+ .annotationsEqual(new Annotation("A", " ", 'A', 0.01f),
+ new Annotation("A", null, 'A', 0.01f));
+ }
+}