--- /dev/null
+package jalview.datamodel.features;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
+import jalview.datamodel.SequenceFeature;
+import jalview.util.MessageManager;
+import jalview.util.matcher.Condition;
+
+import java.util.Locale;
+
+import org.testng.annotations.Test;
+
+public class FeatureMatcherTest
+{
+ @Test(groups = "Functional")
+ public void testMatches_byLabel()
+ {
+ SequenceFeature sf = new SequenceFeature("Cath", "this is my label", 11,
+ 12, "grp");
+
+ /*
+ * contains - not case sensitive
+ */
+ assertTrue(
+ FeatureMatcher.byLabel(Condition.Contains, "IS").matches(sf));
+ assertTrue(FeatureMatcher.byLabel(Condition.Contains, "").matches(sf));
+ assertFalse(
+ FeatureMatcher.byLabel(Condition.Contains, "ISNT").matches(sf));
+
+ /*
+ * does not contain
+ */
+ assertTrue(FeatureMatcher.byLabel(Condition.NotContains, "isnt")
+ .matches(sf));
+ assertFalse(FeatureMatcher.byLabel(Condition.NotContains, "is")
+ .matches(sf));
+
+ /*
+ * matches
+ */
+ assertTrue(FeatureMatcher.byLabel(Condition.Matches, "THIS is MY label")
+ .matches(sf));
+ assertFalse(FeatureMatcher.byLabel(Condition.Matches, "THIS is MY")
+ .matches(sf));
+
+ /*
+ * does not match
+ */
+ assertFalse(FeatureMatcher
+ .byLabel(Condition.NotMatches, "THIS is MY label").matches(sf));
+ assertTrue(FeatureMatcher.byLabel(Condition.NotMatches, "THIS is MY")
+ .matches(sf));
+
+ /*
+ * is present / not present
+ */
+ assertTrue(FeatureMatcher.byLabel(Condition.Present, "").matches(sf));
+ assertFalse(
+ FeatureMatcher.byLabel(Condition.NotPresent, "").matches(sf));
+ }
+
+ @Test(groups = "Functional")
+ public void testMatches_byScore()
+ {
+ SequenceFeature sf = new SequenceFeature("Cath", "this is my label", 11,
+ 12, 3.2f, "grp");
+
+ assertTrue(FeatureMatcher.byScore(Condition.LT, "3.3").matches(sf));
+ assertFalse(FeatureMatcher.byScore(Condition.LT, "3.2").matches(sf));
+ assertFalse(FeatureMatcher.byScore(Condition.LT, "2.2").matches(sf));
+
+ assertTrue(FeatureMatcher.byScore(Condition.LE, "3.3").matches(sf));
+ assertTrue(FeatureMatcher.byScore(Condition.LE, "3.2").matches(sf));
+ assertFalse(FeatureMatcher.byScore(Condition.LE, "2.2").matches(sf));
+
+ assertFalse(FeatureMatcher.byScore(Condition.EQ, "3.3").matches(sf));
+ assertTrue(FeatureMatcher.byScore(Condition.EQ, "3.2").matches(sf));
+
+ assertFalse(FeatureMatcher.byScore(Condition.GE, "3.3").matches(sf));
+ assertTrue(FeatureMatcher.byScore(Condition.GE, "3.2").matches(sf));
+ assertTrue(FeatureMatcher.byScore(Condition.GE, "2.2").matches(sf));
+
+ assertFalse(FeatureMatcher.byScore(Condition.GT, "3.3").matches(sf));
+ assertFalse(FeatureMatcher.byScore(Condition.GT, "3.2").matches(sf));
+ assertTrue(FeatureMatcher.byScore(Condition.GT, "2.2").matches(sf));
+ }
+
+ @Test(groups = "Functional")
+ public void testMatches_byAttribute()
+ {
+ /*
+ * a numeric matcher - MatcherTest covers more conditions
+ */
+ FeatureMatcherI fm = FeatureMatcher
+ .byAttribute(Condition.GE, "-2", "AF");
+ SequenceFeature sf = new SequenceFeature("Cath", "desc", 11, 12, "grp");
+ assertFalse(fm.matches(sf));
+ sf.setValue("AF", "foobar");
+ assertFalse(fm.matches(sf));
+ sf.setValue("AF", "-2");
+ assertTrue(fm.matches(sf));
+ sf.setValue("AF", "-1");
+ assertTrue(fm.matches(sf));
+ sf.setValue("AF", "-3");
+ assertFalse(fm.matches(sf));
+ sf.setValue("AF", "");
+ assertFalse(fm.matches(sf));
+
+ /*
+ * a string pattern matcher
+ */
+ fm = FeatureMatcher.byAttribute(Condition.Contains, "Cat", "AF");
+ assertFalse(fm.matches(sf));
+ sf.setValue("AF", "raining cats and dogs");
+ assertTrue(fm.matches(sf));
+
+ fm = FeatureMatcher.byAttribute(Condition.Present, "", "AC");
+ assertFalse(fm.matches(sf));
+ sf.setValue("AC", "21");
+ assertTrue(fm.matches(sf));
+
+ fm = FeatureMatcher.byAttribute(Condition.NotPresent, "", "AC_Females");
+ assertTrue(fm.matches(sf));
+ sf.setValue("AC_Females", "21");
+ assertFalse(fm.matches(sf));
+ }
+
+ @Test(groups = "Functional")
+ public void testToString()
+ {
+ Locale.setDefault(Locale.ENGLISH);
+
+ /*
+ * toString uses the i18n translation of the enum conditions
+ */
+ FeatureMatcherI fm = FeatureMatcher.byAttribute(Condition.LT, "1.2",
+ "AF");
+ assertEquals(fm.toString(), "AF < 1.2");
+
+ /*
+ * Present / NotPresent omit the value pattern
+ */
+ fm = FeatureMatcher.byAttribute(Condition.Present, "", "AF");
+ assertEquals(fm.toString(), "AF is present");
+ fm = FeatureMatcher.byAttribute(Condition.NotPresent, "", "AF");
+ assertEquals(fm.toString(), "AF is not present");
+
+ /*
+ * by Label
+ */
+ fm = FeatureMatcher.byLabel(Condition.Matches, "foobar");
+ assertEquals(fm.toString(),
+ MessageManager.getString("label.label") + " matches 'foobar'");
+
+ /*
+ * by Score
+ */
+ fm = FeatureMatcher.byScore(Condition.GE, "12.2");
+ assertEquals(fm.toString(),
+ MessageManager.getString("label.score") + " >= 12.2");
+ }
+
+ @Test(groups = "Functional")
+ public void testGetAttribute()
+ {
+ FeatureMatcherI fm = FeatureMatcher.byAttribute(Condition.GE, "-2",
+ "AF");
+ assertEquals(fm.getAttribute(), new String[] { "AF" });
+
+ /*
+ * compound key (attribute / subattribute)
+ */
+ fm = FeatureMatcher.byAttribute(Condition.GE, "-2F", "CSQ",
+ "Consequence");
+ assertEquals(fm.getAttribute(), new String[] { "CSQ", "Consequence" });
+
+ /*
+ * answers null if match is by Label or by Score
+ */
+ assertNull(FeatureMatcher.byLabel(Condition.NotContains, "foo")
+ .getAttribute());
+ assertNull(FeatureMatcher.byScore(Condition.LE, "-1").getAttribute());
+ }
+
+ @Test(groups = "Functional")
+ public void testIsByAttribute()
+ {
+ assertFalse(FeatureMatcher.byLabel(Condition.NotContains, "foo")
+ .isByAttribute());
+ assertFalse(FeatureMatcher.byScore(Condition.LE, "-1").isByAttribute());
+ assertTrue(FeatureMatcher.byAttribute(Condition.LE, "-1", "AC")
+ .isByAttribute());
+ }
+
+ @Test(groups = "Functional")
+ public void testIsByLabel()
+ {
+ assertTrue(FeatureMatcher.byLabel(Condition.NotContains, "foo")
+ .isByLabel());
+ assertFalse(FeatureMatcher.byScore(Condition.LE, "-1").isByLabel());
+ assertFalse(FeatureMatcher.byAttribute(Condition.LE, "-1", "AC")
+ .isByLabel());
+ }
+
+ @Test(groups = "Functional")
+ public void testIsByScore()
+ {
+ assertFalse(FeatureMatcher.byLabel(Condition.NotContains, "foo")
+ .isByScore());
+ assertTrue(FeatureMatcher.byScore(Condition.LE, "-1").isByScore());
+ assertFalse(FeatureMatcher.byAttribute(Condition.LE, "-1", "AC")
+ .isByScore());
+ }
+
+ @Test(groups = "Functional")
+ public void testGetMatcher()
+ {
+ FeatureMatcherI fm = FeatureMatcher.byAttribute(Condition.GE, "-2f",
+ "AF");
+ assertEquals(fm.getMatcher().getCondition(), Condition.GE);
+ assertEquals(fm.getMatcher().getFloatValue(), -2F);
+ assertEquals(fm.getMatcher().getPattern(), "-2.0");
+ }
+
+ @Test(groups = "Functional")
+ public void testFromString()
+ {
+ FeatureMatcherI fm = FeatureMatcher.fromString("'AF' LT 1.2");
+ assertFalse(fm.isByLabel());
+ assertFalse(fm.isByScore());
+ assertEquals(fm.getAttribute(), new String[] { "AF" });
+ assertSame(Condition.LT, fm.getMatcher().getCondition());
+ assertEquals(fm.getMatcher().getFloatValue(), 1.2f);
+ assertEquals(fm.getMatcher().getPattern(), "1.2");
+
+ // quotes are optional, condition is not case sensitive
+ fm = FeatureMatcher.fromString("AF lt '1.2'");
+ assertFalse(fm.isByLabel());
+ assertFalse(fm.isByScore());
+ assertEquals(fm.getAttribute(), new String[] { "AF" });
+ assertSame(Condition.LT, fm.getMatcher().getCondition());
+ assertEquals(fm.getMatcher().getFloatValue(), 1.2f);
+ assertEquals(fm.getMatcher().getPattern(), "1.2");
+
+ fm = FeatureMatcher.fromString("'AF' Present");
+ assertFalse(fm.isByLabel());
+ assertFalse(fm.isByScore());
+ assertEquals(fm.getAttribute(), new String[] { "AF" });
+ assertSame(Condition.Present, fm.getMatcher().getCondition());
+
+ fm = FeatureMatcher.fromString("CSQ:Consequence contains damaging");
+ assertFalse(fm.isByLabel());
+ assertFalse(fm.isByScore());
+ assertEquals(fm.getAttribute(), new String[] { "CSQ", "Consequence" });
+ assertSame(Condition.Contains, fm.getMatcher().getCondition());
+ assertEquals(fm.getMatcher().getPattern(), "damaging");
+
+ // keyword Label is not case sensitive
+ fm = FeatureMatcher.fromString("LABEL Matches 'foobar'");
+ assertTrue(fm.isByLabel());
+ assertFalse(fm.isByScore());
+ assertNull(fm.getAttribute());
+ assertSame(Condition.Matches, fm.getMatcher().getCondition());
+ assertEquals(fm.getMatcher().getPattern(), "foobar");
+
+ fm = FeatureMatcher.fromString("'Label' matches 'foo bar'");
+ assertTrue(fm.isByLabel());
+ assertFalse(fm.isByScore());
+ assertNull(fm.getAttribute());
+ assertSame(Condition.Matches, fm.getMatcher().getCondition());
+ assertEquals(fm.getMatcher().getPattern(), "foo bar");
+
+ // quotes optional on pattern
+ fm = FeatureMatcher.fromString("'Label' matches foo bar");
+ assertTrue(fm.isByLabel());
+ assertFalse(fm.isByScore());
+ assertNull(fm.getAttribute());
+ assertSame(Condition.Matches, fm.getMatcher().getCondition());
+ assertEquals(fm.getMatcher().getPattern(), "foo bar");
+
+ fm = FeatureMatcher.fromString("Score GE 12.2");
+ assertFalse(fm.isByLabel());
+ assertTrue(fm.isByScore());
+ assertNull(fm.getAttribute());
+ assertSame(Condition.GE, fm.getMatcher().getCondition());
+ assertEquals(fm.getMatcher().getPattern(), "12.2");
+ assertEquals(fm.getMatcher().getFloatValue(), 12.2f);
+
+ // keyword Score is not case sensitive
+ fm = FeatureMatcher.fromString("'SCORE' ge '12.2'");
+ assertFalse(fm.isByLabel());
+ assertTrue(fm.isByScore());
+ assertNull(fm.getAttribute());
+ assertSame(Condition.GE, fm.getMatcher().getCondition());
+ assertEquals(fm.getMatcher().getPattern(), "12.2");
+ assertEquals(fm.getMatcher().getFloatValue(), 12.2f);
+
+ // invalid numeric pattern
+ assertNull(FeatureMatcher.fromString("Score eq twelve"));
+ // unbalanced opening quote
+ assertNull(FeatureMatcher.fromString("'Score ge 12.2"));
+ // unbalanced pattern quote
+ assertNull(FeatureMatcher.fromString("'Score' ge '12.2"));
+ // pattern missing
+ assertNull(FeatureMatcher.fromString("Score ge"));
+ // condition and pattern missing
+ assertNull(FeatureMatcher.fromString("Score"));
+ // everything missing
+ assertNull(FeatureMatcher.fromString(""));
+ }
+
+ /**
+ * Tests for toStableString which (unlike toString) does not i18n the
+ * conditions
+ */
+ @Test(groups = "Functional")
+ public void testToStableString()
+ {
+ // attribute name not quoted unless it contains space
+ FeatureMatcherI fm = FeatureMatcher.byAttribute(Condition.LT, "1.2",
+ "AF");
+ assertEquals(fm.toStableString(), "AF LT 1.2");
+
+ /*
+ * Present / NotPresent omit the value pattern
+ */
+ fm = FeatureMatcher.byAttribute(Condition.Present, "", "AF");
+ assertEquals(fm.toStableString(), "AF Present");
+ fm = FeatureMatcher.byAttribute(Condition.NotPresent, "", "AF");
+ assertEquals(fm.toStableString(), "AF NotPresent");
+
+ /*
+ * by Label
+ * pattern not quoted unless it contains space
+ */
+ fm = FeatureMatcher.byLabel(Condition.Matches, "foobar");
+ assertEquals(fm.toStableString(), "Label Matches foobar");
+
+ fm = FeatureMatcher.byLabel(Condition.Matches, "foo bar");
+ assertEquals(fm.toStableString(), "Label Matches 'foo bar'");
+
+ /*
+ * by Score
+ */
+ fm = FeatureMatcher.byScore(Condition.GE, "12.2");
+ assertEquals(fm.toStableString(), "Score GE 12.2");
+ }
+}