LEFT | RIGHT |
1 # This file is part of Adblock Plus <https://adblockplus.org/>, | 1 # This file is part of Adblock Plus <https://adblockplus.org/>, |
2 # Copyright (C) 2006-2017 eyeo GmbH | 2 # Copyright (C) 2006-present eyeo GmbH |
3 # | 3 # |
4 # Adblock Plus is free software: you can redistribute it and/or modify | 4 # Adblock Plus is free software: you can redistribute it and/or modify |
5 # it under the terms of the GNU General Public License version 3 as | 5 # it under the terms of the GNU General Public License version 3 as |
6 # published by the Free Software Foundation. | 6 # published by the Free Software Foundation. |
7 # | 7 # |
8 # Adblock Plus is distributed in the hope that it will be useful, | 8 # Adblock Plus is distributed in the hope that it will be useful, |
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 # GNU General Public License for more details. | 11 # GNU General Public License for more details. |
12 # | 12 # |
13 # You should have received a copy of the GNU General Public License | 13 # You should have received a copy of the GNU General Public License |
14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 14 # along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
15 | 15 |
16 from __future__ import unicode_literals | 16 from __future__ import unicode_literals |
17 | 17 |
18 import pytest | 18 import pytest |
19 | 19 |
20 from abp.filters import parse_line, parse_filterlist | 20 from abp.filters import ( |
21 from abp.filters.parser import InvalidLine, Comment, Metadata, ParseError | 21 parse_line, parse_filterlist, ParseError, |
| 22 SELECTOR_TYPE as ST, FILTER_ACTION as FA, FILTER_OPTION as OPT, |
| 23 ) |
| 24 from abp.filters.parser import Comment, Metadata |
22 | 25 |
23 | 26 |
24 def test_parse_empty(): | 27 def test_parse_empty(): |
25 line = parse_line(' ') | 28 line = parse_line(' ') |
26 assert line.type == 'emptyline' | 29 assert line.type == 'emptyline' |
27 | 30 |
28 | 31 |
29 @pytest.mark.parametrize('filter_text, expected', { | 32 @pytest.mark.parametrize('filter_text, expected', { |
30 '||example.com/banner.gif$image,~match-case,domain=abc.com|~def.org': { | 33 # Blocking filters with patterns and regexps and blocking exceptions. |
| 34 '*asdf*d**dd*': { |
| 35 'selector': {'type': ST.URL_PATTERN, 'value': '*asdf*d**dd*'}, |
| 36 'action': FA.BLOCK, |
| 37 }, |
| 38 '@@|*asd|f*d**dd*|': { |
| 39 'selector': {'type': ST.URL_PATTERN, 'value': '|*asd|f*d**dd*|'}, |
| 40 'action': FA.ALLOW, |
| 41 }, |
| 42 '/ddd|f?a[s]d/': { |
| 43 'selector': {'type': ST.URL_REGEXP, 'value': 'ddd|f?a[s]d'}, |
| 44 'action': FA.BLOCK, |
| 45 }, |
| 46 '@@/ddd|f?a[s]d/': { |
| 47 'selector': {'type': ST.URL_REGEXP, 'value': 'ddd|f?a[s]d'}, |
| 48 'action': FA.ALLOW, |
| 49 }, |
| 50 # Blocking filters with some options. |
| 51 'bla$match-case,~script,domain=foo.com|~bar.com,sitekey=foo': { |
| 52 'selector': {'type': ST.URL_PATTERN, 'value': 'bla'}, |
| 53 'action': FA.BLOCK, |
| 54 'options': [ |
| 55 (OPT.MATCH_CASE, True), |
| 56 (OPT.SCRIPT, False), |
| 57 (OPT.DOMAIN, [('foo.com', True), ('bar.com', False)]), |
| 58 (OPT.SITEKEY, ['foo']), |
| 59 ], |
| 60 }, |
| 61 '@@http://bla$~script,~other,sitekey=foo|bar': { |
| 62 'selector': {'type': ST.URL_PATTERN, 'value': 'http://bla'}, |
| 63 'action': FA.ALLOW, |
| 64 'options': [ |
| 65 (OPT.SCRIPT, False), |
| 66 (OPT.OTHER, False), |
| 67 (OPT.SITEKEY, ['foo', 'bar']), |
| 68 ], |
| 69 }, |
| 70 # Element hiding filters and exceptions. |
| 71 '##ddd': { |
| 72 'selector': {'type': ST.CSS, 'value': 'ddd'}, |
| 73 'action': FA.HIDE, |
| 74 'options': [], |
| 75 }, |
| 76 '#@#body > div:first-child': { |
| 77 'selector': {'type': ST.CSS, 'value': 'body > div:first-child'}, |
| 78 'action': FA.SHOW, |
| 79 'options': [], |
| 80 }, |
| 81 'foo,~bar##ddd': { |
| 82 'options': [(OPT.DOMAIN, [('foo', True), ('bar', False)])], |
| 83 }, |
| 84 # Element hiding emulation filters (extended CSS). |
| 85 'foo,~bar#?#:-abp-properties(abc)': { |
| 86 'selector': {'type': ST.XCSS, 'value': ':-abp-properties(abc)'}, |
| 87 'action': FA.HIDE, |
| 88 'options': [(OPT.DOMAIN, [('foo', True), ('bar', False)])], |
| 89 }, |
| 90 'foo.com#?#aaa :-abp-properties(abc) bbb': { |
31 'selector': { | 91 'selector': { |
32 'type': 'url-pattern', | 92 'type': ST.XCSS, |
33 'value': '||example.com/banner.gif', | 93 'value': 'aaa :-abp-properties(abc) bbb' |
34 }, | |
35 'action': 'block', | |
36 'options': { | |
37 'match-case': False, | |
38 'types-none': True, | |
39 'types-include': ['image'], | |
40 'domains-none': True, | |
41 'domains-include': ['abc.com'], | |
42 'domains-exclude': ['def.org'], | |
43 }, | 94 }, |
44 }, | 95 }, |
45 '/ab?c\\.com/$image': { | 96 '#?#:-abp-properties(|background-image: url(data:*))': { |
46 'selector': {'type': 'url-regexp', 'value': 'ab?c\\.com'}, | 97 'selector': { |
47 'action': 'block', | 98 'type': ST.XCSS, |
48 'options': { | 99 'value': ':-abp-properties(|background-image: url(data:*))' |
49 'types-none': True, | |
50 'types-include': ['image'], | |
51 }, | 100 }, |
52 }, | 101 'options': [], |
53 'abc$~image': { | |
54 'selector': {'type': 'url-pattern', 'value': 'abc'}, | |
55 'action': 'block', | |
56 'options': { | |
57 'types-exclude': ['image'], | |
58 }, | |
59 }, | |
60 '@@||example.com/good.gif': { | |
61 'selector': {'type': 'url-pattern', 'value': '||example.com/good.gif'}, | |
62 'action': 'allow', | |
63 'options': {}, | |
64 }, | |
65 '@@/ab?c\\.com/': { | |
66 'selector': {'type': 'url-regexp', 'value': 'ab?c\\.com'}, | |
67 'action': 'allow', | |
68 'options': {}, | |
69 }, | |
70 'abc.com,cdf.com##div#ad1': { | |
71 'selector': {'type': 'css', 'value': 'div#ad1'}, | |
72 'action': 'hide', | |
73 'options': { | |
74 'domains-none': True, | |
75 'domains-include': ['abc.com', 'cdf.com'], | |
76 }, | |
77 }, | |
78 '#@#div#ad1': { | |
79 'selector': {'type': 'css', 'value': 'div#ad1'}, | |
80 'action': 'show', | |
81 'options': {}, | |
82 }, | |
83 'abc.com,~cdf.abc.com#@##ad1': { | |
84 'selector': {'type': 'css', 'value': '#ad1'}, | |
85 'action': 'show', | |
86 'options': { | |
87 'domains-none': True, | |
88 'domains-include': ['abc.com'], | |
89 'domains-exclude': ['cdf.abc.com'], | |
90 }, | |
91 }, | |
92 # Exception with a site key. | |
93 '@@||abc.com/ad?$subdocument,sitekey=foo': { | |
94 'selector': {'type': 'url-pattern', 'value': '||abc.com/ad?'}, | |
95 'action': 'allow', | |
96 'options': { | |
97 'types-none': True, | |
98 'types-include': ['subdocument'], | |
99 'sitekeys': ['foo'] | |
100 }, | |
101 }, | |
102 # Element hiding filter using old (a.k.a. simple) syntax. | |
103 'abc.com#div(foo)(name=bar)(value=baz)': { | |
104 'selector': { | |
105 'type': 'abp-simple', | |
106 'value': 'div(foo)(name=bar)(value=baz)', | |
107 }, | |
108 'action': 'hide', | |
109 'options': { | |
110 'domains-none': True, | |
111 'domains-include': ['abc.com'], | |
112 }, | |
113 }, | |
114 # Exclude all types with "x|~x" trick. | |
115 'x$image,~image': {'options': {'types-none': True}}, | |
116 # More tricky combos of type flags. | |
117 'x$~image,image': {'options': {}}, | |
118 'x$~image,image,~image': {'options': {'types-exclude': ['image']}}, | |
119 'x$image,~image,image': { | |
120 'options': {'types-none': True, 'types-include': ['image']}, | |
121 }, | 102 }, |
122 }.items()) | 103 }.items()) |
123 def test_parse_filters(filter_text, expected): | 104 def test_parse_filters(filter_text, expected): |
124 """Parametric test for filter parsing""" | 105 """Parametric test for filter parsing""" |
125 parsed = parse_line(filter_text) | 106 parsed = parse_line(filter_text) |
126 assert parsed.type == 'filter' | 107 assert parsed.type == 'filter' |
127 assert parsed.text == filter_text | 108 assert parsed.text == filter_text |
128 for attribute, expected_value in expected.items(): | 109 for attribute, expected_value in expected.items(): |
129 assert getattr(parsed, attribute) == expected_value | 110 assert getattr(parsed, attribute) == expected_value |
130 | 111 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
167 def test_parse_bad_header(): | 148 def test_parse_bad_header(): |
168 with pytest.raises(ParseError): | 149 with pytest.raises(ParseError): |
169 parse_line('[Adblock 1.1]') | 150 parse_line('[Adblock 1.1]') |
170 | 151 |
171 | 152 |
172 def test_parse_filterlist(): | 153 def test_parse_filterlist(): |
173 result = parse_filterlist(['! foo', '! Title: bar']) | 154 result = parse_filterlist(['! foo', '! Title: bar']) |
174 assert list(result) == [Comment('foo'), Metadata('Title', 'bar')] | 155 assert list(result) == [Comment('foo'), Metadata('Title', 'bar')] |
175 | 156 |
176 | 157 |
177 def test_invalid_lines(): | 158 def test_exception_timing(): |
178 result = parse_filterlist([ | 159 result = parse_filterlist(['! good line', '%bad line%']) |
179 '[Bad header]', | 160 assert next(result) == Comment('good line') |
180 '! Good comment', | 161 with pytest.raises(ParseError): |
181 '%Bad instruction%', | 162 next(result) |
182 ]) | |
183 assert list(result) == [ | |
184 InvalidLine('[Bad header]', 'Malformed header'), | |
185 Comment('Good comment'), | |
186 InvalidLine('%Bad instruction%', 'Unrecognized instruction'), | |
187 ] | |
LEFT | RIGHT |