From 3e29d04bc564f89c94d1e3375de54870e03df7b1 Mon Sep 17 00:00:00 2001 From: Aqua-sama Date: Tue, 14 Apr 2020 16:39:34 +0300 Subject: Add tests for MatcherRule and RegexRule --- staging/adblock/meson.build | 8 +++- staging/adblock/options.h | 6 +++ staging/adblock/rule.cpp | 35 ---------------- staging/adblock/rule.h | 92 +++++++++++++++++++++++++++++++++--------- staging/adblock/test/rules.cpp | 80 ++++++++++++++++++++++++++++++++++++ 5 files changed, 166 insertions(+), 55 deletions(-) delete mode 100644 staging/adblock/rule.cpp create mode 100644 staging/adblock/test/rules.cpp diff --git a/staging/adblock/meson.build b/staging/adblock/meson.build index 24e87c4..972b7cf 100644 --- a/staging/adblock/meson.build +++ b/staging/adblock/meson.build @@ -1,5 +1,5 @@ lib_adblockfilter = static_library('adblockfilter', - [ 'filterlist.cpp', 'rule.cpp', 'options.cpp' ], + [ 'filterlist.cpp', 'options.cpp' ], include_directories: smolbote_interfaces, dependencies: [ dep_qt5 ] ) @@ -16,3 +16,9 @@ AdblockPlusFilterPlugin = shared_library('AdblockPlusPlugin', install_dir: get_option('libdir')/'smolbote/plugins' ) +test('filter: adblock format rules', executable('libadblockfilter_rules', + sources: 'test/rules.cpp', + link_with: lib_adblockfilter, + dependencies: [ dep_qt5, dep_catch ] +)) + diff --git a/staging/adblock/options.h b/staging/adblock/options.h index 327e0ec..ed28502 100644 --- a/staging/adblock/options.h +++ b/staging/adblock/options.h @@ -23,8 +23,14 @@ enum OptionState { }; struct Options { + // request handling options bool exception = false; + bool redirect = false; + + // pattern options bool matchcase = false; + + // request type options bool firstparty = true; bool thirdparty = true; QHash resource_options; diff --git a/staging/adblock/rule.cpp b/staging/adblock/rule.cpp deleted file mode 100644 index 38d6b40..0000000 --- a/staging/adblock/rule.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of smolbote. It's copyrighted by the contributors recorded - * in the version control history of the file, available from its original - * location: https://library.iserlohn-fortress.net/aqua/smolbote.git - * - * SPDX-License-Identifier: GPL-3.0 - */ - -#include "rule.h" -#include -#include - -using namespace AdblockPlus; - -MatcherRule::MatcherRule(const QString &rule, const Options &opt) - : options(opt) -{ - matcher = new QStringMatcher(rule, Qt::CaseInsensitive); -} - -MatcherRule::~MatcherRule() -{ - delete matcher; -} - -RegexRule::RegexRule(const QString &rule, const Options &opt) - : options(opt) -{ - regex = new QRegularExpression(rule, QRegularExpression::CaseInsensitiveOption); -} - -RegexRule::~RegexRule() -{ - delete regex; -} diff --git a/staging/adblock/rule.h b/staging/adblock/rule.h index a9a9592..03970dc 100644 --- a/staging/adblock/rule.h +++ b/staging/adblock/rule.h @@ -6,48 +6,102 @@ * SPDX-License-Identifier: GPL-3.0 */ -#include "options.h" -#include -#include - #ifndef SMOLBOTE_ADBLOCK_RULE_H #define SMOLBOTE_ADBLOCK_RULE_H -class QStringMatcher; -class QRegularExpression; +#include "options.h" +#include +#include +#include +#include namespace AdblockPlus { class Rule { public: - // virtual bool hasMatch(const QString &url) const = 0; + virtual ~Rule() = default; + virtual bool hasMatch(const QStringRef &) const = 0; + bool shouldRedirect() const + { + return options.redirect; + } + bool shouldBlock() const + { + return !options.exception; + } + +protected: + Rule(const Options &opt) + : options(opt) + { + } + const Options options; }; class MatcherRule : public Rule { - Q_DISABLE_COPY(MatcherRule) - public: - MatcherRule(const QString &rule, const Options &opt); - ~MatcherRule(); + enum MatchPosition { + UrlStartsWith, + UrlEndsWith, + UrlContains + }; + + MatcherRule(const QString &pattern, const Options &opt, const MatchPosition pos = UrlContains) + : Rule(opt) + , position(pos) + , matcher(pattern, opt.matchcase ? Qt::CaseSensitive : Qt::CaseInsensitive) + , patternLength(pattern.length()) + { + } + explicit MatcherRule(const MatcherRule &) = delete; + MatcherRule &operator=(const MatcherRule &) = delete; + + ~MatcherRule() = default; + bool hasMatch(const QStringRef &url) const override + { + const auto index = matcher.indexIn(url); + + switch(position) { + case UrlStartsWith: + return (index == 0); + case UrlEndsWith: + return (index == url.length() - patternLength); + case UrlContains: + return (index != -1); + } + } private: - Options options; - QStringMatcher *matcher; + const MatchPosition position; + const QStringMatcher matcher; + const int patternLength; }; class RegexRule : public Rule { - Q_DISABLE_COPY(RegexRule) - public: - RegexRule(const QString &rule, const Options &opt); - ~RegexRule(); + RegexRule(const QString &rule, const Options &opt) + : Rule(opt) + , regex(rule) + { + if(!opt.matchcase) { + regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption); + } + } + explicit RegexRule(const RegexRule &) = delete; + RegexRule &operator=(const RegexRule &) = delete; + + ~RegexRule() = default; + bool hasMatch(const QStringRef &url) const override + { + const auto match = regex.match(url); + return match.hasMatch(); + } private: - Options options; - QRegularExpression *regex; + QRegularExpression regex; }; } // namespace AdblockPlus diff --git a/staging/adblock/test/rules.cpp b/staging/adblock/test/rules.cpp new file mode 100644 index 0000000..d192601 --- /dev/null +++ b/staging/adblock/test/rules.cpp @@ -0,0 +1,80 @@ +#define CATCH_CONFIG_MAIN +#include "rule.h" +#include + +SCENARIO("MatcherRule") +{ + GIVEN("options with case sensitive pattern") + { + const AdblockPlus::Options opt { .matchcase=true }; + const QString patternContains("this string contains the pattern in it"); + const QString patternBegins("pattern starts this string"); + const QString patternEnds("this string ends with pattern"); + const QString patternMissing("and this one does not"); + + WHEN("contains") + { + AdblockPlus::MatcherRule rule("pattern", opt); + REQUIRE(rule.shouldBlock()); + + THEN("pattern is matched anywhere in the URL") + { + REQUIRE(rule.hasMatch(&patternContains)); + REQUIRE(rule.hasMatch(&patternBegins)); + REQUIRE(rule.hasMatch(&patternEnds)); + REQUIRE(!rule.hasMatch(&patternMissing)); + } + } + + WHEN("startsWith") + { + AdblockPlus::MatcherRule rule("pattern", opt, AdblockPlus::MatcherRule::UrlStartsWith); + REQUIRE(rule.shouldBlock()); + + THEN("pattern is matched if at the start of the URL") + { + REQUIRE(!rule.hasMatch(&patternContains)); + REQUIRE(rule.hasMatch(&patternBegins)); + REQUIRE(!rule.hasMatch(&patternEnds)); + REQUIRE(!rule.hasMatch(&patternMissing)); + } + } + + WHEN("endsWith") + { + AdblockPlus::MatcherRule rule("pattern", opt, AdblockPlus::MatcherRule::UrlEndsWith); + REQUIRE(rule.shouldBlock()); + + THEN("pattern is matched if at the end of the URL") + { + REQUIRE(!rule.hasMatch(&patternContains)); + REQUIRE(!rule.hasMatch(&patternBegins)); + REQUIRE(rule.hasMatch(&patternEnds)); + REQUIRE(!rule.hasMatch(&patternMissing)); + } + } + } +} + +SCENARIO("RegexRule") +{ + GIVEN("options with case sensitive pattern") + { + const AdblockPlus::Options opt { .matchcase=true }; + const QString patternContains("this string contains the pattern in it"); + const QString patternMissing("and this one does not"); + + WHEN("contains") + { + AdblockPlus::RegexRule rule("pattern", opt); + REQUIRE(rule.shouldBlock()); + + THEN("pattern is matched anywhere in the URL") + { + REQUIRE(rule.hasMatch(&patternContains)); + REQUIRE(!rule.hasMatch(&patternMissing)); + } + } + } +} + -- cgit v1.2.1