Commit 50b1a523 authored by Charles Harrison's avatar Charles Harrison Committed by Commit Bot

SubresourceFilter instrumentation test harness

This CL adds some simple tests to the SubresourceFilter in filtering
ads on sites violating the better ads standard.

Followups will include tests for other android-specific features like UI.

Bug: 800176
Change-Id: I28a1c8c7af4a2646ddf9572c2560d6cf43c18f3a
Reviewed-on: https://chromium-review.googlesource.com/857656Reviewed-by: default avatarShivani Sharma <shivanisha@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Commit-Queue: Charlie Harrison <csharrison@chromium.org>
Cr-Commit-Position: refs/heads/master@{#530644}
parent 60415dc1
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.subresource_filter;
import org.chromium.base.annotations.CalledByNative;
/**
* Class which aids in publishing test rulesets for SubresourceFilter instrumentation tests.
* All methods and members must be called on the UI thread.
*/
public final class TestSubresourceFilterPublisher {
private boolean mPublished = false;
public void createAndPublishRulesetDisallowingSuffixForTesting(String suffix) {
nativeCreateAndPublishRulesetDisallowingSuffixForTesting(suffix);
}
public boolean isPublished() {
return mPublished;
}
@CalledByNative
private void onRulesetPublished() {
mPublished = true;
}
private native void nativeCreateAndPublishRulesetDisallowingSuffixForTesting(String suffix);
}
......@@ -1035,6 +1035,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/preferences/website/WebsitePreferenceBridge.java",
"java/src/org/chromium/chrome/browser/prerender/ChromePrerenderService.java",
"java/src/org/chromium/chrome/browser/prerender/ExternalPrerenderHandler.java",
"java/src/org/chromium/chrome/browser/subresource_filter/TestSubresourceFilterPublisher.java",
"java/src/org/chromium/chrome/browser/printing/PrintShareActivity.java",
"java/src/org/chromium/chrome/browser/printing/TabPrinter.java",
"java/src/org/chromium/chrome/browser/profiles/Profile.java",
......@@ -1769,6 +1770,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/provider/ProviderTestRule.java",
"javatests/src/org/chromium/chrome/browser/push_messaging/PushMessagingTest.java",
"javatests/src/org/chromium/chrome/browser/SafeBrowsingTest.java",
"javatests/src/org/chromium/chrome/browser/SubresourceFilterTest.java",
"javatests/src/org/chromium/chrome/browser/MockSafeBrowsingApiHandler.java",
"javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java",
"javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.browser.subresource_filter.TestSubresourceFilterPublisher;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.safe_browsing.SafeBrowsingApiBridge;
import org.chromium.content.browser.test.util.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper;
import org.chromium.net.test.EmbeddedTestServer;
/**
* End to end tests of SubresourceFilter ad filtering on Android.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
"enable-features=SubresourceFilter<SB,SubresourceFilterExperimentalUI",
"force-fieldtrials=SB/Enabled",
"force-fieldtrial-params=SB.Enabled:enable_presets/liverun_on_better_ads_violating_sites"})
public final class SubresourceFilterTest {
@Rule
public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
new ChromeActivityTestRule<>(ChromeActivity.class);
private EmbeddedTestServer mTestServer;
private static final String PAGE_WITH_JPG =
"/chrome/test/data/android/subresource_filter/page-with-img.html";
private static final String METADATA_FOR_ENFORCEMENT =
"{\"matches\":[{\"threat_type\":\"13\",\"sf_bas\":\"\"}]}";
private static final String METADATA_FOR_WARNING =
"{\"matches\":[{\"threat_type\":\"13\",\"sf_bas\":\"warn\"}]}";
private void createAndPublishRulesetDisallowingSuffix(String suffix) {
TestSubresourceFilterPublisher publisher = new TestSubresourceFilterPublisher();
ThreadUtils.runOnUiThreadBlocking(
(Runnable) ()
-> publisher.createAndPublishRulesetDisallowingSuffixForTesting(suffix));
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return publisher.isPublished();
}
});
}
@Before
public void setUp() throws Exception {
mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
// Create a new temporary instance to ensure the Class is loaded. Otherwise we will get a
// ClassNotFoundException when trying to instantiate during startup.
SafeBrowsingApiBridge.setSafeBrowsingHandlerType(
new MockSafeBrowsingApiHandler().getClass());
mActivityTestRule.startMainActivityOnBlankPage();
// Disallow all jpgs.
createAndPublishRulesetDisallowingSuffix(".jpg");
}
@After
public void tearDown() throws Exception {
mTestServer.stopAndDestroyServer();
MockSafeBrowsingApiHandler.clearMockResponses();
}
@Test
@MediumTest
public void resourceNotFiltered() throws Exception {
String url = mTestServer.getURL(PAGE_WITH_JPG);
mActivityTestRule.loadUrl(url);
String loaded = mActivityTestRule.runJavaScriptCodeInCurrentTab("imgLoaded");
Assert.assertEquals("true", loaded);
}
@Test
@MediumTest
public void resourceFiltered() throws Exception {
String url = mTestServer.getURL(PAGE_WITH_JPG);
MockSafeBrowsingApiHandler.addMockResponse(url, METADATA_FOR_ENFORCEMENT);
mActivityTestRule.loadUrl(url);
String loaded = mActivityTestRule.runJavaScriptCodeInCurrentTab("imgLoaded");
Assert.assertEquals("false", loaded);
}
@Test
@MediumTest
public void resourceNotFilteredWithWarning() throws Exception {
String url = mTestServer.getURL(PAGE_WITH_JPG);
MockSafeBrowsingApiHandler.addMockResponse(url, METADATA_FOR_WARNING);
mActivityTestRule.loadUrl(url);
String loaded = mActivityTestRule.runJavaScriptCodeInCurrentTab("imgLoaded");
Assert.assertEquals("true", loaded);
}
}
......@@ -2144,6 +2144,7 @@ split_static_library("browser") {
"android/signin/signin_manager_android.h",
"android/signin/signin_promo_util_android.cc",
"android/signin/signin_promo_util_android.h",
"android/subresource_filter/test_subresource_filter_publisher.cc",
"android/tab_android.cc",
"android/tab_android.h",
"android/tab_state.cc",
......@@ -4377,6 +4378,7 @@ if (is_android) {
"../android/java/src/org/chromium/chrome/browser/snackbar/smartlockautosignin/AutoSigninSnackbarController.java",
"../android/java/src/org/chromium/chrome/browser/ssl/CaptivePortalHelper.java",
"../android/java/src/org/chromium/chrome/browser/ssl/SecurityStateModel.java",
"../android/java/src/org/chromium/chrome/browser/subresource_filter/TestSubresourceFilterPublisher.java",
"../android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSites.java",
"../android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java",
"../android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsEventReporterBridge.java",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <jni.h>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/numerics/safe_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/browser_process.h"
#include "components/subresource_filter/content/browser/content_ruleset_service.h"
#include "components/subresource_filter/core/browser/ruleset_service.h"
#include "components/url_pattern_index/unindexed_ruleset.h"
#include "content/public/browser/browser_thread.h"
#include "jni/TestSubresourceFilterPublisher_jni.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
// TODO(csharrison): This whole file is a hack, because Android cannot use
// native files that are test-only. So, this is a duplication and simplification
// of a lot of subresource_filter test harness code. Because it is compiled into
// a normal build of Chrome, I've tried to strip it down as much as possible.
// Once native test files can be used on Android, most all of this can be
// deleted.
using content::BrowserThread;
namespace {
url_pattern_index::proto::UrlRule CreateSuffixRule(const std::string& suffix) {
url_pattern_index::proto::UrlRule rule;
rule.set_semantics(url_pattern_index::proto::RULE_SEMANTICS_BLACKLIST);
rule.set_source_type(url_pattern_index::proto::SOURCE_TYPE_ANY);
rule.set_element_types(url_pattern_index::proto::ELEMENT_TYPE_ALL);
rule.set_url_pattern_type(
url_pattern_index::proto::URL_PATTERN_TYPE_SUBSTRING);
rule.set_anchor_left(url_pattern_index::proto::ANCHOR_TYPE_NONE);
rule.set_anchor_right(url_pattern_index::proto::ANCHOR_TYPE_BOUNDARY);
rule.set_url_pattern(suffix);
return rule;
}
void OnPublished(std::unique_ptr<base::ScopedTempDir> scoped_temp_dir,
subresource_filter::ContentRulesetService* service,
base::android::ScopedJavaGlobalRef<jobject> publisher) {
base::ScopedAllowBlockingForTesting allow_blocking;
// Ensure the callback does not retain |publisher| by resetting it.
service->SetRulesetPublishedCallbackForTesting(base::RepeatingClosure());
scoped_temp_dir.reset();
Java_TestSubresourceFilterPublisher_onRulesetPublished(
base::android::AttachCurrentThread(), publisher);
}
} // namespace
void JNI_TestSubresourceFilterPublisher_CreateAndPublishRulesetDisallowingSuffixForTesting(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& publisher_param,
const base::android::JavaParamRef<jstring>& suffix) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::android::ScopedJavaGlobalRef<jobject> publisher;
publisher.Reset(env, publisher_param);
// Create the ruleset contents.
std::string ruleset_contents_str;
google::protobuf::io::StringOutputStream output(&ruleset_contents_str);
url_pattern_index::UnindexedRulesetWriter ruleset_writer(&output);
ruleset_writer.AddUrlRule(
CreateSuffixRule(base::android::ConvertJavaStringToUTF8(env, suffix)));
ruleset_writer.Finish();
auto* data = reinterpret_cast<const uint8_t*>(ruleset_contents_str.data());
std::vector<uint8_t> ruleset_contents(data,
data + ruleset_contents_str.size());
// Create the ruleset directory and write the ruleset data into a file there.
base::ScopedAllowBlockingForTesting allow_blocking;
auto scoped_temp_dir = std::make_unique<base::ScopedTempDir>();
CHECK(scoped_temp_dir->CreateUniqueTempDir());
base::FilePath ruleset_path = scoped_temp_dir->GetPath().AppendASCII("1");
int ruleset_size_as_int = base::checked_cast<int>(ruleset_contents.size());
CHECK_EQ(
ruleset_size_as_int,
base::WriteFile(ruleset_path,
reinterpret_cast<const char*>(ruleset_contents.data()),
ruleset_size_as_int));
subresource_filter::ContentRulesetService* service =
g_browser_process->subresource_filter_ruleset_service();
service->SetIsAfterStartupForTesting();
service->SetRulesetPublishedCallbackForTesting(base::BindRepeating(
&OnPublished, base::Passed(&scoped_temp_dir), service, publisher));
subresource_filter::UnindexedRulesetInfo unindexed_ruleset_info;
unindexed_ruleset_info.content_version = "1";
unindexed_ruleset_info.ruleset_path = ruleset_path;
service->IndexAndStoreAndPublishRulesetIfNeeded(unindexed_ruleset_info);
}
<!DOCTYPE html>
<img id="image" src="../capybara.jpg"/>
<script>
imgLoaded = false;
var img = document.getElementById("image");
img.onload = () => { imgLoaded = true; }
</script>
......@@ -105,6 +105,11 @@ void ContentRulesetService::IndexAndStoreAndPublishRulesetIfNeeded(
unindexed_ruleset_info);
}
void ContentRulesetService::SetIsAfterStartupForTesting() {
DCHECK(ruleset_service_);
ruleset_service_->set_is_after_startup_for_testing();
}
void ContentRulesetService::Observe(
int type,
const content::NotificationSource& source,
......
......@@ -77,6 +77,8 @@ class ContentRulesetService : public RulesetServiceDelegate,
return ruleset_dealer_.get();
}
void SetIsAfterStartupForTesting();
private:
// content::NotificationObserver:
void Observe(int type,
......
......@@ -187,6 +187,8 @@ class RulesetService : public base::SupportsWeakPtr<RulesetService> {
virtual void IndexAndStoreAndPublishRulesetIfNeeded(
const UnindexedRulesetInfo& unindexed_ruleset_info);
void set_is_after_startup_for_testing() { is_after_startup_ = true; }
private:
friend class SubresourceFilteringRulesetServiceTest;
FRIEND_TEST_ALL_PREFIXES(SubresourceFilteringRulesetServiceTest,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment