Index: test/FilterEngine.cpp |
diff --git a/test/FilterEngine.cpp b/test/FilterEngine.cpp |
index 46d26c1463e9c0dd5fc48637bbc9935d4c6d7ef8..739e61ee5ccda0ebd715cb623241d6cfc6b785f4 100644 |
--- a/test/FilterEngine.cpp |
+++ b/test/FilterEngine.cpp |
@@ -17,9 +17,21 @@ |
#include "BaseJsTest.h" |
#include <thread> |
+#include <condition_variable> |
using namespace AdblockPlus; |
+namespace AdblockPlus |
+{ |
+ namespace Utils |
+ { |
+ inline bool BeginsWith(const std::string& str, const std::string& beginning) |
+ { |
+ return 0 == str.compare(0, beginning.size(), beginning); |
+ } |
+ } |
+} |
+ |
namespace |
{ |
class VeryLazyFileSystem : public LazyFileSystem |
@@ -145,6 +157,122 @@ namespace |
std::this_thread::sleep_for(std::chrono::seconds(2)); |
} |
}; |
+ |
+ class FilterEngineIsAllowedConnectionTest : public BaseJsTest |
+ { |
+ class MockWebRequest : public LazyWebRequest |
+ { |
+ public: |
+ std::map</*beginning of url*/std::string, AdblockPlus::ServerResponse> responses; |
+ |
+ AdblockPlus::ServerResponse GET(const std::string& url, |
+ const AdblockPlus::HeaderList& requestHeaders) const |
+ { |
+ for (const auto& response : responses) |
+ { |
+ if (Utils::BeginsWith(url, response.first)) |
+ { |
+ return response.second; |
+ } |
+ } |
+ return LazyWebRequest::GET(url, requestHeaders); |
+ } |
+ }; |
+ class SyncStrings |
+ { |
+ public: |
+ void Add(const std::string* value) |
+ { |
+ std::lock_guard<std::mutex> lock(mutex); |
+ strings.emplace_back(!!value, value ? *value : ""); |
+ } |
+ std::vector<std::pair<bool, std::string>> GetStrings() const |
+ { |
+ std::lock_guard<std::mutex> lock(mutex); |
+ return strings; |
+ } |
+ void Clear() |
+ { |
+ std::lock_guard<std::mutex> lock(mutex); |
+ strings.clear(); |
+ } |
+ private: |
+ mutable std::mutex mutex; |
+ std::vector<std::pair<bool, std::string>> strings; |
+ }; |
+ protected: |
+ MockWebRequest* webRequest; |
+ std::string subscriptionUrlPrefix; |
+ FilterEngine::CreationParameters createParams; |
+ SyncStrings capturedConnectionTypes; |
+ bool isConnectionAllowed; |
+ FilterEnginePtr filterEngine; |
+ |
+ struct |
+ { |
+ std::string url; |
+ std::mutex mutex; |
+ std::condition_variable cv; |
+ } downloadStatusChanged; |
+ |
+ void SetUp() |
+ { |
+ BaseJsTest::SetUp(); |
+ jsEngine->SetFileSystem(AdblockPlus::FileSystemPtr(new LazyFileSystem())); |
+ jsEngine->SetWebRequest(AdblockPlus::WebRequestPtr(webRequest = new MockWebRequest())); |
+ jsEngine->SetLogSystem(AdblockPlus::LogSystemPtr(new LazyLogSystem())); |
+ |
+ subscriptionUrlPrefix = "http://example"; |
+ ServerResponse exampleSubscriptionResponse; |
+ exampleSubscriptionResponse.responseStatus = 200; |
+ exampleSubscriptionResponse.status = WebRequest::NS_OK; |
+ exampleSubscriptionResponse.responseText = "[Adblock Plus 2.0]\n||example.com"; |
+ webRequest->responses.emplace(subscriptionUrlPrefix, exampleSubscriptionResponse); |
+ createParams.preconfiguredPrefs["first_run_subscription_auto_select"] = jsEngine->NewValue(false); |
+ isConnectionAllowed = true; |
+ createParams.isConnectionAllowedCallback = [this](const std::string* allowedConnectionType)->bool{ |
+ capturedConnectionTypes.Add(allowedConnectionType); |
+ return isConnectionAllowed; |
+ }; |
+ jsEngine->SetEventCallback("filterChange", [this](const JsValueList& params/*action, item*/) |
+ { |
+ ASSERT_EQ(2u, params.size()); |
+ if (params[0]->AsString() == "subscription.downloadStatus") |
+ { |
+ { |
+ std::lock_guard<std::mutex> lock(downloadStatusChanged.mutex); |
+ downloadStatusChanged.url = params[1]->GetProperty("url")->AsString(); |
+ } |
+ downloadStatusChanged.cv.notify_one(); |
+ } |
+ }); |
+ } |
+ |
+ SubscriptionPtr EnsureExampleSubscriptionAndForceUpdate(const std::string& apppendToUrl = std::string()) |
+ { |
+ if (!filterEngine) |
+ filterEngine = FilterEngine::Create(jsEngine, createParams); |
+ auto subscriptionUrl = subscriptionUrlPrefix + apppendToUrl; |
+ auto subscription = filterEngine->GetSubscription(subscriptionUrl); |
+ EXPECT_EQ(0u, subscription->GetProperty("filters")->AsList().size()) << subscriptionUrl; |
+ EXPECT_TRUE(subscription->GetProperty("downloadStatus")->IsNull()) << subscriptionUrl; |
+ subscription->UpdateFilters(); |
+ { |
+ std::unique_lock<std::mutex> lock(downloadStatusChanged.mutex); |
+ downloadStatusChanged.cv.wait_for(lock, |
+ /*don't block tests forever*/std::chrono::seconds(5), |
+ [this, subscriptionUrl]()->bool |
+ { |
+ return subscriptionUrl == downloadStatusChanged.url; |
+ }); |
+ // Basically it's enough to wait only for downloadStatus although there |
+ // is still some JS code being executed. Any following attempt to work |
+ // with subscription object will result in execution of JS, which will |
+ // be blocked until finishing of currently running code. |
+ } |
+ return subscription; |
+ } |
+ }; |
} |
TEST_F(FilterEngineTest, FilterCreation) |
@@ -628,3 +756,90 @@ TEST_F(FilterEngineWithFreshFolder, DisableSubscriptionsAutoSelectOnFirstRun) |
const auto subscriptions = filterEngine->GetListedSubscriptions(); |
EXPECT_EQ(0u, subscriptions.size()); |
} |
+ |
+TEST_F(FilterEngineIsAllowedConnectionTest, AbsentCallbackAllowsUpdating) |
+{ |
+ createParams.isConnectionAllowedCallback = FilterEngine::IsConnectionAllowedCallback(); |
+ auto subscription = EnsureExampleSubscriptionAndForceUpdate(); |
+ EXPECT_EQ("synchronize_ok", subscription->GetProperty("downloadStatus")->AsString()); |
+ EXPECT_EQ(1u, subscription->GetProperty("filters")->AsList().size()); |
+} |
+ |
+TEST_F(FilterEngineIsAllowedConnectionTest, AllowingCallbackAllowsUpdating) |
+{ |
+ // no stored allowed_connection_type preference |
+ auto subscription = EnsureExampleSubscriptionAndForceUpdate(); |
+ EXPECT_EQ("synchronize_ok", subscription->GetProperty("downloadStatus")->AsString()); |
+ EXPECT_EQ(1u, subscription->GetProperty("filters")->AsList().size()); |
+ auto capturedConnectionTypes = this->capturedConnectionTypes.GetStrings(); |
+ ASSERT_EQ(1u, capturedConnectionTypes.size()); |
+ EXPECT_FALSE(capturedConnectionTypes[0].first); |
+} |
+ |
+TEST_F(FilterEngineIsAllowedConnectionTest, NotAllowingCallbackDoesNotAllowUpdating) |
+{ |
+ isConnectionAllowed = false; |
+ // no stored allowed_connection_type preference |
+ auto subscription = EnsureExampleSubscriptionAndForceUpdate(); |
+ EXPECT_EQ("synchronize_connection_error", subscription->GetProperty("downloadStatus")->AsString()); |
+ EXPECT_EQ(0u, subscription->GetProperty("filters")->AsList().size()); |
+ auto capturedConnectionTypes = this->capturedConnectionTypes.GetStrings(); |
+ EXPECT_EQ(1u, capturedConnectionTypes.size()); |
+} |
+ |
+TEST_F(FilterEngineIsAllowedConnectionTest, PredefinedAllowedConnectionTypeIsPassedToCallback) |
+{ |
+ std::string predefinedAllowedConnectionType = "non-metered"; |
+ createParams.preconfiguredPrefs["allowed_connection_type"] = jsEngine->NewValue(predefinedAllowedConnectionType); |
+ auto subscription = EnsureExampleSubscriptionAndForceUpdate(); |
+ EXPECT_EQ("synchronize_ok", subscription->GetProperty("downloadStatus")->AsString()); |
+ EXPECT_EQ(1u, subscription->GetProperty("filters")->AsList().size()); |
+ auto capturedConnectionTypes = this->capturedConnectionTypes.GetStrings(); |
+ ASSERT_EQ(1u, capturedConnectionTypes.size()); |
+ EXPECT_TRUE(capturedConnectionTypes[0].first); |
+ EXPECT_EQ(predefinedAllowedConnectionType, capturedConnectionTypes[0].second); |
+} |
+ |
+TEST_F(FilterEngineIsAllowedConnectionTest, ConfiguredConnectionTypeIsPassedToCallback) |
+{ |
+ // FilterEngine->RemoveSubscription is not usable here because subscriptions |
+ // are cached internally by URL. So, different URLs are used in diffirent |
+ // checks. |
+ { |
+ std::string predefinedAllowedConnectionType = "non-metered"; |
+ createParams.preconfiguredPrefs["allowed_connection_type"] = jsEngine->NewValue(predefinedAllowedConnectionType); |
+ auto subscription = EnsureExampleSubscriptionAndForceUpdate(); |
+ EXPECT_EQ("synchronize_ok", subscription->GetProperty("downloadStatus")->AsString()); |
+ EXPECT_EQ(1u, subscription->GetProperty("filters")->AsList().size()); |
+ auto capturedConnectionTypes = this->capturedConnectionTypes.GetStrings(); |
+ ASSERT_EQ(1u, capturedConnectionTypes.size()); |
+ EXPECT_TRUE(capturedConnectionTypes[0].first); |
+ EXPECT_EQ(predefinedAllowedConnectionType, capturedConnectionTypes[0].second); |
+ } |
+ capturedConnectionTypes.Clear(); |
+ { |
+ // set no value |
+ filterEngine->SetAllowedConnectionType(nullptr); |
+ auto subscription = EnsureExampleSubscriptionAndForceUpdate("subA"); |
+ EXPECT_EQ("synchronize_ok", subscription->GetProperty("downloadStatus")->AsString()); |
+ EXPECT_EQ(1u, subscription->GetProperty("filters")->AsList().size()); |
+ auto capturedConnectionTypes = this->capturedConnectionTypes.GetStrings(); |
+ ASSERT_EQ(1u, capturedConnectionTypes.size()); |
+ EXPECT_FALSE(capturedConnectionTypes[0].first); |
+ subscription->RemoveFromList(); |
+ this->capturedConnectionTypes.Clear(); |
+ } |
+ capturedConnectionTypes.Clear(); |
+ { |
+ // set some value |
+ std::string testConnection = "test connection"; |
+ filterEngine->SetAllowedConnectionType(&testConnection); |
+ auto subscription = EnsureExampleSubscriptionAndForceUpdate("subB"); |
+ EXPECT_EQ("synchronize_ok", subscription->GetProperty("downloadStatus")->AsString()); |
+ EXPECT_EQ(1u, subscription->GetProperty("filters")->AsList().size()); |
+ auto capturedConnectionTypes = this->capturedConnectionTypes.GetStrings(); |
+ ASSERT_EQ(1u, capturedConnectionTypes.size()); |
+ EXPECT_TRUE(capturedConnectionTypes[0].first); |
+ EXPECT_EQ(testConnection, capturedConnectionTypes[0].second); |
+ } |
+} |