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