File indexing completed on 2024-11-17 04:55:15

0001 /*
0002     SPDX-License-Identifier: MPL-2.0
0003 */
0004 
0005 /* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
0006  * This Source Code Form is subject to the terms of the Mozilla Public
0007  * License, v. 2.0. If a copy of the MPL was not distributed with this
0008  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
0009 
0010 #include <string.h>
0011 #include <fstream>
0012 #include <sstream>
0013 #include <string>
0014 #include <vector>
0015 #include <cerrno>
0016 #include <algorithm>
0017 #include <iostream>
0018 #include <set>
0019 #include "./CppUnitLite/TestHarness.h"
0020 #include "./CppUnitLite/Test.h"
0021 #include "./ad_block_client.h"
0022 #include "./hash_set.h"
0023 #include "./util.h"
0024 
0025 using std::string;
0026 using std::vector;
0027 using std::endl;
0028 using std::set;
0029 using std::cout;
0030 
0031 bool testFilter(const char *rawFilter, FilterType expectedFilterType,
0032     FilterOption expectedFilterOption,
0033     const char *expectedData,
0034     set<string> &&blocked, // NOLINT
0035     set<string> &&notBlocked) { // NOLINT
0036   Filter filter;
0037   parseFilter(rawFilter, &filter);
0038 
0039   if (filter.filterOption != expectedFilterOption) {
0040     cout << "Actual filter option: " << filter.filterOption
0041       << endl << "Expected: " << expectedFilterOption << endl;
0042     return false;
0043   }
0044 
0045   if (filter.filterType != expectedFilterType) {
0046     cout << "Actual filter type: " << filter.filterType
0047       << endl << "Expected: " << expectedFilterType << endl;
0048     return false;
0049   }
0050 
0051   if (strcmp(filter.data, expectedData)) {
0052     cout << "Actual filter data: " << filter.data
0053       << endl << "Expected: " << expectedData << endl;
0054     return false;
0055   }
0056 
0057   bool ret = true;
0058   string lastChecked;
0059   std::for_each(blocked.begin(), blocked.end(),
0060       [&filter, &ret, &lastChecked, &expectedFilterOption](string const &s) {
0061     const FilterOption filterOptions = static_cast<FilterOption>(
0062             expectedFilterOption & FOResourcesOnly);
0063     ret = ret && filter.matches(s.c_str(), filterOptions);
0064     lastChecked = s;
0065   });
0066   if (!ret) {
0067     cout << "Should match but did not: " << lastChecked.c_str() << endl;
0068     return false;
0069   }
0070 
0071   std::for_each(notBlocked.begin(), notBlocked.end(),
0072       [&filter, &ret, &lastChecked, &expectedFilterOption](string const &s) {
0073     const auto filterOptions = static_cast<FilterOption>(
0074             expectedFilterOption & FOResourcesOnly);
0075     ret = ret && !filter.matches(s.c_str(), filterOptions);
0076     lastChecked = s;
0077   });
0078   if (!ret) {
0079     cout << "Should NOT match but did: " << lastChecked.c_str() << endl;
0080     return false;
0081   }
0082 
0083   return true;
0084 }
0085 
0086 TEST(client, parseFilterMatchesFilter) {
0087   CHECK(testFilter("/banner/*/img",
0088     FTNoFilterType,
0089     FONoFilterOption,
0090     "/banner/*/img",
0091     {
0092       "http://example.com/banner/foo/img",
0093       "http://example.com/banner/foo/bar/img?param",
0094       "http://example.com/banner//img/foo",
0095       "http://example.com/banner//img.gif",
0096     }, {
0097       "http://example.com/banner",
0098       "http://example.com/banner/",
0099       "http://example.com/banner/img",
0100       "http://example.com/img/banner/",
0101     }));
0102 
0103   CHECK(testFilter("/banner/*/img^",
0104     FTNoFilterType,
0105     FONoFilterOption,
0106     "/banner/*/img^",
0107     {
0108       "http://example.com/banner/foo/img",
0109       "http://example.com/banner/foo/bar/img?param",
0110       "http://example.com/banner//img/foo",
0111     }, {
0112       "http://example.com/banner/img",
0113       "http://example.com/banner/foo/imgraph",
0114       "http://example.com/banner/foo/img.gif",
0115     }));
0116 
0117   CHECK(testFilter("||ads.example.com^",
0118     static_cast<FilterType>(FTHostAnchored | FTHostOnly),
0119     FONoFilterOption,
0120     "ads.example.com^",
0121     {
0122       "http://ads.example.com/foo.gif",
0123       "http://server1.ads.example.com/foo.gif",
0124       "https://ads.example.com:8000/",
0125     }, {
0126       "http://ads.example.com.ua/foo.gif",
0127       "http://example.com/redirect/http://ads.example.com/",
0128     }));
0129 
0130   CHECK(testFilter("|http://example.com/|",
0131     static_cast<FilterType>(FTLeftAnchored | FTRightAnchored),
0132     FONoFilterOption,
0133     "http://example.com/",
0134     {
0135       "http://example.com/"
0136     }, {
0137       "http://example.com/foo.gif",
0138       "http://example.info/redirect/http://example.com/",
0139     }));
0140 
0141   CHECK(testFilter("swf|",
0142     FTRightAnchored,
0143     FONoFilterOption,
0144     "swf",
0145     {
0146       "http://example.com/annoyingflash.swf",
0147     },
0148     {
0149       "http://example.com/swf/index.html"
0150     }));
0151 
0152   CHECK(testFilter("|http://baddomain.example/",
0153     FTLeftAnchored,
0154     FONoFilterOption,
0155     "http://baddomain.example/",
0156     {
0157      "http://baddomain.example/banner.gif",
0158     },
0159     {
0160       "http://gooddomain.example/analyze?http://baddomain.example",
0161     }));
0162 
0163   CHECK(testFilter("||example.com/banner.gif",
0164     FTHostAnchored,
0165     FONoFilterOption,
0166     "example.com/banner.gif",
0167     {
0168       "http://example.com/banner.gif",
0169       "https://example.com/banner.gif",
0170       "http://www.example.com/banner.gif",
0171     },
0172     {
0173       "http://badexample.com/banner.gif",
0174       "http://gooddomain.example/analyze?http://example.com/banner.gif",
0175       "http://example.com.au/banner.gif",
0176       "http://example.com/banner2.gif",
0177     }));
0178 
0179   CHECK(testFilter("http://example.com^",
0180     FTNoFilterType,
0181     FONoFilterOption,
0182     "http://example.com^",
0183     {
0184       "http://example.com/",
0185       "http://example.com:8000/ ",
0186     },
0187     {}));
0188 
0189   CHECK(testFilter("^example.com^",
0190     FTNoFilterType,
0191     FONoFilterOption,
0192     "^example.com^",
0193     {
0194       "http://example.com:8000/foo.bar?a=12&b=%D1%82%D0%B5%D1%81%D1%82",
0195     },
0196     {}));
0197   CHECK(testFilter("^%D1%82%D0%B5%D1%81%D1%82^",
0198     FTNoFilterType,
0199     FONoFilterOption,
0200     "^%D1%82%D0%B5%D1%81%D1%82^",
0201     {
0202       "http://example.com:8000/foo.bar?a=12&b=%D1%82%D0%B5%D1%81%D1%82",
0203     },
0204     {
0205       "http://example.com:8000/foo.bar?a=12&b%D1%82%D0%B5%D1%81%D1%823",
0206     }));
0207   CHECK(testFilter("^foo.bar^",
0208     FTNoFilterType,
0209     FONoFilterOption,
0210     "^foo.bar^",
0211     {
0212       "http://example.com:8000/foo.bar?a=12&b=%D1%82%D0%B5%D1%81%D1%82"
0213     },
0214     {}));
0215   CHECK(testFilter("^promotion^",
0216     FTNoFilterType,
0217     FONoFilterOption,
0218     "^promotion^",
0219     {
0220       "http://test.com/promotion/test"
0221     },
0222     {
0223     }));
0224 #ifdef ENABLE_REGEX
0225   CHECK(testFilter("/banner[0-9]+/",
0226     FTRegex,
0227     FONoFilterOption,
0228     "banner[0-9]+",
0229     {
0230       "banner123",
0231       "testbanner1"
0232     },
0233     {
0234       "banners",
0235       "banners123"
0236     }));
0237 #endif
0238   CHECK(testFilter(
0239     "||static.tumblr.com/dhqhfum/WgAn39721/cfh_header_banner_v2.jpg",
0240     FTHostAnchored,
0241     FONoFilterOption,
0242     "static.tumblr.com/dhqhfum/WgAn39721/cfh_header_banner_v2.jpg",
0243     {
0244       "http://static.tumblr.com/dhqhfum/WgAn39721/cfh_header_banner_v2.jpg"
0245     },
0246     {}));
0247 
0248   CHECK(testFilter("||googlesyndication.com/safeframe/$third-party",
0249     FTHostAnchored,
0250     FOThirdParty,
0251     "googlesyndication.com/safeframe/",
0252     {
0253       "http://tpc.googlesyndication.com/safeframe/1-0-2/html/container.html"
0254       "#xpc=sf-gdn-exp-2&p=http%3A//slashdot.org;",
0255     },
0256     {}));
0257   CHECK(testFilter("||googlesyndication.com/safeframe/$third-party,script",
0258     FTHostAnchored,
0259     static_cast<FilterOption>(FOThirdParty|FOScript),
0260     "googlesyndication.com/safeframe/",
0261     {
0262       "http://tpc.googlesyndication.com/safeframe/1-0-2/html/container.html"
0263       "#xpc=sf-gdn-exp-2&p=http%3A//slashdot.org;",
0264     },
0265     {}));
0266 }
0267 
0268 bool checkMatch(const char *rules,
0269     set<string> &&blocked, // NOLINT
0270     set<string> &&notBlocked, // NOLINT
0271     vector<string> &&tags) { // NOLINT
0272   AdBlockClient clients[2];
0273   char * buffer = nullptr;
0274   for (int i = 0; i < 2; i++) {
0275     AdBlockClient &client = clients[i];
0276     if (i == 0) {
0277       client.parse(rules);
0278       int size;
0279       buffer = clients[0].serialize(&size);
0280     } else if (!client.deserialize(buffer)) {
0281       cout << "Deserialization failed" << endl;
0282       delete[] buffer;
0283       return false;
0284     // The second else clause is for valgrind only
0285     } else if (!client.deserialize(buffer)) {
0286       cout << "Deserialization failed" << endl;
0287       delete[] buffer;
0288       return false;
0289     }
0290     std::for_each(tags.begin(), tags.end(),
0291         [&client](string const &tag) {
0292       client.addTag(tag);
0293     });
0294 
0295     bool ret = true;
0296     string lastChecked;
0297     std::for_each(blocked.begin(), blocked.end(),
0298         [&client, &lastChecked, &ret](string const &s) {
0299       ret = ret && client.matches(s.c_str());
0300       lastChecked = s;
0301     });
0302     if (!ret) {
0303       cout << "Should match but did not: " << lastChecked.c_str() << endl;
0304       delete[] buffer;
0305       return false;
0306     }
0307 
0308     std::for_each(notBlocked.begin(), notBlocked.end(),
0309         [&client, &ret, &lastChecked](string const &s) {
0310       ret = ret && !client.matches(s.c_str());
0311       lastChecked = s;
0312     });
0313     if (!ret) {
0314       cout << "Should NOT match but did: " << lastChecked.c_str() << endl;
0315       delete[] buffer;
0316       return false;
0317     }
0318   }
0319   delete[] buffer;
0320   return true;
0321 }
0322 
0323 TEST(client, exceptionRules) {
0324   CHECK(checkMatch("adv\n"
0325                    "@@advice.",
0326     {
0327       "http://example.com/advert.html"
0328     }, {
0329       "http://example.com/advice.html",
0330     }, {}));
0331 
0332   CHECK(checkMatch("@@advice.\n"
0333                    "adv",
0334     {
0335       "http://example.com/advert.html"
0336     }, {
0337       "http://example.com/advice.html"
0338     }, {}));
0339   CHECK(checkMatch("@@|http://example.com\n"
0340                    "@@advice.\n"
0341                    "adv\n"
0342                    "!foo",
0343     {
0344       "http://examples.com/advert.html",
0345     }, {
0346       "http://example.com/advice.html",
0347       "http://example.com/advert.html",
0348       "http://examples.com/advice.html",
0349       "http://examples.com/#!foo",
0350     }, {}));
0351   CHECK(checkMatch("/ads/freewheel/*\n"
0352                    "@@||turner.com^*/ads/freewheel/*/"
0353                      "AdManager.js$domain=cnn.com",
0354     {
0355     }, {
0356       "http://z.cdn.turner.com/xslo/cvp/ads/freewheel/js/0/AdManager.js",
0357     }, {}));
0358   CHECK(checkMatch("^promotion^",
0359     {
0360       "http://yahoo.co.jp/promotion/imgs"
0361     }, {}, {}));
0362 
0363   CHECK(checkMatch("^ads^",
0364     {
0365       "http://yahoo.co.jp/ads/imgs",
0366       "http://yahoo.co.jp/ads",
0367       "http://yahoo.co.jp/ads?xyz",
0368       "http://yahoo.co.jp/xyz?ads",
0369     }, {
0370       "http://yahoo.co.jp/uploads/imgs",
0371       "http://yahoo.co.jp/adsx/imgs",
0372       "http://yahoo.co.jp/adsshmads/imgs",
0373       "ads://ads.co.ads/aads",
0374     }, {}));
0375 }
0376 
0377 TEST(client, tagTests) {
0378   // No matching tags should not match a tagged filter
0379   CHECK(checkMatch("adv$tag=stuff\n"
0380                    "somelongpath/test$tag=stuff\n"
0381                    "||brianbondy.com/$tag=brian\n"
0382                    "||brave.com$tag=brian", {}, {
0383     "http://example.com/advert.html",
0384     "http://example.com/somelongpath/test/2.html",
0385     "https://brianbondy.com/about",
0386     "https://brave.com/about"
0387   }, {}));
0388   // A matching tag should match a tagged filter
0389   CHECK(checkMatch("adv$tag=stuff\n"
0390                    "somelongpath/test$tag=stuff\n"
0391                    "||brianbondy.com/$tag=brian\n"
0392                    "||brave.com$tag=brian", {
0393     "http://example.com/advert.html",
0394     "http://example.com/somelongpath/test/2.html",
0395     "https://brianbondy.com/about",
0396     "https://brave.com/about"
0397   }, {}, {
0398     "stuff", "brian"
0399   }));
0400   // A tag which doesn't match shouldn't match
0401   CHECK(checkMatch("adv$tag=stuff\n"
0402                    "somelongpath/test$tag=stuff\n"
0403                    "||brianbondy.com/$tag=brian\n"
0404                    "||brave.com$tag=brian", {
0405   }, {
0406     "http://example.com/advert.html",
0407     "http://example.com/somelongpath/test/2.html",
0408     "https://brianbondy.com/about",
0409     "https://brave.com/about"
0410   }, {
0411     "filtertag1", "filtertag2"
0412   }));
0413 }
0414 
0415 struct OptionRuleData {
0416   OptionRuleData(const char *testUrl, FilterOption context,
0417       const char *contextDomain, bool shouldBlock) {
0418     this->testUrl = testUrl;
0419     this->context = context;
0420     this->contextDomain = contextDomain;
0421     this->shouldBlock = shouldBlock;
0422   }
0423 
0424   bool operator<(const OptionRuleData& rhs) const {
0425     return this->testUrl - rhs.testUrl < 0;
0426   }
0427 
0428   const char *testUrl;
0429   FilterOption context;
0430   const char *contextDomain;
0431   bool shouldBlock;
0432 };
0433 
0434 bool checkOptionRule(const char *rules,
0435     set<OptionRuleData> &&optionTests) { // NOLINT
0436   AdBlockClient client;
0437   client.parse(rules);
0438 
0439   bool fail = false;
0440   std::for_each(optionTests.begin(), optionTests.end(),
0441       [&client, &fail](OptionRuleData const &data) {
0442     bool matches = client.matches(data.testUrl,
0443         data.context, data.contextDomain);
0444     if (matches != data.shouldBlock) {
0445       cout << "Expected to block: " << data.shouldBlock
0446         << endl << "Actual blocks: " << matches << endl;
0447       fail = true;
0448       return;
0449     }
0450   });
0451   if (fail) {
0452     return false;
0453   }
0454 
0455   return true;
0456 }
0457 
0458 // Option rules
0459 TEST(client, optionRules) {
0460   CHECK(checkOptionRule("||example.com",
0461     {
0462       OptionRuleData("http://example.com",
0463         FOThirdParty, nullptr, true),
0464       OptionRuleData("http://example2.com", FOThirdParty, nullptr, false),
0465       OptionRuleData("http://example.com", FONotThirdParty, nullptr, true),
0466     }));
0467 
0468   CHECK(checkOptionRule("||example.com^$third-party",
0469     {
0470       OptionRuleData("http://example.com", FOScript, "brianbondy.com", true),
0471       OptionRuleData("http://example.com", FOScript, "example.com", false),
0472       OptionRuleData("http://ad.example.com", FOScript, "brianbondy.com", true),
0473       OptionRuleData("http://ad.example.com", FOScript, "example.com", false),
0474       OptionRuleData("http://example2.com", FOScript, "brianbondy.com", false),
0475       OptionRuleData("http://example2.com", FOScript, "example.com", false),
0476       OptionRuleData("http://example.com.au", FOScript,
0477           "brianbondy.com", false),
0478       OptionRuleData("http://example.com.au", FOScript, "example.com", false),
0479     }));
0480 
0481   // We should block ping rules if the resource type is FOPing
0482   CHECK(checkOptionRule("||example.com^$ping",
0483     {
0484       OptionRuleData("http://example.com", FOPing, "example.com", true),
0485       OptionRuleData("http://example.com", FOImage, "example.com", false),
0486     }));
0487 
0488   // Make sure we ignore popup rules for now
0489   CHECK(checkOptionRule("||example.com^$popup",
0490     {
0491       OptionRuleData("http://example.com", FOPopup, "example.com", false),
0492     }));
0493 
0494   CHECK(checkOptionRule("||example.com^$third-party,~script",
0495     {
0496       OptionRuleData("http://example.com",
0497           static_cast<FilterOption>(FOThirdParty | FOScript), nullptr, false),
0498       OptionRuleData("http://example.com", FOOther, nullptr, true),
0499       OptionRuleData("http://example2.com",
0500           static_cast<FilterOption>(FOThirdParty | FOOther), nullptr, false),
0501       OptionRuleData("http://example.com",
0502           static_cast<FilterOption>(FONotThirdParty | FOOther), nullptr, false),
0503     }));
0504 
0505   CHECK(checkOptionRule("adv$domain=example.com|example.net",
0506     {
0507       OptionRuleData("http://example.net/adv",
0508           FONoFilterOption, "example.net", true),
0509       OptionRuleData("http://somewebsite.com/adv",
0510           FONoFilterOption, "example.com", true),
0511       OptionRuleData("http://www.example.net/adv",
0512           FONoFilterOption, "www.example.net", true),
0513       OptionRuleData("http://my.subdomain.example.com/adv",
0514           FONoFilterOption, "my.subdomain.example.com", true),
0515       OptionRuleData("http://my.subdomain.example.com/adv",
0516           FONoFilterOption, "my.subdomain.example.com", true),
0517       OptionRuleData("http://example.com/adv",
0518           FONoFilterOption, "badexample.com", false),
0519       OptionRuleData("http://example.com/adv",
0520           FONoFilterOption, "otherdomain.net", false),
0521       OptionRuleData("http://example.net/ad",
0522           FONoFilterOption, "example.net", false),
0523     }));
0524 
0525   CHECK(checkOptionRule("adv$domain=example.com|~foo.example.com",
0526     {
0527       OptionRuleData("http://example.net/adv",
0528           FONoFilterOption, "example.com", true),
0529       OptionRuleData("http://example.net/adv",
0530           FONoFilterOption, "foo.example.com", false),
0531       OptionRuleData("http://example.net/adv",
0532           FONoFilterOption, "www.foo.example.com", false),
0533     }));
0534 
0535   CHECK(checkOptionRule("adv$domain=~example.com|foo.example.com",
0536     {
0537       OptionRuleData("http://example.net/adv",
0538           FONoFilterOption, "example.com", false),
0539       OptionRuleData("http://example.net/adv",
0540           FONoFilterOption, "foo.example.com", true),
0541       OptionRuleData("http://example.net/adv",
0542           FONoFilterOption, "www.foo.example.com", true),
0543     }));
0544 
0545   CHECK(checkOptionRule("adv$domain=~example.com",
0546     {
0547       OptionRuleData("http://example.net/adv",
0548           FONoFilterOption, "otherdomain.com", true),
0549       OptionRuleData("http://somewebsite.com/adv",
0550           FONoFilterOption, "example.com", false),
0551     }));
0552 
0553   CHECK(checkOptionRule("adv$domain=~example.com|~example.net",
0554     {
0555       OptionRuleData("http://example.net/adv",
0556           FONoFilterOption, "example.net", false),
0557       OptionRuleData("http://somewebsite.com/adv",
0558           FONoFilterOption, "example.com", false),
0559       OptionRuleData("http://www.example.net/adv",
0560           FONoFilterOption, "www.example.net", false),
0561       OptionRuleData("http://my.subdomain.example.com/adv",
0562           FONoFilterOption, "my.subdomain.example.com", false),
0563       OptionRuleData("http://example.com/adv",
0564           FONoFilterOption, "badexample.com", true),
0565       OptionRuleData("http://example.com/adv",
0566           FONoFilterOption, "otherdomain.net", true),
0567       OptionRuleData("http://example.net/ad",
0568           FONoFilterOption, "example.net", false),
0569     }));
0570 
0571   CHECK(checkOptionRule("adv$domain=example.com|~example.net",
0572     {
0573       OptionRuleData("http://example.net/adv",
0574           FONoFilterOption, "example.net", false),
0575       OptionRuleData("http://somewebsite.com/adv",
0576           FONoFilterOption, "example.com", true),
0577       OptionRuleData("http://www.example.net/adv",
0578           FONoFilterOption, "www.example.net", false),
0579       OptionRuleData("http://my.subdomain.example.com/adv",
0580           FONoFilterOption, "my.subdomain.example.com", true),
0581       OptionRuleData("http://example.com/adv",
0582           FONoFilterOption, "badexample.com", false),
0583       OptionRuleData("http://example.com/adv",
0584           FONoFilterOption, "otherdomain.net", false),
0585       OptionRuleData("http://example.net/ad",
0586           FONoFilterOption, "example.net", false),
0587     }));
0588 
0589   CHECK(checkOptionRule(
0590         "adv$domain=example.com|~foo.example.com,script",
0591     {
0592       OptionRuleData("http://example.net/adv",
0593           FOScript, "example.com", true),
0594       OptionRuleData("http://example.net/adv",
0595           FOScript, "foo.example.com", false),
0596       OptionRuleData("http://example.net/adv",
0597           FOScript, "www.foo.example.com", false),
0598       OptionRuleData("http://example.net/adv",
0599           FOOther, "example.com", false),
0600       OptionRuleData("http://example.net/adv",
0601           FOOther, "foo.example.com", false),
0602       OptionRuleData("http://example.net/adv",
0603           FOOther, "www.foo.example.com", false),
0604     }));
0605 
0606   CHECK(checkOptionRule("adv\n"
0607                         "@@advice.$~script",
0608     {
0609       OptionRuleData("http://example.com/advice.html",
0610           FOOther, nullptr, false),
0611       OptionRuleData("http://example.com/advice.html",
0612           FOScript, nullptr, true),
0613       OptionRuleData("http://example.com/advert.html",
0614           FOOther, nullptr, true),
0615       OptionRuleData("http://example.com/advert.html",
0616           FOScript, nullptr, true),
0617     }));
0618 
0619   // Single matching context domain to domain list
0620   CHECK(checkOptionRule(
0621         "||mzstatic.com^$image,object-subrequest,domain=dailymotion.com",
0622     {
0623       OptionRuleData("http://www.dailymotion.com",
0624           FONoFilterOption, "dailymotion.com", false),
0625     }));
0626 
0627   // Third party flags work correctly
0628   CHECK(checkOptionRule(
0629         "||s1.wp.com^$subdocument,third-party",
0630     {
0631       OptionRuleData("http://s1.wp.com/_static",
0632           FOScript, "windsorstar.com", false),
0633     }));
0634 
0635   // Third party flags work correctly
0636   CHECK(checkOptionRule(
0637         "/scripts/ad.",
0638     {
0639       OptionRuleData("http://a.fsdn.com/sd/js/scripts/ad.js?release_20160112",
0640           FOScript, "slashdot.org", true),
0641     }));
0642 }
0643 
0644 struct ListCounts {
0645   size_t filters;
0646   size_t cosmeticFilters;
0647   size_t htmlFilters;
0648   size_t exceptions;
0649 };
0650 
0651 ListCounts easyList = { 36342, 32667, 0, 5174 };
0652 ListCounts easyPrivacy = { 15144, 0, 0, 1202 };
0653 ListCounts ublockUnbreak = { 133, 106, 0, 494 };
0654 ListCounts braveUnbreak = { 79, 0, 0, 32 };
0655 ListCounts disconnectSimpleMalware = { 2450, 0, 0, 0 };
0656 ListCounts spam404MainBlacklist = { 5629, 166, 0, 0 };
0657 
0658 // Should parse EasyList without failing
0659 TEST(client, parse_easylist) {
0660   string && fileContents = // NOLINT
0661     getFileContents("./test/data/easylist.txt");
0662   AdBlockClient client;
0663   client.parse(fileContents.c_str());
0664 
0665   CHECK(compareNums(client.numFilters +
0666           client.numNoFingerprintFilters +
0667           client.numNoFingerprintDomainOnlyFilters +
0668           client.numNoFingerprintAntiDomainOnlyFilters +
0669           client.hostAnchoredHashSet->GetSize(),
0670         easyList.filters));
0671   CHECK(compareNums(client.numCosmeticFilters, easyList.cosmeticFilters));
0672   CHECK(compareNums(client.numHtmlFilters, easyList.htmlFilters));
0673   CHECK(compareNums(client.numExceptionFilters +
0674           client.numNoFingerprintExceptionFilters +
0675           client.numNoFingerprintDomainOnlyExceptionFilters +
0676           client.numNoFingerprintAntiDomainOnlyExceptionFilters +
0677           client.hostAnchoredExceptionHashSet->GetSize(),
0678         easyList.exceptions));
0679 }
0680 
0681 // Should parse EasyPrivacy without failing
0682 TEST(client, parse_easyprivacy) {
0683   string && fileContents = // NOLINT
0684     getFileContents("./test/data/easyprivacy.txt");
0685   AdBlockClient client;
0686   client.parse(fileContents.c_str());
0687 
0688   CHECK(compareNums(client.numFilters +
0689           client.numNoFingerprintFilters +
0690           client.numNoFingerprintDomainOnlyFilters +
0691           client.numNoFingerprintAntiDomainOnlyFilters +
0692           client.hostAnchoredHashSet->GetSize(),
0693         easyPrivacy.filters));
0694   CHECK(compareNums(client.numCosmeticFilters, easyPrivacy.cosmeticFilters));
0695   CHECK(compareNums(client.numHtmlFilters, easyPrivacy.htmlFilters));
0696   CHECK(compareNums(client.numExceptionFilters +
0697           client.numNoFingerprintExceptionFilters +
0698           client.numNoFingerprintDomainOnlyExceptionFilters +
0699           client.numNoFingerprintAntiDomainOnlyExceptionFilters +
0700           client.hostAnchoredExceptionHashSet->GetSize(),
0701         easyPrivacy.exceptions));
0702 }
0703 
0704 // Should parse ublock-unbreak list without failing
0705 TEST(client, parse_ublock_unbreak) {
0706   string && fileContents = // NOLINT
0707     getFileContents("./test/data/ublock-unbreak.txt");
0708   AdBlockClient client;
0709   client.parse(fileContents.c_str());
0710 
0711   CHECK(compareNums(client.numFilters +
0712          client.numNoFingerprintFilters +
0713           client.numNoFingerprintDomainOnlyFilters +
0714           client.numNoFingerprintAntiDomainOnlyFilters +
0715           client.hostAnchoredHashSet->GetSize(),
0716         ublockUnbreak.filters));
0717   CHECK(compareNums(client.numCosmeticFilters, ublockUnbreak.cosmeticFilters));
0718   CHECK(compareNums(client.numHtmlFilters, ublockUnbreak.htmlFilters));
0719   CHECK(compareNums(client.numExceptionFilters +
0720           client.numNoFingerprintExceptionFilters +
0721           client.numNoFingerprintDomainOnlyExceptionFilters +
0722           client.numNoFingerprintAntiDomainOnlyExceptionFilters +
0723           client.hostAnchoredExceptionHashSet->GetSize(),
0724         ublockUnbreak.exceptions));
0725 }
0726 
0727 // Should parse brave-unbreak list without failing
0728 TEST(client, parse_brave_unbreak) {
0729   string && fileContents = // NOLINT
0730     getFileContents("./test/data/brave-unbreak.txt");
0731   AdBlockClient client;
0732   client.parse(fileContents.c_str());
0733 
0734   CHECK(compareNums(client.numFilters +
0735           client.numNoFingerprintFilters +
0736           client.numNoFingerprintDomainOnlyFilters +
0737           client.numNoFingerprintAntiDomainOnlyFilters +
0738           client.hostAnchoredHashSet->GetSize(),
0739         braveUnbreak.filters));
0740   CHECK(compareNums(client.numCosmeticFilters, braveUnbreak.cosmeticFilters));
0741   CHECK(compareNums(client.numHtmlFilters, braveUnbreak.htmlFilters));
0742   CHECK(compareNums(client.numExceptionFilters +
0743           client.numNoFingerprintExceptionFilters +
0744           client.numNoFingerprintDomainOnlyExceptionFilters +
0745           client.numNoFingerprintAntiDomainOnlyExceptionFilters +
0746           client.hostAnchoredExceptionHashSet->GetSize(),
0747         braveUnbreak.exceptions));
0748 }
0749 
0750 // Should parse disconnect-simple-malware.txt list without failing
0751 TEST(client, parse_brave_disconnect_simple_malware) {
0752   string && fileContents = // NOLINT
0753     getFileContents("./test/data/disconnect-simple-malware.txt");
0754   AdBlockClient client;
0755   client.parse(fileContents.c_str());
0756 
0757   CHECK(compareNums(client.numFilters +
0758           client.numNoFingerprintFilters +
0759           client.numNoFingerprintDomainOnlyFilters +
0760           client.numNoFingerprintAntiDomainOnlyFilters +
0761           client.hostAnchoredHashSet->GetSize(),
0762         disconnectSimpleMalware.filters));
0763   CHECK(compareNums(client.numCosmeticFilters,
0764         disconnectSimpleMalware.cosmeticFilters));
0765   CHECK(compareNums(client.numHtmlFilters,
0766         disconnectSimpleMalware.htmlFilters));
0767   CHECK(compareNums(client.numExceptionFilters +
0768           client.numNoFingerprintExceptionFilters +
0769           client.numNoFingerprintDomainOnlyExceptionFilters +
0770           client.numNoFingerprintAntiDomainOnlyExceptionFilters +
0771           client.hostAnchoredExceptionHashSet->GetSize(),
0772         disconnectSimpleMalware.exceptions));
0773 }
0774 
0775 
0776 // Should parse spam404-main-blacklist.txt list without failing
0777 TEST(client, parse_spam404_main_blacklist) {
0778   string && fileContents = // NOLINT
0779     getFileContents("./test/data/spam404-main-blacklist.txt");
0780   AdBlockClient client;
0781   client.parse(fileContents.c_str());
0782 
0783   CHECK(compareNums(client.numFilters +
0784           client.numNoFingerprintFilters +
0785           client.numNoFingerprintDomainOnlyFilters +
0786           client.numNoFingerprintAntiDomainOnlyFilters +
0787           client.hostAnchoredHashSet->GetSize(),
0788         spam404MainBlacklist.filters));
0789   CHECK(compareNums(client.numCosmeticFilters,
0790         spam404MainBlacklist.cosmeticFilters));
0791   CHECK(compareNums(client.numHtmlFilters, spam404MainBlacklist.htmlFilters));
0792   CHECK(compareNums(client.numExceptionFilters +
0793           client.numNoFingerprintExceptionFilters +
0794           client.numNoFingerprintDomainOnlyExceptionFilters +
0795           client.numNoFingerprintAntiDomainOnlyExceptionFilters +
0796           client.hostAnchoredExceptionHashSet->GetSize(),
0797         spam404MainBlacklist.exceptions));
0798 
0799   const char *urlToCheck = "http://excellentmovies.net/";
0800   const char *currentPageDomain = "excellentmovies.net";
0801   CHECK(client.matches(urlToCheck, FOImage, currentPageDomain));
0802 }
0803 
0804 
0805 // Should parse lists without failing
0806 TEST(client, parse_multiList) {
0807   string && fileContentsEasylist = // NOLINT
0808     getFileContents("./test/data/easylist.txt");
0809 
0810   string && fileContentsEasyPrivacy = // NOLINT
0811     getFileContents("./test/data/easyprivacy.txt");
0812 
0813   string && fileContentsUblockUnbreak = // NOLINT
0814     getFileContents("./test/data/ublock-unbreak.txt");
0815 
0816   string && fileContentsBraveUnbreak = // NOLINT
0817     getFileContents("./test/data/brave-unbreak.txt");
0818 
0819   AdBlockClient client;
0820   client.parse(fileContentsEasylist.c_str());
0821   client.parse(fileContentsEasyPrivacy.c_str());
0822   client.parse(fileContentsUblockUnbreak.c_str());
0823   client.parse(fileContentsBraveUnbreak.c_str());
0824 
0825   // I think counts are slightly off due to same rule hash set
0826 
0827   /*
0828   CHECK(compareNums(client.numFilters +
0829           client.numNoFingerprintFilters +
0830           client.numNoFingerprintDomainOnlyFilters +
0831           client.numNoFingerprintAntiDomainOnlyFilters +
0832           client.hostAnchoredHashSet->GetSize(),
0833         easyList.filters +
0834           easyPrivacy.filters +
0835           ublockUnbreak.filters +
0836           braveUnbreak.filters));
0837           */
0838   CHECK(compareNums(client.numCosmeticFilters,
0839         easyList.cosmeticFilters +
0840           easyPrivacy.cosmeticFilters +
0841           ublockUnbreak.cosmeticFilters +
0842           braveUnbreak.cosmeticFilters));
0843 
0844   CHECK(compareNums(client.numHtmlFilters,
0845         easyList.htmlFilters+
0846           easyPrivacy.htmlFilters+
0847           ublockUnbreak.htmlFilters +
0848           braveUnbreak.htmlFilters));
0849   /*
0850   CHECK(compareNums(client.numExceptionFilters +
0851           client.hostAnchoredExceptionHashSet->GetSize() +
0852           client.numNoFingerprintExceptionFilters,
0853           client.numNoFingerprintDomainOnlyExceptionFilters +
0854           client.numNoFingerprintAntiDomainOnlyExceptionFilters +
0855         easyList.exceptions +
0856           easyPrivacy.exceptions +
0857           ublockUnbreak.exceptions +
0858           braveUnbreak.exceptions));
0859   */
0860 }
0861 
0862 // Should parse lists without failing
0863 TEST(client, parse_malware_multiList) {
0864   string && fileContentsSpam404 = // NOLINT
0865     getFileContents("./test/data/spam404-main-blacklist.txt");
0866 
0867   string && fileContentsDisconnectSimpleMalware = // NOLINT
0868     getFileContents("./test/data/disconnect-simple-malware.txt");
0869 
0870   AdBlockClient client;
0871   client.parse(fileContentsSpam404.c_str());
0872   client.parse(fileContentsDisconnectSimpleMalware.c_str());
0873 
0874   // I think counts are slightly off due to same rule hash set
0875 
0876   /*
0877   CHECK(compareNums(client.numFilters +
0878           client.numNoFingerprintFilters +
0879           client.numNoFingerprintDomainOnlyFilters +
0880           client.numNoFingerprintAntiDomainOnlyFilters +
0881           client.hostAnchoredHashSet->GetSize(),
0882         disconnectSimpleMalware.filters +
0883           spam404MainBlacklist.filters));
0884   */
0885   CHECK(compareNums(client.numCosmeticFilters,
0886         disconnectSimpleMalware.cosmeticFilters +
0887           spam404MainBlacklist.cosmeticFilters));
0888 
0889   CHECK(compareNums(client.numHtmlFilters,
0890         disconnectSimpleMalware.htmlFilters +
0891           spam404MainBlacklist.htmlFilters));
0892 
0893   CHECK(compareNums(client.numExceptionFilters +
0894           client.hostAnchoredExceptionHashSet->GetSize() +
0895           client.numNoFingerprintExceptionFilters +
0896           client.numNoFingerprintAntiDomainOnlyExceptionFilters,
0897         disconnectSimpleMalware.exceptions+
0898           spam404MainBlacklist.exceptions));
0899 }
0900 
0901 
0902 // Calling parse amongst 2 different lists should preserve both sets of rules
0903 TEST(multipleParse, multipleParse2) {
0904   AdBlockClient client;
0905   client.parse("adv\n"
0906                "@@test\n"
0907                "###test\n"
0908                "a.com$$script[src]\n");
0909   client.parse("adv2\n"
0910                "@@test2\n"
0911                "###test2\n"
0912                "adv3\n"
0913                "@@test3\n"
0914                "###test3\n"
0915                "b.com$$script[src]\n");
0916 
0917   CHECK(compareNums(client.numFilters +
0918         client.numNoFingerprintFilters +
0919           client.numNoFingerprintDomainOnlyFilters +
0920           client.numNoFingerprintAntiDomainOnlyFilters, 3));
0921   CHECK(compareNums(client.numCosmeticFilters, 3));
0922   CHECK(compareNums(client.numHtmlFilters, 2));
0923   CHECK(compareNums(client.numExceptionFilters +
0924         client.numNoFingerprintExceptionFilters +
0925         client.numNoFingerprintDomainOnlyExceptionFilters +
0926         client.numNoFingerprintAntiDomainOnlyExceptionFilters, 3));
0927 }
0928 
0929 // Demo app test
0930 TEST(demoApp, demoApp2) {
0931   AdBlockClient client;
0932   client.parse("||googlesyndication.com/safeframe/$third-party");
0933   const char *urlToCheck =
0934     "http://tpc.googlesyndication.com/safeframe/1-0-2/html/container.html";
0935   const char *currentPageDomain = "slashdot.org";
0936   CHECK(client.matches(urlToCheck, FOScript, currentPageDomain));
0937 }
0938 
0939 TEST(hostAnchoredFiltersParseCorrectly, hostAnchoredFiltersParseCorrectly2) {
0940   // Host anchor is calculated correctly
0941   Filter filter;
0942   parseFilter("||test.com$third-party", &filter);
0943   CHECK(!strcmp("test.com", filter.host));
0944 
0945   Filter filter2;
0946   parseFilter("||test.com/ok$third-party", &filter2);
0947   CHECK(!strcmp("test.com", filter2.host));
0948 
0949   Filter filter3;
0950   parseFilter("||test.com/ok", &filter3);
0951   CHECK(!strcmp("test.com", filter3.host));
0952 
0953   Filter filter4;
0954   Filter filter5;
0955   CHECK(filter4 == filter5);
0956 }
0957 
0958 TEST(misc, misc2) {
0959   for (int i = 0; i < 256; i++) {
0960     if (i == static_cast<int>(':') || i == static_cast<int>('?') ||
0961         i == static_cast<int>('/') ||
0962         i == static_cast<int>('=') || i == static_cast<int>('^') ||
0963         i == static_cast<int>('$')) {
0964       CHECK(isSeparatorChar(static_cast<char>(i)));
0965     } else {
0966       CHECK(!isSeparatorChar(static_cast<int>(static_cast<char>(i))));
0967     }
0968   }
0969 }
0970 
0971 
0972 TEST(serializationTests, serializationTests2) {
0973   AdBlockClient client;
0974   client.parse(
0975       "||googlesyndication.com$third-party\n@@||googlesyndication.ca\na$explicitcancel");
0976   int size;
0977   char * buffer = client.serialize(&size);
0978 
0979   AdBlockClient client2;
0980   CHECK(client2.deserialize(buffer));
0981   // For valgrind only
0982   client2.deserialize(buffer);
0983 
0984   Filter f(static_cast<FilterType>(FTHostAnchored | FTHostOnly), FOThirdParty,
0985       FONoFilterOption, "googlesyndication.com", 21, nullptr,
0986       "googlesyndication.com");
0987   Filter f2(FTNoFilterType, FOThirdParty, FONoFilterOption,
0988       "googleayndication.com", 21, nullptr, "googleayndication.com");
0989   CHECK(client.hostAnchoredHashSet->Exists(f));
0990   CHECK(client2.hostAnchoredHashSet->Exists(f));
0991   CHECK(!client.hostAnchoredHashSet->Exists(f2));
0992   CHECK(!client2.hostAnchoredHashSet->Exists(f2));
0993 
0994   Filter f3(static_cast<FilterType>(FTHostAnchored | FTHostOnly | FTException),
0995       FONoFilterOption, FONoFilterOption, "googlesyndication.ca",
0996       20, nullptr, "googlesyndication.ca");
0997   Filter f4(FTNoFilterType, FONoFilterOption, FONoFilterOption,
0998       "googleayndication.ca", 20, nullptr, "googleayndication.ca");
0999   CHECK(client.hostAnchoredExceptionHashSet->Exists(f3));
1000   CHECK(client2.hostAnchoredExceptionHashSet->Exists(f3));
1001   CHECK(!client.hostAnchoredExceptionHashSet->Exists(f4));
1002   CHECK(!client2.hostAnchoredExceptionHashSet->Exists(f4));
1003   CHECK(client.noFingerprintFilters[0].filterOption & FOExplicitCancel);
1004   CHECK(client2.noFingerprintFilters[0].filterOption & FOExplicitCancel);
1005   delete[] buffer;
1006 }
1007 
1008 // Testing matchingFilter
1009 TEST(findMatchingFilters, basic) {
1010   AdBlockClient client;
1011   client.parse("||googlesyndication.com/safeframe/$third-party\n"
1012       "||brianbondy.com/ads");
1013   const char *urlToCheck =
1014     "http://tpc.googlesyndication.com/safeframe/1-0-2/html/container.html";
1015   const char *currentPageDomain = "slashdot.org";
1016 
1017   Filter none;
1018   Filter *matchingFilter = &none;
1019   Filter *matchingExceptionFilter = &none;
1020 
1021   // Test finds a match
1022   CHECK(client.findMatchingFilters(urlToCheck, FOScript, currentPageDomain,
1023     &matchingFilter, &matchingExceptionFilter));
1024   CHECK(matchingFilter)
1025   CHECK(matchingExceptionFilter == nullptr)
1026   CHECK(!strcmp(matchingFilter->data, "googlesyndication.com/safeframe/"));
1027 
1028   // Test when no filter is found, returns false and sets out params to nullptr
1029   CHECK(!client.findMatchingFilters("ssafsdf.com", FOScript, currentPageDomain,
1030     &matchingFilter, &matchingExceptionFilter));
1031   CHECK(matchingFilter == nullptr)
1032   CHECK(matchingExceptionFilter == nullptr)
1033 
1034   // Parse that it finds exception filters correctly
1035   client.parse("@@safeframe\n");
1036   CHECK(!client.findMatchingFilters(urlToCheck, FOScript, currentPageDomain,
1037     &matchingFilter, &matchingExceptionFilter));
1038   CHECK(matchingFilter)
1039   CHECK(matchingExceptionFilter)
1040   CHECK(!strcmp(matchingFilter->data, "googlesyndication.com/safeframe/"));
1041   CHECK(!strcmp(matchingExceptionFilter->data, "safeframe"));
1042 }
1043 
1044 // Testing matchingFilter
1045 TEST(matchesWithFilterInfo, basic) {
1046   AdBlockClient client;
1047   client.parse("||googlesyndication.com/safeframe/$third-party\n"
1048       "||brianbondy.com/ads");
1049   const char *urlToCheck =
1050     "http://tpc.googlesyndication.com/safeframe/1-0-2/html/container.html";
1051   const char *currentPageDomain = "slashdot.org";
1052 
1053   Filter none;
1054   Filter *matchingFilter = &none;
1055   Filter *matchingExceptionFilter = &none;
1056 
1057   // Test finds a match
1058   CHECK(client.matches(urlToCheck, FOScript, currentPageDomain,
1059     &matchingFilter, &matchingExceptionFilter));
1060   CHECK(matchingFilter)
1061   CHECK(matchingExceptionFilter == nullptr)
1062   CHECK(!strcmp(matchingFilter->data, "googlesyndication.com/safeframe/"));
1063 
1064   // Test when no filter is found, returns false and sets out params to nullptr
1065   CHECK(!client.matches("ssafsdf.com", FOScript, currentPageDomain,
1066     &matchingFilter, &matchingExceptionFilter));
1067   CHECK(matchingFilter == nullptr)
1068   CHECK(matchingExceptionFilter == nullptr)
1069 
1070   // Parse that it finds exception filters correctly
1071   client.parse("@@safeframe\n");
1072   CHECK(!client.matches(urlToCheck, FOScript, currentPageDomain,
1073     &matchingFilter, &matchingExceptionFilter));
1074   CHECK(matchingFilter)
1075   CHECK(matchingExceptionFilter)
1076   CHECK(!strcmp(matchingFilter->data, "googlesyndication.com/safeframe/"));
1077   CHECK(!strcmp(matchingExceptionFilter->data, "safeframe"));
1078 
1079   // Preserves explicitcancel and important options when matching
1080   client.clear();
1081   client.parse("||brianbondy.com^$explicitcancel");
1082   bool matches = (client.matches("https://brianbondy.com/t", FOScript, "test.com",
1083     &matchingFilter, &matchingExceptionFilter));
1084   CHECK(matches)
1085   CHECK(matchingFilter)
1086   CHECK(matchingFilter->filterOption & FOExplicitCancel)
1087   CHECK(!matchingExceptionFilter)
1088 
1089   client.clear();
1090   client.parse("||brianbondy.com^$important");
1091   CHECK(client.matches("https://brianbondy.com/t", FOScript, "test.com",
1092     &matchingFilter, &matchingExceptionFilter))
1093   CHECK(matchingFilter)
1094   CHECK(matchingFilter->filterOption & FOImportant)
1095   CHECK(!matchingExceptionFilter)
1096 }