Commit 11e04d40 authored by Patrick Noland's avatar Patrick Noland Committed by Commit Bot

[Eoc] Wire PeekConditions to Java

Add Java PeekConditions class and equivalent C++ struct.
Parse AutoPeekConditions and plumb from fetch through to bridge.
Create and use ContextualSuggestionsResult to encapsulate all response data.
Add contextual_suggestions_test_utils and move common test code there.

Change-Id: I5cd8eb83c6a321377924bf14a2e71d2fefd9b98c
Reviewed-on: https://chromium-review.googlesource.com/1034015
Commit-Queue: Patrick Noland <pnoland@chromium.org>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#555261}
parent c4b5578f
...@@ -28,6 +28,7 @@ public class ContextualSuggestionsBridge { ...@@ -28,6 +28,7 @@ public class ContextualSuggestionsBridge {
public static class ContextualSuggestionsResult { public static class ContextualSuggestionsResult {
private String mPeekText; private String mPeekText;
private List<ContextualSuggestionsCluster> mClusters = new ArrayList<>(); private List<ContextualSuggestionsCluster> mClusters = new ArrayList<>();
private PeekConditions mPeekConditions = new PeekConditions();
ContextualSuggestionsResult(String peekText) { ContextualSuggestionsResult(String peekText) {
mPeekText = peekText; mPeekText = peekText;
...@@ -42,6 +43,16 @@ public class ContextualSuggestionsBridge { ...@@ -42,6 +43,16 @@ public class ContextualSuggestionsBridge {
public List<ContextualSuggestionsCluster> getClusters() { public List<ContextualSuggestionsCluster> getClusters() {
return mClusters; return mClusters;
} }
/** Server-provided conditions for when to "peek" the suggestions UI. */
public PeekConditions getPeekConditions() {
return mPeekConditions;
}
/** Setter for mPeekConditions. */
public void setPeekConditions(PeekConditions peekConditions) {
mPeekConditions = peekConditions;
}
} }
/** /**
...@@ -115,6 +126,14 @@ public class ContextualSuggestionsBridge { ...@@ -115,6 +126,14 @@ public class ContextualSuggestionsBridge {
return new ContextualSuggestionsResult(peekText); return new ContextualSuggestionsResult(peekText);
} }
@CalledByNative
private static void setPeekConditionsOnResult(ContextualSuggestionsResult result,
float pageScrollPercentage, float minimumSecondsOnPage, float maximumNumberOfPeeks) {
PeekConditions peekConditions = new PeekConditions(
pageScrollPercentage, minimumSecondsOnPage, maximumNumberOfPeeks);
result.setPeekConditions(peekConditions);
}
@CalledByNative @CalledByNative
private static void addNewClusterToResult(ContextualSuggestionsResult result, String title) { private static void addNewClusterToResult(ContextualSuggestionsResult result, String title) {
result.getClusters().add(new ContextualSuggestionsCluster(title)); result.getClusters().add(new ContextualSuggestionsCluster(title));
......
// 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.contextual_suggestions;
/** Encapsulates server-provided conditions for "peeking" the contextual suggestions UI. */
public class PeekConditions {
private final float mPageScrollPercentage;
private final float mMinimumSecondsOnPage;
private final float mMaximumNumberOfPeeks;
public PeekConditions() {
this(0, 0, 0);
}
/**
* Constructs a new PeekConditions.
* @param pageScrollPercentage float The percentage of the page that the user scrolls required
* for an auto peek to occur.
* @param minimumSecondsOnPage float The minimum time (seconds) the user spends on the page
* required for auto peek.
* @param maximumNumberOfPeeks float The maximum number of auto peeks that we can show for this
* page.
*/
public PeekConditions(
float pageScrollPercentage, float minimumSecondsOnPage, float maximumNumberOfPeeks) {
mPageScrollPercentage = pageScrollPercentage;
mMinimumSecondsOnPage = minimumSecondsOnPage;
mMaximumNumberOfPeeks = maximumNumberOfPeeks;
}
}
\ No newline at end of file
...@@ -314,6 +314,7 @@ chrome_java_sources = [ ...@@ -314,6 +314,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/contextual_suggestions/EnabledStateMonitor.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/EnabledStateMonitor.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/FetchHelper.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/FetchHelper.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/PageViewTimer.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/PageViewTimer.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/PeekConditions.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarCoordinator.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarCoordinator.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarView.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarView.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarViewBinder.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/ToolbarViewBinder.java",
......
...@@ -150,13 +150,16 @@ void ContextualSuggestionsBridge::ReportEvent( ...@@ -150,13 +150,16 @@ void ContextualSuggestionsBridge::ReportEvent(
void ContextualSuggestionsBridge::OnSuggestionsAvailable( void ContextualSuggestionsBridge::OnSuggestionsAvailable(
ScopedJavaGlobalRef<jobject> j_callback, ScopedJavaGlobalRef<jobject> j_callback,
std::string peek_text, ContextualSuggestionsResult result) {
std::vector<Cluster> clusters) {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_result = ScopedJavaLocalRef<jobject> j_result =
Java_ContextualSuggestionsBridge_createContextualSuggestionsResult( Java_ContextualSuggestionsBridge_createContextualSuggestionsResult(
env, ConvertUTF8ToJavaString(env, peek_text)); env, ConvertUTF8ToJavaString(env, result.peek_text));
for (auto& cluster : clusters) { Java_ContextualSuggestionsBridge_setPeekConditionsOnResult(
env, j_result, result.peek_conditions.page_scroll_percentage,
result.peek_conditions.minimum_seconds_on_page,
result.peek_conditions.maximum_number_of_peeks);
for (auto& cluster : result.clusters) {
Java_ContextualSuggestionsBridge_addNewClusterToResult( Java_ContextualSuggestionsBridge_addNewClusterToResult(
env, j_result, ConvertUTF8ToJavaString(env, cluster.title)); env, j_result, ConvertUTF8ToJavaString(env, cluster.title));
for (auto& suggestion : cluster.suggestions) { for (auto& suggestion : cluster.suggestions) {
......
...@@ -66,8 +66,7 @@ class ContextualSuggestionsBridge { ...@@ -66,8 +66,7 @@ class ContextualSuggestionsBridge {
void OnSuggestionsAvailable( void OnSuggestionsAvailable(
base::android::ScopedJavaGlobalRef<jobject> j_callback, base::android::ScopedJavaGlobalRef<jobject> j_callback,
std::string peek_text, ContextualSuggestionsResult result);
std::vector<ntp_snippets::Cluster> clusters);
void OnImageFetched(base::android::ScopedJavaGlobalRef<jobject> j_callback, void OnImageFetched(base::android::ScopedJavaGlobalRef<jobject> j_callback,
const gfx::Image& image); const gfx::Image& image);
......
...@@ -46,8 +46,6 @@ static_library("ntp_snippets") { ...@@ -46,8 +46,6 @@ static_library("ntp_snippets") {
"content_suggestions_provider.h", "content_suggestions_provider.h",
"content_suggestions_service.cc", "content_suggestions_service.cc",
"content_suggestions_service.h", "content_suggestions_service.h",
"contextual/cluster.cc",
"contextual/cluster.h",
"contextual/contextual_content_suggestions_service.cc", "contextual/contextual_content_suggestions_service.cc",
"contextual/contextual_content_suggestions_service.h", "contextual/contextual_content_suggestions_service.h",
"contextual/contextual_content_suggestions_service_proxy.cc", "contextual/contextual_content_suggestions_service_proxy.cc",
...@@ -61,6 +59,8 @@ static_library("ntp_snippets") { ...@@ -61,6 +59,8 @@ static_library("ntp_snippets") {
"contextual/contextual_suggestions_fetcher_impl.h", "contextual/contextual_suggestions_fetcher_impl.h",
"contextual/contextual_suggestions_metrics_reporter.cc", "contextual/contextual_suggestions_metrics_reporter.cc",
"contextual/contextual_suggestions_metrics_reporter.h", "contextual/contextual_suggestions_metrics_reporter.h",
"contextual/contextual_suggestions_result.cc",
"contextual/contextual_suggestions_result.h",
"contextual/contextual_suggestions_ukm_entry.cc", "contextual/contextual_suggestions_ukm_entry.cc",
"contextual/contextual_suggestions_ukm_entry.h", "contextual/contextual_suggestions_ukm_entry.h",
"features.cc", "features.cc",
...@@ -291,6 +291,8 @@ source_set("test_support") { ...@@ -291,6 +291,8 @@ source_set("test_support") {
"category_rankers/fake_category_ranker.h", "category_rankers/fake_category_ranker.h",
"category_rankers/mock_category_ranker.cc", "category_rankers/mock_category_ranker.cc",
"category_rankers/mock_category_ranker.h", "category_rankers/mock_category_ranker.h",
"contextual/contextual_suggestions_test_utils.cc",
"contextual/contextual_suggestions_test_utils.h",
"fake_content_suggestions_provider_observer.cc", "fake_content_suggestions_provider_observer.cc",
"fake_content_suggestions_provider_observer.h", "fake_content_suggestions_provider_observer.h",
"mock_content_suggestions_provider.cc", "mock_content_suggestions_provider.cc",
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "components/ntp_snippets/contextual/cluster.h" #include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
#include "components/ntp_snippets/remote/cached_image_fetcher.h" #include "components/ntp_snippets/remote/cached_image_fetcher.h"
#include "components/ntp_snippets/remote/remote_suggestions_database.h" #include "components/ntp_snippets/remote/remote_suggestions_database.h"
#include "components/ntp_snippets/remote/remote_suggestions_provider_impl.h" #include "components/ntp_snippets/remote/remote_suggestions_provider_impl.h"
...@@ -22,13 +22,17 @@ namespace ntp_snippets { ...@@ -22,13 +22,17 @@ namespace ntp_snippets {
using contextual_suggestions::Cluster; using contextual_suggestions::Cluster;
using contextual_suggestions::ContextualSuggestionsMetricsReporterProvider; using contextual_suggestions::ContextualSuggestionsMetricsReporterProvider;
using contextual_suggestions::ContextualSuggestionsResult;
using contextual_suggestions::FetchClustersCallback; using contextual_suggestions::FetchClustersCallback;
using contextual_suggestions::PeekConditions;
namespace { namespace {
bool IsEligibleURL(const GURL& url) { bool IsEligibleURL(const GURL& url) {
return url.is_valid() && url.SchemeIsHTTPOrHTTPS() && !url.HostIsIPAddress(); return url.is_valid() && url.SchemeIsHTTPOrHTTPS() && !url.HostIsIPAddress();
} }
static constexpr float kMinimumConfidence = 0.75;
} // namespace } // namespace
ContextualContentSuggestionsService::ContextualContentSuggestionsService( ContextualContentSuggestionsService::ContextualContentSuggestionsService(
...@@ -54,10 +58,14 @@ void ContextualContentSuggestionsService::FetchContextualSuggestionClusters( ...@@ -54,10 +58,14 @@ void ContextualContentSuggestionsService::FetchContextualSuggestionClusters(
ReportFetchMetricsCallback metrics_callback) { ReportFetchMetricsCallback metrics_callback) {
// TODO(pnoland): Also check that the url is safe. // TODO(pnoland): Also check that the url is safe.
if (IsEligibleURL(url)) { if (IsEligibleURL(url)) {
FetchClustersCallback internal_callback = base::BindOnce(
&ContextualContentSuggestionsService::FetchDone, base::Unretained(this),
std::move(callback), metrics_callback);
contextual_suggestions_fetcher_->FetchContextualSuggestionsClusters( contextual_suggestions_fetcher_->FetchContextualSuggestionsClusters(
url, std::move(callback), std::move(metrics_callback)); url, std::move(internal_callback), metrics_callback);
} else { } else {
std::move(callback).Run("", {}); std::move(callback).Run(
ContextualSuggestionsResult("", {}, PeekConditions()));
} }
} }
...@@ -86,6 +94,20 @@ void ContextualContentSuggestionsService::FetchContextualSuggestionImageLegacy( ...@@ -86,6 +94,20 @@ void ContextualContentSuggestionsService::FetchContextualSuggestionImageLegacy(
FetchContextualSuggestionImage(suggestion_id, image_url, std::move(callback)); FetchContextualSuggestionImage(suggestion_id, image_url, std::move(callback));
} }
void ContextualContentSuggestionsService::FetchDone(
FetchClustersCallback callback,
ReportFetchMetricsCallback metrics_callback,
ContextualSuggestionsResult result) {
if (result.peek_conditions.confidence < kMinimumConfidence) {
metrics_callback.Run(contextual_suggestions::FETCH_BELOW_THRESHOLD);
std::move(callback).Run(
ContextualSuggestionsResult("", {}, PeekConditions()));
return;
}
std::move(callback).Run(result);
}
std::unique_ptr< std::unique_ptr<
contextual_suggestions::ContextualContentSuggestionsServiceProxy> contextual_suggestions::ContextualContentSuggestionsServiceProxy>
ContextualContentSuggestionsService::CreateProxy() { ContextualContentSuggestionsService::CreateProxy() {
......
...@@ -27,6 +27,7 @@ class ContextualContentSuggestionsServiceProxy; ...@@ -27,6 +27,7 @@ class ContextualContentSuggestionsServiceProxy;
namespace ntp_snippets { namespace ntp_snippets {
using contextual_suggestions::Cluster; using contextual_suggestions::Cluster;
using contextual_suggestions::ContextualSuggestionsResult;
using contextual_suggestions::FetchClustersCallback; using contextual_suggestions::FetchClustersCallback;
using contextual_suggestions::ReportFetchMetricsCallback; using contextual_suggestions::ReportFetchMetricsCallback;
...@@ -67,6 +68,10 @@ class ContextualContentSuggestionsService : public KeyedService { ...@@ -67,6 +68,10 @@ class ContextualContentSuggestionsService : public KeyedService {
const ContentSuggestion::ID& suggestion_id, const ContentSuggestion::ID& suggestion_id,
ImageFetchedCallback callback); ImageFetchedCallback callback);
void FetchDone(FetchClustersCallback callback,
ReportFetchMetricsCallback metrics_callback,
ContextualSuggestionsResult result);
std::unique_ptr< std::unique_ptr<
contextual_suggestions::ContextualContentSuggestionsServiceProxy> contextual_suggestions::ContextualContentSuggestionsServiceProxy>
CreateProxy(); CreateProxy();
......
...@@ -126,15 +126,14 @@ void ContextualContentSuggestionsServiceProxy::FetchImageImpl( ...@@ -126,15 +126,14 @@ void ContextualContentSuggestionsServiceProxy::FetchImageImpl(
void ContextualContentSuggestionsServiceProxy::CacheSuggestions( void ContextualContentSuggestionsServiceProxy::CacheSuggestions(
ClustersCallback callback, ClustersCallback callback,
std::string peek_text, ContextualSuggestionsResult result) {
std::vector<Cluster> clusters) {
suggestions_.clear(); suggestions_.clear();
for (auto& cluster : clusters) { for (auto& cluster : result.clusters) {
for (auto& suggestion : cluster.suggestions) { for (auto& suggestion : cluster.suggestions) {
suggestions_.emplace(std::make_pair(suggestion.id, suggestion)); suggestions_.emplace(std::make_pair(suggestion.id, suggestion));
} }
} }
std::move(callback).Run(peek_text, std::move(clusters)); std::move(callback).Run(std::move(result));
} }
} // namespace contextual_suggestions } // namespace contextual_suggestions
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "components/ntp_snippets/contextual/cluster.h"
#include "components/ntp_snippets/contextual/contextual_content_suggestions_service.h" #include "components/ntp_snippets/contextual/contextual_content_suggestions_service.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
class GURL; class GURL;
...@@ -62,8 +62,7 @@ class ContextualContentSuggestionsServiceProxy { ...@@ -62,8 +62,7 @@ class ContextualContentSuggestionsServiceProxy {
ntp_snippets::ImageFetchedCallback callback); ntp_snippets::ImageFetchedCallback callback);
void CacheSuggestions(ClustersCallback callback, void CacheSuggestions(ClustersCallback callback,
std::string peek_text, ContextualSuggestionsResult result);
std::vector<Cluster> clusters);
// Pointer to the service. // Pointer to the service.
ntp_snippets::ContextualContentSuggestionsService* service_; ntp_snippets::ContextualContentSuggestionsService* service_;
// Cache of contextual suggestions. // Cache of contextual suggestions.
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "components/ntp_snippets/contextual/contextual_content_suggestions_service.h" #include "components/ntp_snippets/contextual/contextual_content_suggestions_service.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h" #include "components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_test_utils.h"
#include "components/ntp_snippets/remote/cached_image_fetcher.h" #include "components/ntp_snippets/remote/cached_image_fetcher.h"
#include "components/ntp_snippets/remote/remote_suggestions_database.h" #include "components/ntp_snippets/remote/remote_suggestions_database.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
...@@ -41,16 +42,16 @@ class FakeContextualContentSuggestionsService ...@@ -41,16 +42,16 @@ class FakeContextualContentSuggestionsService
clusters_callback_ = std::move(callback); clusters_callback_ = std::move(callback);
} }
void RunClustersCallback(std::string peek_text, void RunClustersCallback(ContextualSuggestionsResult result) {
std::vector<Cluster> clusters) { std::move(clusters_callback_).Run(std::move(result));
std::move(clusters_callback_)
.Run(std::move(peek_text), std::move(clusters));
} }
private: private:
ClustersCallback clusters_callback_; ClustersCallback clusters_callback_;
}; };
} // namespace
FakeContextualContentSuggestionsService:: FakeContextualContentSuggestionsService::
FakeContextualContentSuggestionsService() FakeContextualContentSuggestionsService()
: ContextualContentSuggestionsService(nullptr, nullptr, nullptr, nullptr) {} : ContextualContentSuggestionsService(nullptr, nullptr, nullptr, nullptr) {}
...@@ -58,27 +59,6 @@ FakeContextualContentSuggestionsService:: ...@@ -58,27 +59,6 @@ FakeContextualContentSuggestionsService::
FakeContextualContentSuggestionsService:: FakeContextualContentSuggestionsService::
~FakeContextualContentSuggestionsService() {} ~FakeContextualContentSuggestionsService() {}
// GMock does not support movable-only types (Cluster).
// Instead WrappedRun is used as callback and it redirects the call to a
// method without movable-only types, which is then mocked.
class MockClustersCallback {
public:
void WrappedRun(std::string peek_text, std::vector<Cluster> clusters) {
Run(peek_text, &clusters);
}
ClustersCallback ToOnceCallback() {
return base::BindOnce(&MockClustersCallback::WrappedRun,
base::Unretained(this));
}
MOCK_METHOD2(Run,
void(const std::string& peek_text,
std::vector<Cluster>* clusters));
};
} // namespace
class ContextualContentSuggestionsServiceProxyTest : public testing::Test { class ContextualContentSuggestionsServiceProxyTest : public testing::Test {
public: public:
void SetUp() override; void SetUp() override;
...@@ -106,8 +86,8 @@ TEST_F(ContextualContentSuggestionsServiceProxyTest, ...@@ -106,8 +86,8 @@ TEST_F(ContextualContentSuggestionsServiceProxyTest,
proxy()->FetchContextualSuggestions(GURL(kValidFromUrl), proxy()->FetchContextualSuggestions(GURL(kValidFromUrl),
mock_cluster_callback.ToOnceCallback()); mock_cluster_callback.ToOnceCallback());
EXPECT_CALL(mock_cluster_callback, Run(kTestPeekText, Pointee(IsEmpty()))); service()->RunClustersCallback(ContextualSuggestionsResult());
service()->RunClustersCallback(kTestPeekText, std::vector<Cluster>()); EXPECT_TRUE(mock_cluster_callback.has_run);
} }
// TODO(fgorski): More tests will be added, once we have the suggestions // TODO(fgorski): More tests will be added, once we have the suggestions
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "components/ntp_snippets/contextual/contextual_suggestion.h" #include "components/ntp_snippets/contextual/contextual_suggestion.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_fetcher.h" #include "components/ntp_snippets/contextual/contextual_suggestions_fetcher.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h" #include "components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_test_utils.h"
#include "components/ntp_snippets/remote/cached_image_fetcher.h" #include "components/ntp_snippets/remote/cached_image_fetcher.h"
#include "components/ntp_snippets/remote/json_to_categories.h" #include "components/ntp_snippets/remote/json_to_categories.h"
#include "components/ntp_snippets/remote/remote_suggestions_database.h" #include "components/ntp_snippets/remote/remote_suggestions_database.h"
...@@ -31,6 +32,9 @@ ...@@ -31,6 +32,9 @@
#include "ui/gfx/image/image_unittest_util.h" #include "ui/gfx/image/image_unittest_util.h"
using contextual_suggestions::ClusterBuilder; using contextual_suggestions::ClusterBuilder;
using contextual_suggestions::ContextualSuggestionsResult;
using contextual_suggestions::MockClustersCallback;
using contextual_suggestions::PeekConditions;
using contextual_suggestions::ReportFetchMetricsCallback; using contextual_suggestions::ReportFetchMetricsCallback;
using testing::_; using testing::_;
using testing::AllOf; using testing::AllOf;
...@@ -44,20 +48,6 @@ namespace ntp_snippets { ...@@ -44,20 +48,6 @@ namespace ntp_snippets {
namespace { namespace {
class MockClustersCallback {
public:
void Done(std::string peek_text, std::vector<Cluster> clusters) {
EXPECT_FALSE(has_run);
has_run = true;
response_peek_text = peek_text;
response_clusters = std::move(clusters);
}
bool has_run = false;
std::string response_peek_text;
std::vector<Cluster> response_clusters;
};
// Always fetches the result that was set by SetFakeResponse. // Always fetches the result that was set by SetFakeResponse.
class FakeContextualSuggestionsFetcher : public ContextualSuggestionsFetcher { class FakeContextualSuggestionsFetcher : public ContextualSuggestionsFetcher {
public: public:
...@@ -65,16 +55,23 @@ class FakeContextualSuggestionsFetcher : public ContextualSuggestionsFetcher { ...@@ -65,16 +55,23 @@ class FakeContextualSuggestionsFetcher : public ContextualSuggestionsFetcher {
const GURL& url, const GURL& url,
FetchClustersCallback callback, FetchClustersCallback callback,
ReportFetchMetricsCallback metrics_callback) override { ReportFetchMetricsCallback metrics_callback) override {
std::move(callback).Run("peek text", std::move(fake_suggestions_)); ContextualSuggestionsResult result;
result.peek_text = "peek text";
result.clusters = std::move(fake_suggestions_);
result.peek_conditions = peek_conditions_;
std::move(callback).Run(std::move(result));
fake_suggestions_.clear(); fake_suggestions_.clear();
} }
void SetFakeResponse(std::vector<Cluster> fake_suggestions) { void SetFakeResponse(std::vector<Cluster> fake_suggestions,
PeekConditions peek_conditions = PeekConditions()) {
fake_suggestions_ = std::move(fake_suggestions); fake_suggestions_ = std::move(fake_suggestions);
peek_conditions_ = peek_conditions;
} }
private: private:
std::vector<Cluster> fake_suggestions_; std::vector<Cluster> fake_suggestions_;
PeekConditions peek_conditions_;
}; };
// Always fetches a fake image if the given URL is valid. // Always fetches a fake image if the given URL is valid.
...@@ -160,10 +157,7 @@ TEST_F(ContextualContentSuggestionsServiceTest, ...@@ -160,10 +157,7 @@ TEST_F(ContextualContentSuggestionsServiceTest,
fetcher()->SetFakeResponse(std::move(clusters)); fetcher()->SetFakeResponse(std::move(clusters));
source()->FetchContextualSuggestionClusters( source()->FetchContextualSuggestionClusters(
context_url, context_url, mock_callback.ToOnceCallback(), base::DoNothing());
base::BindOnce(&MockClustersCallback::Done,
base::Unretained(&mock_callback)),
base::DoNothing());
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_TRUE(mock_callback.has_run); EXPECT_TRUE(mock_callback.has_run);
...@@ -187,4 +181,33 @@ TEST_F(ContextualContentSuggestionsServiceTest, ShouldRejectInvalidUrls) { ...@@ -187,4 +181,33 @@ TEST_F(ContextualContentSuggestionsServiceTest, ShouldRejectInvalidUrls) {
} }
} }
TEST_F(ContextualContentSuggestionsServiceTest,
ShouldNotReportLowConfidenceResults) {
MockClustersCallback mock_callback;
std::vector<Cluster> clusters;
GURL context_url("http://www.from.url");
clusters.emplace_back(ClusterBuilder("Title")
.AddSuggestion(SuggestionBuilder(context_url)
.Title("Title1")
.PublisherName("from.url")
.Snippet("Summary")
.ImageId("abc")
.Build())
.Build());
PeekConditions peek_conditions;
peek_conditions.confidence = 0.5;
fetcher()->SetFakeResponse(std::move(clusters), peek_conditions);
source()->FetchContextualSuggestionClusters(
context_url, mock_callback.ToOnceCallback(), base::DoNothing());
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(mock_callback.has_run);
EXPECT_EQ(mock_callback.response_clusters.size(), 0u);
EXPECT_EQ(mock_callback.response_peek_text, std::string());
}
} // namespace ntp_snippets } // namespace ntp_snippets
...@@ -65,6 +65,22 @@ Cluster PivotToCluster(const PivotCluster& pivot) { ...@@ -65,6 +65,22 @@ Cluster PivotToCluster(const PivotCluster& pivot) {
return cluster_builder.Build(); return cluster_builder.Build();
} }
PeekConditions PeekConditionsFromResponse(
const GetPivotsResponse& response_proto) {
AutoPeekConditions proto_conditions =
response_proto.pivots().auto_peek_conditions();
PeekConditions peek_conditions;
peek_conditions.confidence = proto_conditions.confidence();
peek_conditions.page_scroll_percentage =
proto_conditions.page_scroll_percentage();
peek_conditions.minimum_seconds_on_page =
proto_conditions.minimum_seconds_on_page();
peek_conditions.maximum_number_of_peeks =
proto_conditions.maximum_number_of_peeks();
return peek_conditions;
}
std::vector<Cluster> ClustersFromResponse( std::vector<Cluster> ClustersFromResponse(
const GetPivotsResponse& response_proto) { const GetPivotsResponse& response_proto) {
std::vector<Cluster> clusters; std::vector<Cluster> clusters;
...@@ -123,6 +139,14 @@ const std::string SerializedPivotsRequest(const std::string& url, ...@@ -123,6 +139,14 @@ const std::string SerializedPivotsRequest(const std::string& url,
return pivot_request.SerializeAsString(); return pivot_request.SerializeAsString();
} }
ContextualSuggestionsResult ResultFromResponse(
const GetPivotsResponse& response_proto) {
return ContextualSuggestionsResult(
PeekTextFromResponse(response_proto),
ClustersFromResponse(response_proto),
PeekConditionsFromResponse(response_proto));
}
} // namespace } // namespace
ContextualSuggestionsFetch::ContextualSuggestionsFetch( ContextualSuggestionsFetch::ContextualSuggestionsFetch(
...@@ -217,8 +241,7 @@ net::HttpRequestHeaders ContextualSuggestionsFetch::MakeHeaders() const { ...@@ -217,8 +241,7 @@ net::HttpRequestHeaders ContextualSuggestionsFetch::MakeHeaders() const {
void ContextualSuggestionsFetch::OnURLLoaderComplete( void ContextualSuggestionsFetch::OnURLLoaderComplete(
ReportFetchMetricsCallback metrics_callback, ReportFetchMetricsCallback metrics_callback,
std::unique_ptr<std::string> result) { std::unique_ptr<std::string> result) {
std::vector<Cluster> clusters; ContextualSuggestionsResult suggestions_result;
std::string peek_text;
int32_t response_code = 0; int32_t response_code = 0;
int32_t error_code = url_loader_->NetError(); int32_t error_code = url_loader_->NetError();
...@@ -236,8 +259,7 @@ void ContextualSuggestionsFetch::OnURLLoaderComplete( ...@@ -236,8 +259,7 @@ void ContextualSuggestionsFetch::OnURLLoaderComplete(
if (coded_stream.ReadVarint32(&response_size) && response_size != 0) { if (coded_stream.ReadVarint32(&response_size) && response_size != 0) {
GetPivotsResponse response_proto; GetPivotsResponse response_proto;
if (response_proto.MergePartialFromCodedStream(&coded_stream)) { if (response_proto.MergePartialFromCodedStream(&coded_stream)) {
clusters = ClustersFromResponse(response_proto); suggestions_result = ResultFromResponse(response_proto);
peek_text = PeekTextFromResponse(response_proto);
} }
} }
} }
...@@ -246,10 +268,10 @@ void ContextualSuggestionsFetch::OnURLLoaderComplete( ...@@ -246,10 +268,10 @@ void ContextualSuggestionsFetch::OnURLLoaderComplete(
static_cast<int>(result->length() / 1024)); static_cast<int>(result->length() / 1024));
} }
ReportFetchMetrics(error_code, response_code, clusters.size(), ReportFetchMetrics(error_code, response_code,
suggestions_result.clusters.size(),
std::move(metrics_callback)); std::move(metrics_callback));
std::move(request_completed_callback_).Run(std::move(suggestions_result));
std::move(request_completed_callback_).Run(peek_text, std::move(clusters));
} }
void ContextualSuggestionsFetch::ReportFetchMetrics( void ContextualSuggestionsFetch::ReportFetchMetrics(
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
#include <utility> #include <utility>
#include "base/callback.h" #include "base/callback.h"
#include "components/ntp_snippets/contextual/cluster.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h" #include "components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
#include "net/http/http_request_headers.h" #include "net/http/http_request_headers.h"
#include "url/gurl.h" #include "url/gurl.h"
......
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
#ifndef COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_FETCHER_H_ #ifndef COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_FETCHER_H_
#define COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_FETCHER_H_ #define COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_FETCHER_H_
#include "components/ntp_snippets/contextual/cluster.h"
#include "components/ntp_snippets/contextual/contextual_suggestion.h" #include "components/ntp_snippets/contextual/contextual_suggestion.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h" #include "components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
using contextual_suggestions::FetchClustersCallback; using contextual_suggestions::FetchClustersCallback;
using contextual_suggestions::ReportFetchMetricsCallback; using contextual_suggestions::ReportFetchMetricsCallback;
......
...@@ -35,13 +35,12 @@ void ContextualSuggestionsFetcherImpl::FetchContextualSuggestionsClusters( ...@@ -35,13 +35,12 @@ void ContextualSuggestionsFetcherImpl::FetchContextualSuggestionsClusters(
void ContextualSuggestionsFetcherImpl::FetchFinished( void ContextualSuggestionsFetcherImpl::FetchFinished(
ContextualSuggestionsFetch* fetch, ContextualSuggestionsFetch* fetch,
FetchClustersCallback callback, FetchClustersCallback callback,
std::string peek_text, ContextualSuggestionsResult result) {
std::vector<Cluster> clusters) {
auto fetch_iterator = pending_requests_.find(fetch); auto fetch_iterator = pending_requests_.find(fetch);
CHECK(fetch_iterator != pending_requests_.end()); CHECK(fetch_iterator != pending_requests_.end());
pending_requests_.erase(fetch_iterator); pending_requests_.erase(fetch_iterator);
std::move(callback).Run(peek_text, std::move(clusters)); std::move(callback).Run(std::move(result));
} }
} // namespace ntp_snippets } // namespace ntp_snippets
...@@ -22,6 +22,7 @@ class SharedURLLoaderFactory; ...@@ -22,6 +22,7 @@ class SharedURLLoaderFactory;
using contextual_suggestions::Cluster; using contextual_suggestions::Cluster;
using contextual_suggestions::ContextualSuggestionsFetch; using contextual_suggestions::ContextualSuggestionsFetch;
using contextual_suggestions::ContextualSuggestionsResult;
namespace ntp_snippets { namespace ntp_snippets {
...@@ -41,8 +42,7 @@ class ContextualSuggestionsFetcherImpl : public ContextualSuggestionsFetcher { ...@@ -41,8 +42,7 @@ class ContextualSuggestionsFetcherImpl : public ContextualSuggestionsFetcher {
private: private:
void FetchFinished(ContextualSuggestionsFetch* fetch, void FetchFinished(ContextualSuggestionsFetch* fetch,
FetchClustersCallback callback, FetchClustersCallback callback,
std::string peek_text, ContextualSuggestionsResult result);
std::vector<Cluster> clusters);
const scoped_refptr<network::SharedURLLoaderFactory> loader_factory_; const scoped_refptr<network::SharedURLLoaderFactory> loader_factory_;
/// BCP47 formatted language code to use. /// BCP47 formatted language code to use.
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/histogram_tester.h" #include "base/test/histogram_tester.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_test_utils.h"
#include "components/ntp_snippets/contextual/proto/chrome_search_api_request_context.pb.h" #include "components/ntp_snippets/contextual/proto/chrome_search_api_request_context.pb.h"
#include "components/ntp_snippets/contextual/proto/get_pivots_request.pb.h" #include "components/ntp_snippets/contextual/proto/get_pivots_request.pb.h"
#include "components/ntp_snippets/contextual/proto/get_pivots_response.pb.h" #include "components/ntp_snippets/contextual/proto/get_pivots_response.pb.h"
...@@ -26,6 +28,7 @@ ...@@ -26,6 +28,7 @@
namespace ntp_snippets { namespace ntp_snippets {
using contextual_suggestions::AutoPeekConditions;
using contextual_suggestions::ClusterBuilder; using contextual_suggestions::ClusterBuilder;
using contextual_suggestions::ContextualSuggestionsEvent; using contextual_suggestions::ContextualSuggestionsEvent;
using contextual_suggestions::ExploreContext; using contextual_suggestions::ExploreContext;
...@@ -33,6 +36,9 @@ using contextual_suggestions::GetPivotsQuery; ...@@ -33,6 +36,9 @@ using contextual_suggestions::GetPivotsQuery;
using contextual_suggestions::GetPivotsRequest; using contextual_suggestions::GetPivotsRequest;
using contextual_suggestions::GetPivotsResponse; using contextual_suggestions::GetPivotsResponse;
using contextual_suggestions::ImageId; using contextual_suggestions::ImageId;
using contextual_suggestions::MockClustersCallback;
using contextual_suggestions::MockMetricsCallback;
using contextual_suggestions::PeekConditions;
using contextual_suggestions::PivotCluster; using contextual_suggestions::PivotCluster;
using contextual_suggestions::PivotClusteringParams; using contextual_suggestions::PivotClusteringParams;
using contextual_suggestions::PivotDocument; using contextual_suggestions::PivotDocument;
...@@ -45,27 +51,6 @@ using testing::ElementsAre; ...@@ -45,27 +51,6 @@ using testing::ElementsAre;
namespace { namespace {
class MockClustersCallback {
public:
void Done(std::string peek_text, std::vector<Cluster> clusters) {
EXPECT_FALSE(has_run);
has_run = true;
response_peek_text = peek_text;
response_clusters = std::move(clusters);
}
bool has_run = false;
std::string response_peek_text;
std::vector<Cluster> response_clusters;
};
class MockMetricsCallback {
public:
void Report(ContextualSuggestionsEvent event) { events.push_back(event); }
std::vector<ContextualSuggestionsEvent> events;
};
// TODO(pnoland): de-dupe this and the identical class in // TODO(pnoland): de-dupe this and the identical class in
// feed_networking_host_unittest.cc // feed_networking_host_unittest.cc
class TestSharedURLLoaderFactory : public SharedURLLoaderFactory { class TestSharedURLLoaderFactory : public SharedURLLoaderFactory {
...@@ -131,12 +116,27 @@ void PopulateDocument(PivotDocument* document, ...@@ -131,12 +116,27 @@ void PopulateDocument(PivotDocument* document,
image_id->set_encrypted_docid(suggestion.image_id); image_id->set_encrypted_docid(suggestion.image_id);
} }
void PopulatePeekConditions(AutoPeekConditions* proto_conditions,
const PeekConditions& peek_conditions) {
proto_conditions->set_confidence(peek_conditions.confidence);
proto_conditions->set_page_scroll_percentage(
peek_conditions.page_scroll_percentage);
proto_conditions->set_minimum_seconds_on_page(
peek_conditions.minimum_seconds_on_page);
proto_conditions->set_maximum_number_of_peeks(
peek_conditions.maximum_number_of_peeks);
}
// Populates a GetPivotsResponse proto using |peek_text| and |clusters| and // Populates a GetPivotsResponse proto using |peek_text| and |clusters| and
// returns that proto as a serialized string. // returns that proto as a serialized string.
std::string SerializedResponseProto(const std::string& peek_text, std::string SerializedResponseProto(
std::vector<Cluster> clusters) { const std::string& peek_text,
std::vector<Cluster> clusters,
PeekConditions peek_conditions = PeekConditions()) {
GetPivotsResponse response_proto; GetPivotsResponse response_proto;
Pivots* pivots = response_proto.mutable_pivots(); Pivots* pivots = response_proto.mutable_pivots();
PopulatePeekConditions(pivots->mutable_auto_peek_conditions(),
peek_conditions);
pivots->mutable_peek_text()->set_text(peek_text); pivots->mutable_peek_text()->set_text(peek_text);
for (const auto& cluster : clusters) { for (const auto& cluster : clusters) {
...@@ -173,36 +173,6 @@ std::string SerializedResponseProto(const std::string& peek_text, ...@@ -173,36 +173,6 @@ std::string SerializedResponseProto(const std::string& peek_text,
return " " + response_proto.SerializeAsString(); return " " + response_proto.SerializeAsString();
} }
void ExpectSuggestionsMatch(ContextualSuggestion actual,
ContextualSuggestion expected) {
EXPECT_EQ(actual.id, expected.id);
EXPECT_EQ(actual.title, expected.title);
EXPECT_EQ(actual.url, expected.url);
EXPECT_EQ(actual.snippet, expected.snippet);
EXPECT_EQ(actual.publisher_name, expected.publisher_name);
EXPECT_EQ(actual.image_id, expected.image_id);
EXPECT_EQ(actual.favicon_image_id, expected.favicon_image_id);
}
void ExpectClustersMatch(Cluster actual, Cluster expected) {
EXPECT_EQ(actual.title, expected.title);
for (size_t i = 0; i < actual.suggestions.size(); i++) {
ExpectSuggestionsMatch(std::move(actual.suggestions[i]),
std::move(expected.suggestions[i]));
}
}
void ExpectResponsesMatch(MockClustersCallback callback,
std::string expected_peek_text,
std::vector<Cluster> expected_clusters) {
EXPECT_EQ(callback.response_peek_text, expected_peek_text);
ASSERT_EQ(callback.response_clusters.size(), expected_clusters.size());
for (size_t i = 0; i < callback.response_clusters.size(); i++) {
ExpectClustersMatch(std::move(callback.response_clusters[i]),
std::move(expected_clusters[i]));
}
}
} // namespace } // namespace
class ContextualSuggestionsFetcherTest : public testing::Test { class ContextualSuggestionsFetcherTest : public testing::Test {
...@@ -241,9 +211,7 @@ class ContextualSuggestionsFetcherTest : public testing::Test { ...@@ -241,9 +211,7 @@ class ContextualSuggestionsFetcherTest : public testing::Test {
base::Unretained(mock_metrics_callback)) base::Unretained(mock_metrics_callback))
: base::DoNothing(); : base::DoNothing();
fetcher().FetchContextualSuggestionsClusters( fetcher().FetchContextualSuggestionsClusters(
context_url, context_url, callback->ToOnceCallback(), metrics_callback);
base::BindOnce(&MockClustersCallback::Done, base::Unretained(callback)),
metrics_callback);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
...@@ -270,7 +238,9 @@ TEST_F(ContextualSuggestionsFetcherTest, SingleSuggestionResponse) { ...@@ -270,7 +238,9 @@ TEST_F(ContextualSuggestionsFetcherTest, SingleSuggestionResponse) {
&metrics_callback); &metrics_callback);
EXPECT_TRUE(callback.has_run); EXPECT_TRUE(callback.has_run);
ExpectResponsesMatch(std::move(callback), "Peek Text", DefaultClusters()); ExpectResponsesMatch(std::move(callback),
ContextualSuggestionsResult(
"Peek Text", DefaultClusters(), PeekConditions()));
EXPECT_EQ(metrics_callback.events, EXPECT_EQ(metrics_callback.events,
std::vector<ContextualSuggestionsEvent>( std::vector<ContextualSuggestionsEvent>(
{contextual_suggestions::FETCH_COMPLETED})); {contextual_suggestions::FETCH_COMPLETED}));
...@@ -331,8 +301,10 @@ TEST_F(ContextualSuggestionsFetcherTest, ...@@ -331,8 +301,10 @@ TEST_F(ContextualSuggestionsFetcherTest,
SendAndAwaitResponse(GURL("http://www.article.com/"), &callback); SendAndAwaitResponse(GURL("http://www.article.com/"), &callback);
EXPECT_TRUE(callback.has_run); EXPECT_TRUE(callback.has_run);
ExpectResponsesMatch(std::move(callback), "Peek Text", ExpectResponsesMatch(
std::move(clusters_copy)); std::move(callback),
ContextualSuggestionsResult("Peek Text", std::move(clusters_copy),
PeekConditions()));
histogram_tester.ExpectTotalCount("ContextualSuggestions.FetchResponseSizeKB", histogram_tester.ExpectTotalCount("ContextualSuggestions.FetchResponseSizeKB",
1); 1);
...@@ -348,8 +320,31 @@ TEST_F(ContextualSuggestionsFetcherTest, FlatResponse) { ...@@ -348,8 +320,31 @@ TEST_F(ContextualSuggestionsFetcherTest, FlatResponse) {
// PivotCluster to copy it from. So we clear the expected title. // PivotCluster to copy it from. So we clear the expected title.
std::vector<Cluster> expected_clusters = DefaultClusters(); std::vector<Cluster> expected_clusters = DefaultClusters();
expected_clusters[0].title = ""; expected_clusters[0].title = "";
ExpectResponsesMatch(std::move(callback), "Peek Text", ExpectResponsesMatch(
std::move(expected_clusters)); std::move(callback),
ContextualSuggestionsResult("Peek Text", std::move(expected_clusters),
PeekConditions()));
}
TEST_F(ContextualSuggestionsFetcherTest, PeekConditionsAreParsed) {
MockClustersCallback callback;
MockMetricsCallback metrics_callback;
PeekConditions peek_conditions;
peek_conditions.confidence = 0.7;
peek_conditions.page_scroll_percentage = 35.0;
peek_conditions.minimum_seconds_on_page = 4.5;
peek_conditions.maximum_number_of_peeks = 5.0;
SetFakeResponse(
SerializedResponseProto("Peek Text", DefaultClusters(), peek_conditions));
SendAndAwaitResponse(GURL("http://www.article.com"), &callback,
&metrics_callback);
EXPECT_TRUE(callback.has_run);
ExpectResponsesMatch(std::move(callback),
ContextualSuggestionsResult(
"Peek Text", DefaultClusters(), peek_conditions));
} }
TEST_F(ContextualSuggestionsFetcherTest, HtmlEntitiesAreUnescaped) { TEST_F(ContextualSuggestionsFetcherTest, HtmlEntitiesAreUnescaped) {
...@@ -375,8 +370,10 @@ TEST_F(ContextualSuggestionsFetcherTest, HtmlEntitiesAreUnescaped) { ...@@ -375,8 +370,10 @@ TEST_F(ContextualSuggestionsFetcherTest, HtmlEntitiesAreUnescaped) {
// entities we added. // entities we added.
expected_clusters[0].suggestions[0].title = "\"foobar\""; expected_clusters[0].suggestions[0].title = "\"foobar\"";
expected_clusters[0].suggestions[0].snippet = "\'barbaz\'"; expected_clusters[0].suggestions[0].snippet = "\'barbaz\'";
ExpectResponsesMatch(std::move(callback), "Peek Text", ExpectResponsesMatch(
std::move(expected_clusters)); std::move(callback),
ContextualSuggestionsResult("Peek Text", std::move(expected_clusters),
PeekConditions()));
} }
TEST_F(ContextualSuggestionsFetcherTest, RequestHeaderSetCorrectly) { TEST_F(ContextualSuggestionsFetcherTest, RequestHeaderSetCorrectly) {
...@@ -513,7 +510,9 @@ TEST_F(ContextualSuggestionsFetcherTest, ResponseWithUnsetFields) { ...@@ -513,7 +510,9 @@ TEST_F(ContextualSuggestionsFetcherTest, ResponseWithUnsetFields) {
EXPECT_TRUE(callback.has_run); EXPECT_TRUE(callback.has_run);
EXPECT_EQ(callback.response_clusters.size(), 1u); EXPECT_EQ(callback.response_clusters.size(), 1u);
ExpectResponsesMatch(std::move(callback), "", std::move(expected_clusters)); ExpectResponsesMatch(std::move(callback),
ContextualSuggestionsResult(
"", std::move(expected_clusters), PeekConditions()));
} }
TEST_F(ContextualSuggestionsFetcherTest, CorruptResponse) { TEST_F(ContextualSuggestionsFetcherTest, CorruptResponse) {
......
...@@ -2,12 +2,18 @@ ...@@ -2,12 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "components/ntp_snippets/contextual/cluster.h" #include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
#include "components/ntp_snippets/contextual/contextual_suggestion.h" #include "components/ntp_snippets/contextual/contextual_suggestion.h"
namespace contextual_suggestions { namespace contextual_suggestions {
PeekConditions::PeekConditions()
: confidence(1.0),
page_scroll_percentage(0.0),
minimum_seconds_on_page(0.0),
maximum_number_of_peeks(0.0) {}
Cluster::Cluster() = default; Cluster::Cluster() = default;
// MSVC doesn't support defaulted move constructors, so we have to define it // MSVC doesn't support defaulted move constructors, so we have to define it
...@@ -16,6 +22,8 @@ Cluster::Cluster(Cluster&& other) noexcept ...@@ -16,6 +22,8 @@ Cluster::Cluster(Cluster&& other) noexcept
: title(std::move(other.title)), : title(std::move(other.title)),
suggestions(std::move(other.suggestions)) {} suggestions(std::move(other.suggestions)) {}
Cluster::Cluster(const Cluster&) = default;
Cluster::~Cluster() = default; Cluster::~Cluster() = default;
ClusterBuilder::ClusterBuilder(const std::string& title) { ClusterBuilder::ClusterBuilder(const std::string& title) {
...@@ -30,6 +38,7 @@ ClusterBuilder::ClusterBuilder(const ClusterBuilder& other) { ...@@ -30,6 +38,7 @@ ClusterBuilder::ClusterBuilder(const ClusterBuilder& other) {
.PublisherName(suggestion.publisher_name) .PublisherName(suggestion.publisher_name)
.Snippet(suggestion.snippet) .Snippet(suggestion.snippet)
.ImageId(suggestion.image_id) .ImageId(suggestion.image_id)
.FaviconImageId(suggestion.favicon_image_id)
.Build()); .Build());
} }
} }
...@@ -43,4 +52,30 @@ Cluster ClusterBuilder::Build() { ...@@ -43,4 +52,30 @@ Cluster ClusterBuilder::Build() {
return std::move(cluster_); return std::move(cluster_);
} }
ContextualSuggestionsResult::ContextualSuggestionsResult() = default;
ContextualSuggestionsResult::ContextualSuggestionsResult(
std::string peek_text,
std::vector<Cluster> clusters,
PeekConditions peek_conditions)
: clusters(clusters),
peek_text(peek_text),
peek_conditions(peek_conditions) {}
ContextualSuggestionsResult::ContextualSuggestionsResult(
const ContextualSuggestionsResult& other) = default;
// MSVC doesn't support defaulted move constructors, so we have to define it
// ourselves.
ContextualSuggestionsResult::ContextualSuggestionsResult(
ContextualSuggestionsResult&& other) noexcept
: clusters(std::move(other.clusters)),
peek_text(std::move(other.peek_text)),
peek_conditions(std::move(other.peek_conditions)) {}
ContextualSuggestionsResult::~ContextualSuggestionsResult() = default;
ContextualSuggestionsResult& ContextualSuggestionsResult::operator=(
ContextualSuggestionsResult&& other) = default;
} // namespace contextual_suggestions } // namespace contextual_suggestions
\ No newline at end of file
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CLUSTER_H_ #ifndef COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_RESULT_H_
#define COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CLUSTER_H_ #define COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_RESULT_H_
#include "components/ntp_snippets/contextual/contextual_suggestion.h" #include "components/ntp_snippets/contextual/contextual_suggestion.h"
...@@ -12,21 +12,38 @@ using ntp_snippets::SuggestionBuilder; ...@@ -12,21 +12,38 @@ using ntp_snippets::SuggestionBuilder;
namespace contextual_suggestions { namespace contextual_suggestions {
// Encapsulates conditions under which to show or "peek" the contextual
// suggestions UI.
struct PeekConditions {
PeekConditions();
// A measure of confidence that auto-peek should be enabled for this response
// in the range [0, 1].
float confidence = 1.0;
// The percentage of the page that the user scrolls required for an auto
// peek to occur.
float page_scroll_percentage;
// The minimum time (seconds) the user spends on the page required for
// auto peek.
float minimum_seconds_on_page;
// The maximum number of auto peeks that we can show for this page.
uint64_t maximum_number_of_peeks;
};
// A structure representing a suggestion cluster. // A structure representing a suggestion cluster.
struct Cluster { struct Cluster {
Cluster(); Cluster();
Cluster(const Cluster&);
Cluster(Cluster&&) noexcept; Cluster(Cluster&&) noexcept;
~Cluster(); ~Cluster();
std::string title; std::string title;
std::vector<ContextualSuggestion> suggestions; std::vector<ContextualSuggestion> suggestions;
private:
DISALLOW_COPY_AND_ASSIGN(Cluster);
}; };
// Allows concise construction of a cluster and copying, which cluster // Allows concise construction of a cluster.
// doesn't allow.
class ClusterBuilder { class ClusterBuilder {
public: public:
ClusterBuilder(const std::string& title); ClusterBuilder(const std::string& title);
...@@ -40,10 +57,27 @@ class ClusterBuilder { ...@@ -40,10 +57,27 @@ class ClusterBuilder {
Cluster cluster_; Cluster cluster_;
}; };
// Struct that holds the data from a ContextualSuggestions response that we care
// about for UI purposes.
struct ContextualSuggestionsResult {
ContextualSuggestionsResult();
ContextualSuggestionsResult(std::string peek_text,
std::vector<Cluster> clusters,
PeekConditions peek_conditions);
ContextualSuggestionsResult(const ContextualSuggestionsResult&);
ContextualSuggestionsResult(ContextualSuggestionsResult&&) noexcept;
~ContextualSuggestionsResult();
ContextualSuggestionsResult& operator=(ContextualSuggestionsResult&&);
std::vector<Cluster> clusters;
std::string peek_text;
PeekConditions peek_conditions;
};
using FetchClustersCallback = using FetchClustersCallback =
base::OnceCallback<void(std::string peek_text, base::OnceCallback<void(ContextualSuggestionsResult result)>;
std::vector<Cluster> clusters)>;
} // namespace contextual_suggestions } // namespace contextual_suggestions
#endif // COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CLUSTER_H_ #endif // COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_RESULT_H_
\ No newline at end of file \ No newline at end of file
// 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 "components/ntp_snippets/contextual/contextual_suggestions_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace contextual_suggestions {
MockClustersCallback::MockClustersCallback() = default;
MockClustersCallback::MockClustersCallback(const MockClustersCallback& other) =
default;
MockClustersCallback::~MockClustersCallback() = default;
void MockClustersCallback::Done(ContextualSuggestionsResult result) {
EXPECT_FALSE(has_run);
has_run = true;
response_peek_text = result.peek_text;
response_clusters = std::move(result.clusters);
peek_conditions = result.peek_conditions;
}
FetchClustersCallback MockClustersCallback::ToOnceCallback() {
return base::BindOnce(&MockClustersCallback::Done, base::Unretained(this));
}
MockMetricsCallback::MockMetricsCallback() = default;
MockMetricsCallback::~MockMetricsCallback() = default;
void MockMetricsCallback::Report(ContextualSuggestionsEvent event) {
events.push_back(event);
}
void ExpectSuggestionsMatch(const ContextualSuggestion& actual,
const ContextualSuggestion& expected) {
EXPECT_EQ(actual.id, expected.id);
EXPECT_EQ(actual.title, expected.title);
EXPECT_EQ(actual.url, expected.url);
EXPECT_EQ(actual.snippet, expected.snippet);
EXPECT_EQ(actual.publisher_name, expected.publisher_name);
EXPECT_EQ(actual.image_id, expected.image_id);
EXPECT_EQ(actual.favicon_image_id, expected.favicon_image_id);
}
void ExpectClustersMatch(const Cluster& actual, const Cluster& expected) {
EXPECT_EQ(actual.title, expected.title);
ASSERT_EQ(actual.suggestions.size(), expected.suggestions.size());
for (size_t i = 0; i < actual.suggestions.size(); i++) {
ExpectSuggestionsMatch(std::move(actual.suggestions[i]),
std::move(expected.suggestions[i]));
}
}
void ExpectPeekConditionsMatch(const PeekConditions& actual,
const PeekConditions& expected) {
EXPECT_EQ(actual.confidence, expected.confidence);
EXPECT_EQ(actual.page_scroll_percentage, expected.page_scroll_percentage);
EXPECT_EQ(actual.minimum_seconds_on_page, expected.minimum_seconds_on_page);
EXPECT_EQ(actual.maximum_number_of_peeks, expected.maximum_number_of_peeks);
}
void ExpectResponsesMatch(const MockClustersCallback& callback,
const ContextualSuggestionsResult& expected_result) {
EXPECT_EQ(callback.response_peek_text, expected_result.peek_text);
ExpectPeekConditionsMatch(callback.peek_conditions,
expected_result.peek_conditions);
ASSERT_EQ(callback.response_clusters.size(), expected_result.clusters.size());
for (size_t i = 0; i < callback.response_clusters.size(); i++) {
ExpectClustersMatch(std::move(callback.response_clusters[i]),
std::move(expected_result.clusters[i]));
}
}
} // namespace contextual_suggestions
// 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.
#ifndef COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_TEST_UTILS_H_
#define COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_TEST_UTILS_H_
#include "base/bind.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_metrics_reporter.h"
#include "components/ntp_snippets/contextual/contextual_suggestions_result.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace contextual_suggestions {
// ClustersCallback implementation for testing purposes that expects to be
// called only once, and remembers the results of that call in public members.
class MockClustersCallback {
public:
MockClustersCallback();
MockClustersCallback(const MockClustersCallback&);
~MockClustersCallback();
void Done(ContextualSuggestionsResult result);
FetchClustersCallback ToOnceCallback();
bool has_run = false;
std::string response_peek_text;
std::vector<Cluster> response_clusters;
PeekConditions peek_conditions;
};
class MockMetricsCallback {
public:
MockMetricsCallback();
~MockMetricsCallback();
void Report(ContextualSuggestionsEvent event);
std::vector<ContextualSuggestionsEvent> events;
};
void ExpectSuggestionsMatch(const ContextualSuggestion& actual,
const ContextualSuggestion& expected);
void ExpectClustersMatch(const Cluster& actual, const Cluster& expected);
void ExpectPeekConditionsMatch(const PeekConditions& actual,
const PeekConditions& expected);
// Expect that the individual data members saved in |callback| match the
// corresponding data in |expected_result|.
void ExpectResponsesMatch(const MockClustersCallback& callback,
const ContextualSuggestionsResult& expected_result);
} // namespace contextual_suggestions
#endif // COMPONENTS_NTP_SNIPPETS_CONTEXTUAL_CONTEXTUAL_SUGGESTIONS_TEST_UTILS_H_
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