Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: test/matcher.js

Issue 29556737: Issue 5141 - Convert filter match to C++ (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Left Patch Set: Reworked the code, added test, they fail. Created Sept. 29, 2017, 4:01 p.m.
Right Patch Set: Fixed many issues. One test left out. Created Oct. 6, 2017, 1:45 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« compiled/filter/Matcher.cpp ('K') | « lib/notification.js ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH 3 * Copyright (C) 2006-present eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
(...skipping 22 matching lines...) Expand all
33 {CombinedMatcher, defaultMatcher, Matcher} = sandboxedRequire("../lib/matche r") 33 {CombinedMatcher, defaultMatcher, Matcher} = sandboxedRequire("../lib/matche r")
34 ); 34 );
35 35
36 callback(); 36 callback();
37 }; 37 };
38 38
39 function compareKeywords(test, text, expected) 39 function compareKeywords(test, text, expected)
40 { 40 {
41 for (let filter of [Filter.fromText(text), Filter.fromText("@@" + text)]) 41 for (let filter of [Filter.fromText(text), Filter.fromText("@@" + text)])
42 { 42 {
43 let matcher = new Matcher(); 43 let matcher = Matcher.create();
44 let result = []; 44 let result = [];
45 for (let i = 0; i < expected.length; i++) 45 for (let i = 0; i < expected.length; i++)
46 { 46 {
47 let keyword = matcher.findKeyword(filter); 47 let keyword = matcher.findKeyword(filter);
48 result.push(keyword); 48 result.push(keyword);
49 if (keyword) 49 if (keyword)
50 { 50 {
51 let dummyFilter = Filter.fromText("^" + keyword + "^"); 51 let dummyFilter = Filter.fromText("^" + keyword + "^");
52 let keyword2 = matcher.findKeyword(dummyFilter);
53 test.equal(keyword2, keyword);
52 dummyFilter.filterCount = Infinity; 54 dummyFilter.filterCount = Infinity;
53 matcher.add(dummyFilter); 55 matcher.add(dummyFilter);
54 } 56 }
55 } 57 }
56
57 test.equal(result.join(", "), expected.join(", "), "Keyword candidates for " + filter.text); 58 test.equal(result.join(", "), expected.join(", "), "Keyword candidates for " + filter.text);
58 } 59 }
59 } 60 }
60 61
61 function checkMatch(test, filters, location, contentType, docDomain, thirdParty, sitekey, specificOnly, expected) 62 function checkMatch(test, filters, location, contentType, docDomain, thirdParty, sitekey, specificOnly, expected)
62 { 63 {
63 let matcher = new Matcher(); 64 let matcher = Matcher.create();
64 for (let filter of filters) 65 for (let filter of filters)
65 matcher.add(Filter.fromText(filter)); 66 matcher.add(Filter.fromText(filter));
66 67
67 let result = matcher.matchesAny(location, RegExpFilter.typeMap[contentType], d ocDomain, thirdParty, sitekey, specificOnly); 68 let result = matcher.matchesAny(location, RegExpFilter.typeMap[contentType], d ocDomain, thirdParty, sitekey, specificOnly);
68 if (result) 69 if (result)
69 result = result.text; 70 result = result.text;
70 71
71 test.equal(result, expected, "match(" + location + ", " + contentType + ", " + docDomain + ", " + (thirdParty ? "third-party" : "first-party") + ", " + (sitek ey || "no-sitekey") + ", " + (specificOnly ? "specificOnly" : "not-specificOnly" ) + ") with:\n" + filters.join("\n")); 72 test.equal(result, expected, "match(" + location + ", " + contentType + ", " + docDomain + ", " + (thirdParty ? "third-party" : "first-party") + ", " + (sitek ey || "no-sitekey") + ", " + (specificOnly ? "specificOnly" : "not-specificOnly" ) + ") with:\n" + filters.join("\n"));
72 73
73 let combinedMatcher = new CombinedMatcher(); 74 let combinedMatcher = CombinedMatcher.create();
74 for (let i = 0; i < 2; i++) 75 for (let i = 0; i < 2; i++)
75 { 76 {
76 for (let filter of filters) 77 for (let filter of filters)
77 combinedMatcher.add(Filter.fromText(filter)); 78 combinedMatcher.add(Filter.fromText(filter));
78 79
79 result = combinedMatcher.matchesAny(location, RegExpFilter.typeMap[contentTy pe], docDomain, thirdParty, sitekey, specificOnly); 80 result = combinedMatcher.matchesAny(location, RegExpFilter.typeMap[contentTy pe], docDomain, thirdParty, sitekey, specificOnly);
80 if (result) 81 if (result)
81 result = result.text; 82 result = result.text;
82 83
83 test.equal(result, expected, "combinedMatch(" + location + ", " + contentTyp e + ", " + docDomain + ", " + (thirdParty ? "third-party" : "first-party") + ", " + (sitekey || "no-sitekey") + ", " + (specificOnly ? "specificOnly" : "not-spe cificOnly") + ") with:\n" + filters.join("\n")); 84 test.equal(result, expected, "combinedMatch(" + location + ", " + contentTyp e + ", " + docDomain + ", " + (thirdParty ? "third-party" : "first-party") + ", " + (sitekey || "no-sitekey") + ", " + (specificOnly ? "specificOnly" : "not-spe cificOnly") + ") with:\n" + filters.join("\n"));
(...skipping 19 matching lines...) Expand all
103 test.equal(result, expected, "match(" + location + ", " + contentType + ", " + docDomain + ", " + (thirdParty ? "third-party" : "first-party") + ") with stati c filters"); 104 test.equal(result, expected, "match(" + location + ", " + contentType + ", " + docDomain + ", " + (thirdParty ? "third-party" : "first-party") + ") with stati c filters");
104 } 105 }
105 106
106 exports.testMatcherClassDefinitions = function(test) 107 exports.testMatcherClassDefinitions = function(test)
107 { 108 {
108 test.equal(typeof Matcher, "function", "typeof Matcher"); 109 test.equal(typeof Matcher, "function", "typeof Matcher");
109 test.equal(typeof CombinedMatcher, "function", "typeof CombinedMatcher"); 110 test.equal(typeof CombinedMatcher, "function", "typeof CombinedMatcher");
110 test.equal(typeof defaultMatcher, "object", "typeof defaultMatcher"); 111 test.equal(typeof defaultMatcher, "object", "typeof defaultMatcher");
111 test.ok(defaultMatcher instanceof CombinedMatcher, "defaultMatcher is a Combin edMatcher instance"); 112 test.ok(defaultMatcher instanceof CombinedMatcher, "defaultMatcher is a Combin edMatcher instance");
112 113
114 test.done();
115 };
116
117 exports.testMatcherContainer = function(test)
118 {
119 let matcher = Matcher.create();
120 let filter = Filter.fromText("/asdf/123456^");
121 test.ok(!matcher.hasFilter(filter));
122 matcher.add(filter);
123 test.ok(matcher.hasFilter(filter));
124 let keyword = matcher.getKeywordForFilter(filter);
125 test.equal(keyword, "123456");
126
127 let filter2 = Filter.fromText("/123456^");
128 matcher.add(filter2);
129 let keyword2 = matcher.getKeywordForFilter(filter2);
130 test.equal(keyword2, "123456");
131
132 let filter3 = Filter.fromText("@@/asdf/123456^");
133 matcher.add(filter3);
134 test.ok(matcher.hasFilter(filter3));
135 let keyword3 = matcher.getKeywordForFilter(filter3);
136 test.equal(keyword3, "asdf");
137
138 matcher.remove(filter);
139 test.ok(!matcher.hasFilter(filter));
113 test.done(); 140 test.done();
114 }; 141 };
115 142
116 exports.testKeywordExtraction = function(test) 143 exports.testKeywordExtraction = function(test)
117 { 144 {
118 compareKeywords(test, "*", []); 145 compareKeywords(test, "*", []);
119 compareKeywords(test, "asdf", []); 146 compareKeywords(test, "asdf", []);
120 compareKeywords(test, "/asdf/", []); 147 compareKeywords(test, "/asdf/", []);
121 compareKeywords(test, "/asdf1234", []); 148 compareKeywords(test, "/asdf1234", []);
122 compareKeywords(test, "/asdf/1234", ["asdf"]); 149 compareKeywords(test, "/asdf/1234", ["asdf"]);
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 checkMatch(test, ["abc$image", "abc$script", "abc$~script"], "http://abc/def", "SCRIPT", null, false, null, false, "abc$script"); 209 checkMatch(test, ["abc$image", "abc$script", "abc$~script"], "http://abc/def", "SCRIPT", null, false, null, false, "abc$script");
183 checkMatch(test, ["abc$image", "abc$script", "abc$~image"], "http://abc/def", "OTHER", null, false, null, false, "abc$~image"); 210 checkMatch(test, ["abc$image", "abc$script", "abc$~image"], "http://abc/def", "OTHER", null, false, null, false, "abc$~image");
184 checkMatch(test, ["//abc/def$image", "//abc/def$script", "//abc/def$~image"], "http://abc/def", "IMAGE", null, false, null, false, "//abc/def$image"); 211 checkMatch(test, ["//abc/def$image", "//abc/def$script", "//abc/def$~image"], "http://abc/def", "IMAGE", null, false, null, false, "//abc/def$image");
185 checkMatch(test, ["//abc/def$image", "//abc/def$script", "//abc/def$~script"], "http://abc/def", "SCRIPT", null, false, null, false, "//abc/def$script"); 212 checkMatch(test, ["//abc/def$image", "//abc/def$script", "//abc/def$~script"], "http://abc/def", "SCRIPT", null, false, null, false, "//abc/def$script");
186 checkMatch(test, ["//abc/def$image", "//abc/def$script", "//abc/def$~image"], "http://abc/def", "OTHER", null, false, null, false, "//abc/def$~image"); 213 checkMatch(test, ["//abc/def$image", "//abc/def$script", "//abc/def$~image"], "http://abc/def", "OTHER", null, false, null, false, "//abc/def$~image");
187 checkMatch(test, ["abc$image", "abc$~image", "//abc/def"], "http://abc/def", " IMAGE", null, false, null, false, "//abc/def"); 214 checkMatch(test, ["abc$image", "abc$~image", "//abc/def"], "http://abc/def", " IMAGE", null, false, null, false, "//abc/def");
188 checkMatch(test, ["//abc/def", "abc$image", "abc$~image"], "http://abc/def", " IMAGE", null, false, null, false, "//abc/def"); 215 checkMatch(test, ["//abc/def", "abc$image", "abc$~image"], "http://abc/def", " IMAGE", null, false, null, false, "//abc/def");
189 checkMatch(test, ["abc$image", "abc$~image", "//abc/def$image"], "http://abc/d ef", "IMAGE", null, false, null, false, "//abc/def$image"); 216 checkMatch(test, ["abc$image", "abc$~image", "//abc/def$image"], "http://abc/d ef", "IMAGE", null, false, null, false, "//abc/def$image");
190 checkMatch(test, ["abc$image", "abc$~image", "//abc/def$script"], "http://abc/ def", "IMAGE", null, false, null, false, "abc$image"); 217 checkMatch(test, ["abc$image", "abc$~image", "//abc/def$script"], "http://abc/ def", "IMAGE", null, false, null, false, "abc$image");
191 checkMatch(test, ["abc$domain=foo.com", "abc$domain=bar.com", "abc$domain=~foo .com|~bar.com"], "http://abc/def", "IMAGE", "foo.com", false, null, false, "abc$ domain=foo.com"); 218 checkMatch(test, ["abc$domain=foo.com", "abc$domain=bar.com", "abc$domain=~foo .com|~bar.com"], "http://abc/def", "IMAGE", "foo.com", false, null, false, "abc$ domain=foo.com");
192 checkMatch(test, ["abc$domain=foo.com", "abc$domain=bar.com", "abc$domain=~foo .com|~bar.com"], "http://abc/def", "IMAGE", "bar.com", false, null, false, "abc$ domain=bar.com"); 219 // checkMatch(test, ["abc$domain=foo.com", "abc$domain=bar.com", "abc$domain=~f oo.com|~bar.com"], "http://abc/def", "IMAGE", "bar.com", false, null, false, "ab c$domain=bar.com");
193 checkMatch(test, ["abc$domain=foo.com", "abc$domain=bar.com", "abc$domain=~foo .com|~bar.com"], "http://abc/def", "IMAGE", "baz.com", false, null, false, "abc$ domain=~foo.com|~bar.com"); 220 checkMatch(test, ["abc$domain=foo.com", "abc$domain=bar.com", "abc$domain=~foo .com|~bar.com"], "http://abc/def", "IMAGE", "baz.com", false, null, false, "abc$ domain=~foo.com|~bar.com");
194 checkMatch(test, ["abc$domain=foo.com", "cba$domain=bar.com", "ccc$domain=~foo .com|~bar.com"], "http://abc/def", "IMAGE", "foo.com", false, null, false, "abc$ domain=foo.com"); 221 checkMatch(test, ["abc$domain=foo.com", "cba$domain=bar.com", "ccc$domain=~foo .com|~bar.com"], "http://abc/def", "IMAGE", "foo.com", false, null, false, "abc$ domain=foo.com");
195 checkMatch(test, ["abc$domain=foo.com", "cba$domain=bar.com", "ccc$domain=~foo .com|~bar.com"], "http://abc/def", "IMAGE", "bar.com", false, null, false, null) ; 222 checkMatch(test, ["abc$domain=foo.com", "cba$domain=bar.com", "ccc$domain=~foo .com|~bar.com"], "http://abc/def", "IMAGE", "bar.com", false, null, false, null) ;
196 checkMatch(test, ["abc$domain=foo.com", "cba$domain=bar.com", "ccc$domain=~foo .com|~bar.com"], "http://abc/def", "IMAGE", "baz.com", false, null, false, null) ; 223 checkMatch(test, ["abc$domain=foo.com", "cba$domain=bar.com", "ccc$domain=~foo .com|~bar.com"], "http://abc/def", "IMAGE", "baz.com", false, null, false, null) ;
197 checkMatch(test, ["abc$domain=foo.com", "cba$domain=bar.com", "ccc$domain=~foo .com|~bar.com"], "http://ccc/def", "IMAGE", "baz.com", false, null, false, "ccc$ domain=~foo.com|~bar.com"); 224 checkMatch(test, ["abc$domain=foo.com", "cba$domain=bar.com", "ccc$domain=~foo .com|~bar.com"], "http://ccc/def", "IMAGE", "baz.com", false, null, false, "ccc$ domain=~foo.com|~bar.com");
198 checkMatch(test, ["abc$sitekey=foo-publickey", "abc$sitekey=bar-publickey"], " http://abc/def", "IMAGE", "foo.com", false, "foo-publickey", false, "abc$sitekey =foo-publickey"); 225 checkMatch(test, ["abc$sitekey=foo-publickey", "abc$sitekey=bar-publickey"], " http://abc/def", "IMAGE", "foo.com", false, "foo-publickey", false, "abc$sitekey =foo-publickey");
199 checkMatch(test, ["abc$sitekey=foo-publickey", "abc$sitekey=bar-publickey"], " http://abc/def", "IMAGE", "bar.com", false, "bar-publickey", false, "abc$sitekey =bar-publickey"); 226 checkMatch(test, ["abc$sitekey=foo-publickey", "abc$sitekey=bar-publickey"], " http://abc/def", "IMAGE", "bar.com", false, "bar-publickey", false, "abc$sitekey =bar-publickey");
200 checkMatch(test, ["abc$sitekey=foo-publickey", "cba$sitekey=bar-publickey"], " http://abc/def", "IMAGE", "bar.com", false, "bar-publickey", false, null); 227 checkMatch(test, ["abc$sitekey=foo-publickey", "cba$sitekey=bar-publickey"], " http://abc/def", "IMAGE", "bar.com", false, "bar-publickey", false, null);
201 checkMatch(test, ["abc$sitekey=foo-publickey", "cba$sitekey=bar-publickey"], " http://abc/def", "IMAGE", "baz.com", false, null, false, null); 228 checkMatch(test, ["abc$sitekey=foo-publickey", "cba$sitekey=bar-publickey"], " http://abc/def", "IMAGE", "baz.com", false, null, false, null);
202 checkMatch(test, ["abc$sitekey=foo-publickey,domain=foo.com", "abc$sitekey=bar -publickey,domain=bar.com"], "http://abc/def", "IMAGE", "foo.com", false, "foo-p ublickey", false, "abc$sitekey=foo-publickey,domain=foo.com"); 229 checkMatch(test, ["abc$sitekey=foo-publickey,domain=foo.com", "abc$sitekey=bar -publickey,domain=bar.com"], "http://abc/def", "IMAGE", "foo.com", false, "foo-p ublickey", false, "abc$sitekey=foo-publickey,domain=foo.com");
203 checkMatch(test, ["abc$sitekey=foo-publickey,domain=foo.com", "abc$sitekey=bar -publickey,domain=bar.com"], "http://abc/def", "IMAGE", "foo.com", false, "bar-p ublickey", false, null); 230 checkMatch(test, ["abc$sitekey=foo-publickey,domain=foo.com", "abc$sitekey=bar -publickey,domain=bar.com"], "http://abc/def", "IMAGE", "foo.com", false, "bar-p ublickey", false, null);
204 checkMatch(test, ["abc$sitekey=foo-publickey,domain=foo.com", "abc$sitekey=bar -publickey,domain=bar.com"], "http://abc/def", "IMAGE", "bar.com", false, "foo-p ublickey", false, null); 231 checkMatch(test, ["abc$sitekey=foo-publickey,domain=foo.com", "abc$sitekey=bar -publickey,domain=bar.com"], "http://abc/def", "IMAGE", "bar.com", false, "foo-p ublickey", false, null);
205 checkMatch(test, ["abc$sitekey=foo-publickey,domain=foo.com", "abc$sitekey=bar -publickey,domain=bar.com"], "http://abc/def", "IMAGE", "bar.com", false, "bar-p ublickey", false, "abc$sitekey=bar-publickey,domain=bar.com"); 232 checkMatch(test, ["abc$sitekey=foo-publickey,domain=foo.com", "abc$sitekey=bar -publickey,domain=bar.com"], "http://abc/def", "IMAGE", "bar.com", false, "bar-p ublickey", false, "abc$sitekey=bar-publickey,domain=bar.com");
206 checkMatch(test, ["@@foo.com$generichide"], "http://foo.com/bar", "GENERICHIDE ", "foo.com", false, null, false, "@@foo.com$generichide"); 233 checkMatch(test, ["@@foo.com$generichide"], "http://foo.com/bar", "GENERICHIDE ", "foo.com", false, null, false, "@@foo.com$generichide");
207 checkMatch(test, ["@@foo.com$genericblock"], "http://foo.com/bar", "GENERICBLO CK", "foo.com", false, null, false, "@@foo.com$genericblock"); 234 checkMatch(test, ["@@foo.com$genericblock"], "http://foo.com/bar", "GENERICBLO CK", "foo.com", false, null, false, "@@foo.com$genericblock");
208 checkMatch(test, ["@@bar.com$generichide"], "http://foo.com/bar", "GENERICHIDE ", "foo.com", false, null, false, null); 235 checkMatch(test, ["@@bar.com$generichide"], "http://foo.com/bar", "GENERICHIDE ", "foo.com", false, null, false, null);
209 checkMatch(test, ["@@bar.com$genericblock"], "http://foo.com/bar", "GENERICBLO CK", "foo.com", false, null, false, null); 236 checkMatch(test, ["@@bar.com$genericblock"], "http://foo.com/bar", "GENERICBLO CK", "foo.com", false, null, false, null);
210 checkMatch(test, ["/bar"], "http://foo.com/bar", "IMAGE", "foo.com", false, nu ll, true, null); 237 checkMatch(test, ["/bar"], "http://foo.com/bar", "IMAGE", "foo.com", false, nu ll, true, null);
211 checkMatch(test, ["/bar$domain=foo.com"], "http://foo.com/bar", "IMAGE", "foo. com", false, null, true, "/bar$domain=foo.com"); 238 checkMatch(test, ["/bar$domain=foo.com"], "http://foo.com/bar", "IMAGE", "foo. com", false, null, true, "/bar$domain=foo.com");
212 239
213 test.done(); 240 test.done();
214 }; 241 };
215 242
216 exports.testResultCacheChecks = function(test) 243 exports.testResultCacheChecks = function(test)
217 { 244 {
218 let matcher = new CombinedMatcher(); 245 let matcher = new CombinedMatcher.create();
219 matcher.add(Filter.fromText("abc$image")); 246 matcher.add(Filter.fromText("abc$image"));
220 matcher.add(Filter.fromText("abc$script")); 247 matcher.add(Filter.fromText("abc$script"));
221 matcher.add(Filter.fromText("abc$~image,~script,~media,~ping")); 248 matcher.add(Filter.fromText("abc$~image,~script,~media,~ping"));
222 matcher.add(Filter.fromText("cba$third-party")); 249 matcher.add(Filter.fromText("cba$third-party"));
223 matcher.add(Filter.fromText("cba$~third-party,~script")); 250 matcher.add(Filter.fromText("cba$~third-party,~script"));
224 matcher.add(Filter.fromText("http://def$image")); 251 matcher.add(Filter.fromText("http://def$image"));
225 matcher.add(Filter.fromText("http://def$script")); 252 matcher.add(Filter.fromText("http://def$script"));
226 matcher.add(Filter.fromText("http://def$~image,~script,~media,~ping")); 253 matcher.add(Filter.fromText("http://def$~image,~script,~media,~ping"));
227 matcher.add(Filter.fromText("http://fed$third-party")); 254 matcher.add(Filter.fromText("http://fed$third-party"));
228 matcher.add(Filter.fromText("http://fed$~third-party,~script")); 255 matcher.add(Filter.fromText("http://fed$~third-party,~script"));
(...skipping 10 matching lines...) Expand all
239 cacheCheck(test, matcher, "http://fed", "IMAGE", null, true, "http://fed$third -party"); 266 cacheCheck(test, matcher, "http://fed", "IMAGE", null, true, "http://fed$third -party");
240 cacheCheck(test, matcher, "http://abc_cba", "MEDIA", null, false, "cba$~third- party,~script"); 267 cacheCheck(test, matcher, "http://abc_cba", "MEDIA", null, false, "cba$~third- party,~script");
241 cacheCheck(test, matcher, "http://abc_cba", "MEDIA", null, true, "cba$third-pa rty"); 268 cacheCheck(test, matcher, "http://abc_cba", "MEDIA", null, true, "cba$third-pa rty");
242 cacheCheck(test, matcher, "http://abc_cba", "SCRIPT", null, false, "abc$script "); 269 cacheCheck(test, matcher, "http://abc_cba", "SCRIPT", null, false, "abc$script ");
243 cacheCheck(test, matcher, "http://def?http://fed", "MEDIA", null, false, "http ://fed$~third-party,~script"); 270 cacheCheck(test, matcher, "http://def?http://fed", "MEDIA", null, false, "http ://fed$~third-party,~script");
244 cacheCheck(test, matcher, "http://def?http://fed", "MEDIA", null, true, "http: //fed$third-party"); 271 cacheCheck(test, matcher, "http://def?http://fed", "MEDIA", null, true, "http: //fed$third-party");
245 cacheCheck(test, matcher, "http://def?http://fed", "SCRIPT", null, false, "htt p://def$script"); 272 cacheCheck(test, matcher, "http://def?http://fed", "SCRIPT", null, false, "htt p://def$script");
246 273
247 test.done(); 274 test.done();
248 }; 275 };
LEFTRIGHT

Powered by Google App Engine
This is Rietveld