47c47a9f674344ee856c27f3c4c114cae1b53a60
[jalview.git] / test / jalview / datamodel / features / FeatureMatcherSetTest.java
1 package jalview.datamodel.features;
2
3 import static org.testng.Assert.assertEquals;
4 import static org.testng.Assert.assertFalse;
5 import static org.testng.Assert.assertNull;
6 import static org.testng.Assert.assertSame;
7 import static org.testng.Assert.assertTrue;
8 import static org.testng.Assert.fail;
9
10 import jalview.datamodel.SequenceFeature;
11 import jalview.util.matcher.Condition;
12
13 import java.util.HashMap;
14 import java.util.Iterator;
15 import java.util.Locale;
16 import java.util.Map;
17
18 import org.testng.annotations.Test;
19
20 public class FeatureMatcherSetTest
21 {
22   @Test(groups = "Functional")
23   public void testMatches_byAttribute()
24   {
25     /*
26      * a numeric matcher - MatcherTest covers more conditions
27      */
28     FeatureMatcherI fm = FeatureMatcher.byAttribute(Condition.GE, "-2",
29             "AF");
30     FeatureMatcherSetI fms = new FeatureMatcherSet();
31     fms.and(fm);
32     SequenceFeature sf = new SequenceFeature("Cath", "desc", 11, 12, "grp");
33     assertFalse(fms.matches(sf));
34     sf.setValue("AF", "foobar");
35     assertFalse(fms.matches(sf));
36     sf.setValue("AF", "-2");
37     assertTrue(fms.matches(sf));
38     sf.setValue("AF", "-1");
39     assertTrue(fms.matches(sf));
40     sf.setValue("AF", "-3");
41     assertFalse(fms.matches(sf));
42     sf.setValue("AF", "");
43     assertFalse(fms.matches(sf));
44
45     /*
46      * a string pattern matcher
47      */
48     fm = FeatureMatcher.byAttribute(Condition.Contains, "Cat", "AF");
49     fms = new FeatureMatcherSet();
50     fms.and(fm);
51     assertFalse(fms.matches(sf));
52     sf.setValue("AF", "raining cats and dogs");
53     assertTrue(fms.matches(sf));
54   }
55
56   @Test(groups = "Functional")
57   public void testAnd()
58   {
59     // condition1: AF value contains "dog" (matches)
60     FeatureMatcherI fm1 = FeatureMatcher.byAttribute(Condition.Contains,
61             "dog", "AF");
62     // condition 2: CSQ value does not contain "how" (does not match)
63     FeatureMatcherI fm2 = FeatureMatcher.byAttribute(Condition.NotContains,
64             "how", "CSQ");
65
66     SequenceFeature sf = new SequenceFeature("Cath", "helix domain", 11, 12,
67             6.2f, "grp");
68     sf.setValue("AF", "raining cats and dogs");
69     sf.setValue("CSQ", "showers");
70
71     assertTrue(fm1.matches(sf));
72     assertFalse(fm2.matches(sf));
73
74     FeatureMatcherSetI fms = new FeatureMatcherSet();
75     assertTrue(fms.matches(sf)); // if no conditions, then 'all' pass
76     fms.and(fm1);
77     assertTrue(fms.matches(sf));
78     fms.and(fm2);
79     assertFalse(fms.matches(sf));
80
81     /*
82      * OR a failed attribute condition with a matched label condition
83      */
84     fms = new FeatureMatcherSet();
85     fms.and(fm2);
86     assertFalse(fms.matches(sf));
87     FeatureMatcher byLabelPass = FeatureMatcher.byLabel(Condition.Contains,
88             "Helix");
89     fms.or(byLabelPass);
90     assertTrue(fms.matches(sf));
91
92     /*
93      * OR a failed attribute condition with a failed score condition
94      */
95     fms = new FeatureMatcherSet();
96     fms.and(fm2);
97     assertFalse(fms.matches(sf));
98     FeatureMatcher byScoreFail = FeatureMatcher.byScore(Condition.LT,
99             "5.9");
100     fms.or(byScoreFail);
101     assertFalse(fms.matches(sf));
102
103     /*
104      * OR failed attribute and score conditions with matched label condition
105      */
106     fms = new FeatureMatcherSet();
107     fms.or(fm2);
108     fms.or(byScoreFail);
109     assertFalse(fms.matches(sf));
110     fms.or(byLabelPass);
111     assertTrue(fms.matches(sf));
112   }
113
114   @Test(groups = "Functional")
115   public void testToString()
116   {
117     Locale.setDefault(Locale.ENGLISH);
118     FeatureMatcherI fm1 = FeatureMatcher.byAttribute(Condition.LT, "1.2",
119             "AF");
120     assertEquals(fm1.toString(), "AF < 1.2");
121
122     FeatureMatcher fm2 = FeatureMatcher.byAttribute(Condition.NotContains,
123             "path", "CLIN_SIG");
124     assertEquals(fm2.toString(), "CLIN_SIG does not contain 'path'");
125
126     /*
127      * AND them
128      */
129     FeatureMatcherSetI fms = new FeatureMatcherSet();
130     assertEquals(fms.toString(), "");
131     fms.and(fm1);
132     assertEquals(fms.toString(), "AF < 1.2");
133     fms.and(fm2);
134     assertEquals(fms.toString(),
135             "(AF < 1.2) and (CLIN_SIG does not contain 'path')");
136
137     /*
138      * OR them
139      */
140     fms = new FeatureMatcherSet();
141     assertEquals(fms.toString(), "");
142     fms.or(fm1);
143     assertEquals(fms.toString(), "AF < 1.2");
144     fms.or(fm2);
145     assertEquals(fms.toString(),
146             "(AF < 1.2) or (CLIN_SIG does not contain 'path')");
147
148     try
149     {
150       fms.and(fm1);
151       fail("Expected exception");
152     } catch (IllegalStateException e)
153     {
154       // expected
155     }
156   }
157
158   @Test(groups = "Functional")
159   public void testOr()
160   {
161     // condition1: AF value contains "dog" (matches)
162     FeatureMatcherI fm1 = FeatureMatcher.byAttribute(Condition.Contains,
163             "dog", "AF");
164     // condition 2: CSQ value does not contain "how" (does not match)
165     FeatureMatcherI fm2 = FeatureMatcher.byAttribute(Condition.NotContains,
166             "how", "CSQ");
167
168     SequenceFeature sf = new SequenceFeature("Cath", "desc", 11, 12, "grp");
169     sf.setValue("AF", "raining cats and dogs");
170     sf.setValue("CSQ", "showers");
171
172     assertTrue(fm1.matches(sf));
173     assertFalse(fm2.matches(sf));
174
175     FeatureMatcherSetI fms = new FeatureMatcherSet();
176     assertTrue(fms.matches(sf)); // if no conditions, then 'all' pass
177     fms.or(fm1);
178     assertTrue(fms.matches(sf));
179     fms.or(fm2);
180     assertTrue(fms.matches(sf)); // true or false makes true
181
182     fms = new FeatureMatcherSet();
183     fms.or(fm2);
184     assertFalse(fms.matches(sf));
185     fms.or(fm1);
186     assertTrue(fms.matches(sf)); // false or true makes true
187
188     try
189     {
190       fms.and(fm2);
191       fail("Expected exception");
192     } catch (IllegalStateException e)
193     {
194       // expected
195     }
196   }
197
198   @Test(groups = "Functional")
199   public void testIsEmpty()
200   {
201     FeatureMatcherI fm = FeatureMatcher.byAttribute(Condition.GE, "-2.0",
202             "AF");
203     FeatureMatcherSetI fms = new FeatureMatcherSet();
204     assertTrue(fms.isEmpty());
205     fms.and(fm);
206     assertFalse(fms.isEmpty());
207   }
208
209   @Test(groups = "Functional")
210   public void testGetMatchers()
211   {
212     FeatureMatcherSetI fms = new FeatureMatcherSet();
213
214     /*
215      * empty iterable:
216      */
217     Iterator<FeatureMatcherI> iterator = fms.getMatchers().iterator();
218     assertFalse(iterator.hasNext());
219
220     /*
221      * one matcher:
222      */
223     FeatureMatcherI fm1 = FeatureMatcher.byAttribute(Condition.GE, "-2",
224             "AF");
225     fms.and(fm1);
226     iterator = fms.getMatchers().iterator();
227     assertSame(fm1, iterator.next());
228     assertFalse(iterator.hasNext());
229
230     /*
231      * two matchers:
232      */
233     FeatureMatcherI fm2 = FeatureMatcher.byAttribute(Condition.LT, "8f",
234             "AF");
235     fms.and(fm2);
236     iterator = fms.getMatchers().iterator();
237     assertSame(fm1, iterator.next());
238     assertSame(fm2, iterator.next());
239     assertFalse(iterator.hasNext());
240   }
241
242   /**
243    * Tests for the 'compound attribute' key i.e. where first key's value is a
244    * map from which we take the value for the second key, e.g. CSQ : Consequence
245    */
246   @Test(groups = "Functional")
247   public void testMatches_compoundKey()
248   {
249     /*
250      * a numeric matcher - MatcherTest covers more conditions
251      */
252     FeatureMatcherI fm = FeatureMatcher.byAttribute(Condition.GE, "-2",
253             "CSQ", "Consequence");
254     SequenceFeature sf = new SequenceFeature("Cath", "desc", 2, 10, "grp");
255     FeatureMatcherSetI fms = new FeatureMatcherSet();
256     fms.and(fm);
257     assertFalse(fms.matches(sf));
258     Map<String, String> csq = new HashMap<>();
259     sf.setValue("CSQ", csq);
260     assertFalse(fms.matches(sf));
261     csq.put("Consequence", "-2");
262     assertTrue(fms.matches(sf));
263     csq.put("Consequence", "-1");
264     assertTrue(fms.matches(sf));
265     csq.put("Consequence", "-3");
266     assertFalse(fms.matches(sf));
267     csq.put("Consequence", "");
268     assertFalse(fms.matches(sf));
269     csq.put("Consequence", "junk");
270     assertFalse(fms.matches(sf));
271
272     /*
273      * a string pattern matcher
274      */
275     fm = FeatureMatcher.byAttribute(Condition.Contains, "Cat", "CSQ",
276             "Consequence");
277     fms = new FeatureMatcherSet();
278     fms.and(fm);
279     assertFalse(fms.matches(sf));
280     csq.put("PolyPhen", "damaging");
281     assertFalse(fms.matches(sf));
282     csq.put("Consequence", "damaging");
283     assertFalse(fms.matches(sf));
284     csq.put("Consequence", "Catastrophic");
285     assertTrue(fms.matches(sf));
286   }
287
288   /**
289    * Tests for toStableString which (unlike toString) does not i18n the
290    * conditions
291    * 
292    * @see FeatureMatcherTest#testToStableString()
293    */
294   @Test(groups = "Functional")
295   public void testToStableString()
296   {
297     FeatureMatcherI fm1 = FeatureMatcher.byAttribute(Condition.LT, "1.2",
298             "AF");
299     assertEquals(fm1.toStableString(), "AF LT 1.2");
300
301     FeatureMatcher fm2 = FeatureMatcher.byAttribute(Condition.NotContains,
302             "path", "CLIN_SIG");
303     assertEquals(fm2.toStableString(), "CLIN_SIG NotContains path");
304
305     /*
306      * AND them
307      */
308     FeatureMatcherSetI fms = new FeatureMatcherSet();
309     assertEquals(fms.toStableString(), "");
310     fms.and(fm1);
311     // no brackets needed if a single condition
312     assertEquals(fms.toStableString(), "AF LT 1.2");
313     // brackets if more than one condition
314     fms.and(fm2);
315     assertEquals(fms.toStableString(),
316             "(AF LT 1.2) AND (CLIN_SIG NotContains path)");
317
318     /*
319      * OR them
320      */
321     fms = new FeatureMatcherSet();
322     assertEquals(fms.toStableString(), "");
323     fms.or(fm1);
324     assertEquals(fms.toStableString(), "AF LT 1.2");
325     fms.or(fm2);
326     assertEquals(fms.toStableString(),
327             "(AF LT 1.2) OR (CLIN_SIG NotContains path)");
328
329     /*
330      * attribute or value including space is quoted
331      */
332     FeatureMatcher fm3 = FeatureMatcher.byAttribute(Condition.NotMatches,
333             "foo bar", "CSQ", "Poly Phen");
334     assertEquals(fm3.toStableString(),
335             "'CSQ:Poly Phen' NotMatches 'foo bar'");
336     fms.or(fm3);
337     assertEquals(fms.toStableString(),
338             "(AF LT 1.2) OR (CLIN_SIG NotContains path) OR ('CSQ:Poly Phen' NotMatches 'foo bar')");
339
340     try
341     {
342       fms.and(fm1);
343       fail("Expected exception");
344     } catch (IllegalStateException e)
345     {
346       // expected
347     }
348   }
349
350   /**
351    * Tests for parsing a string representation of a FeatureMatcherSet
352    * 
353    * @see FeatureMatcherSetTest#testToStableString()
354    */
355   @Test(groups = "Functional")
356   public void testFromString()
357   {
358     String descriptor = "AF LT 1.2";
359     FeatureMatcherSetI fms = FeatureMatcherSet.fromString(descriptor);
360
361     /*
362      * shortcut asserts by verifying a 'roundtrip', 
363      * which we trust if other tests pass :-)
364      */
365     assertEquals(fms.toStableString(), descriptor);
366
367     // brackets optional, quotes optional, condition case insensitive
368     fms = FeatureMatcherSet.fromString("('AF' lt '1.2')");
369     assertEquals(fms.toStableString(), descriptor);
370
371     descriptor = "(AF LT 1.2) AND (CLIN_SIG NotContains path)";
372     fms = FeatureMatcherSet.fromString(descriptor);
373     assertEquals(fms.toStableString(), descriptor);
374
375     // AND is not case-sensitive
376     fms = FeatureMatcherSet
377             .fromString("(AF LT 1.2) and (CLIN_SIG NotContains path)");
378     assertEquals(fms.toStableString(), descriptor);
379
380     descriptor = "(AF LT 1.2) OR (CLIN_SIG NotContains path)";
381     fms = FeatureMatcherSet.fromString(descriptor);
382     assertEquals(fms.toStableString(), descriptor);
383
384     // OR is not case-sensitive
385     fms = FeatureMatcherSet
386             .fromString("(AF LT 1.2) or (CLIN_SIG NotContains path)");
387     assertEquals(fms.toStableString(), descriptor);
388
389     // can get away without brackets on last match condition
390     fms = FeatureMatcherSet
391             .fromString("(AF LT 1.2) or CLIN_SIG NotContains path");
392     assertEquals(fms.toStableString(), descriptor);
393
394     descriptor = "(AF LT 1.2) OR (CLIN_SIG NotContains path) OR ('CSQ:Poly Phen' NotMatches 'foo bar')";
395     fms = FeatureMatcherSet.fromString(descriptor);
396     assertEquals(fms.toStableString(), descriptor);
397
398     // can't mix OR and AND
399     descriptor = "(AF LT 1.2) OR (CLIN_SIG NotContains path) AND ('CSQ:Poly Phen' NotMatches 'foo bar')";
400     assertNull(FeatureMatcherSet.fromString(descriptor));
401
402     // can't mix AND and OR
403     descriptor = "(AF LT 1.2) and (CLIN_SIG NotContains path) or ('CSQ:Poly Phen' NotMatches 'foo bar')";
404     assertNull(FeatureMatcherSet.fromString(descriptor));
405
406     // brackets missing
407     assertNull(FeatureMatcherSet
408             .fromString("AF LT 1.2 or CLIN_SIG NotContains path"));
409
410     // invalid conjunction
411     assertNull(FeatureMatcherSet.fromString("(AF LT 1.2) but (AF GT -2)"));
412
413     // unbalanced quote (1)
414     assertNull(FeatureMatcherSet.fromString("('AF lt '1.2')"));
415
416     // unbalanced quote (2)
417     assertNull(FeatureMatcherSet.fromString("('AF' lt '1.2)"));
418   }
419 }