123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- // Copyright 2016 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/remote/remote_suggestion.h"
- #include <utility>
- #include "base/json/json_reader.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/time/time.h"
- #include "base/values.h"
- #include "components/ntp_snippets/remote/proto/ntp_snippets.pb.h"
- #include "components/ntp_snippets/time_serialization.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace ntp_snippets {
- namespace {
- using ::testing::ElementsAre;
- using ::testing::Eq;
- using ::testing::IsNull;
- using ::testing::NotNull;
- SnippetProto TestSnippetProto() {
- SnippetProto proto;
- proto.add_ids("foo");
- proto.add_ids("bar");
- proto.set_title("a suggestion title");
- proto.set_snippet("the snippet describing the suggestion.");
- proto.set_salient_image_url("http://google.com/logo/");
- proto.set_image_dominant_color(4289379276);
- proto.set_publish_date(1476095492);
- proto.set_expiry_date(1476354691);
- proto.set_score(1.5f);
- proto.set_dismissed(false);
- proto.set_remote_category_id(1);
- proto.set_fetch_date(1476364691);
- proto.set_content_type(SnippetProto_ContentType_VIDEO);
- auto* source = proto.mutable_source();
- source->set_url("http://cool-suggestions.com/");
- source->set_publisher_name("Great Suggestions Inc.");
- source->set_amp_url("http://cdn.ampproject.org/c/foo/");
- proto.set_rank(7);
- return proto;
- }
- base::Value::Dict TestSnippetJsonValue() {
- const char kJsonStr[] = R"(
- {
- "ids" : ["foo", "bar"],
- "title" : "a suggestion title",
- "snippet" : "the snippet describing the suggestion.",
- "fullPageUrl" : "http://cool-suggestions.com/",
- "creationTime" : "2016-06-30T11:01:37.000Z",
- "expirationTime" : "2016-07-01T11:01:37.000Z",
- "attribution" : "Great Suggestions Inc.",
- "imageUrl" : "http://google.com/logo/",
- "ampUrl" : "http://cdn.ampproject.org/c/foo/",
- "faviconUrl" : "http://localhost/favicon.ico",
- "score": 1.5,
- "notificationInfo": {
- "shouldNotify": true,
- "deadline": "2016-06-30T13:01:37.000Z"
- },
- "imageDominantColor": 4289379276
- }
- )";
- auto json_parsed = base::JSONReader::ReadAndReturnValueWithError(
- kJsonStr, base::JSON_PARSE_RFC);
- CHECK(json_parsed.has_value())
- << "error_message: " << json_parsed.error().message;
- return std::move(json_parsed->GetDict());
- }
- TEST(RemoteSuggestionTest, FromContentSuggestionsDictionary) {
- base::Value::Dict snippet_dict = TestSnippetJsonValue();
- const base::Time fetch_date = DeserializeTime(1466634774L);
- std::unique_ptr<RemoteSuggestion> snippet =
- RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- snippet_dict, kArticlesRemoteId, fetch_date);
- ASSERT_THAT(snippet, NotNull());
- EXPECT_EQ(snippet->id(), "foo");
- EXPECT_THAT(snippet->GetAllIDs(), ElementsAre("foo", "bar"));
- EXPECT_EQ(snippet->title(), "a suggestion title");
- EXPECT_EQ(snippet->snippet(), "the snippet describing the suggestion.");
- EXPECT_EQ(snippet->salient_image_url(), GURL("http://google.com/logo/"));
- ASSERT_TRUE(snippet->optional_image_dominant_color().has_value());
- EXPECT_EQ(*snippet->optional_image_dominant_color(), 4289379276u);
- EXPECT_EQ(1.5, snippet->score());
- auto unix_publish_date = snippet->publish_date() - base::Time::UnixEpoch();
- auto expiry_duration = snippet->expiry_date() - snippet->publish_date();
- EXPECT_FLOAT_EQ(unix_publish_date.InSecondsF(), 1467284497.000000f);
- EXPECT_FLOAT_EQ(expiry_duration.InSecondsF(), 86400.000000f);
- EXPECT_EQ(snippet->publisher_name(), "Great Suggestions Inc.");
- EXPECT_EQ(snippet->url(), GURL("http://cool-suggestions.com/"));
- EXPECT_EQ(snippet->amp_url(), GURL("http://cdn.ampproject.org/c/foo/"));
- EXPECT_TRUE(snippet->should_notify());
- auto notification_duration =
- snippet->notification_deadline() - snippet->publish_date();
- EXPECT_EQ(7200.0f, notification_duration.InSecondsF());
- EXPECT_EQ(fetch_date, snippet->fetch_date());
- }
- TEST(RemoteSuggestionTest,
- FromContentSuggestionsDictionaryWithoutImageOrSnippet) {
- base::Value::Dict snippet_dict = TestSnippetJsonValue();
- ASSERT_TRUE(snippet_dict.Remove("imageUrl"));
- ASSERT_TRUE(snippet_dict.Remove("snippet"));
- const base::Time fetch_date = DeserializeTime(1466634774L);
- std::unique_ptr<RemoteSuggestion> snippet =
- RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- snippet_dict, kArticlesRemoteId, fetch_date);
- ASSERT_THAT(snippet, NotNull());
- EXPECT_EQ(GURL(), snippet->salient_image_url());
- EXPECT_EQ("", snippet->snippet());
- }
- TEST(RemoteSuggestionTest, CreateFromProtoToProtoRoundtrip) {
- SnippetProto proto = TestSnippetProto();
- std::unique_ptr<RemoteSuggestion> snippet =
- RemoteSuggestion::CreateFromProto(proto);
- ASSERT_THAT(snippet, NotNull());
- // The snippet database relies on the fact that the first id in the protocol
- // buffer is considered the unique id.
- EXPECT_EQ(snippet->id(), "foo");
- // Unfortunately, we only have MessageLite protocol buffers in Chrome, so
- // comparing via DebugString() or MessageDifferencer is not working.
- // So we either need to compare field-by-field (maintenance heavy) or
- // compare the binary version (unusable diagnostic). Deciding for the latter.
- std::string proto_serialized, round_tripped_serialized;
- proto.SerializeToString(&proto_serialized);
- snippet->ToProto().SerializeToString(&round_tripped_serialized);
- EXPECT_EQ(proto_serialized, round_tripped_serialized);
- }
- TEST(RemoteSuggestionTest, CreateFromProtoIgnoreMissingFetchDate) {
- SnippetProto proto = TestSnippetProto();
- proto.clear_fetch_date();
- std::unique_ptr<RemoteSuggestion> snippet =
- RemoteSuggestion::CreateFromProto(proto);
- ASSERT_THAT(snippet, NotNull());
- EXPECT_EQ(snippet->fetch_date(), base::Time());
- }
- TEST(RemoteSuggestionTest, CreateFromProtoIgnoreMissingImageDominantColor) {
- SnippetProto proto = TestSnippetProto();
- proto.clear_image_dominant_color();
- std::unique_ptr<RemoteSuggestion> snippet =
- RemoteSuggestion::CreateFromProto(proto);
- ASSERT_THAT(snippet, NotNull());
- // The snippet database relies on the fact that the first id in the protocol
- // buffer is considered the unique id.
- EXPECT_EQ(snippet->id(), "foo");
- EXPECT_FALSE(snippet->optional_image_dominant_color().has_value());
- }
- TEST(RemoteSuggestionTest, CreateFromProtoIgnoreMissingSalientImageAndSnippet) {
- SnippetProto proto = TestSnippetProto();
- proto.clear_salient_image_url();
- proto.clear_snippet();
- std::unique_ptr<RemoteSuggestion> snippet =
- RemoteSuggestion::CreateFromProto(proto);
- ASSERT_THAT(snippet, NotNull());
- // The snippet database relies on the fact that the first id in the protocol
- // buffer is considered the unique id.
- EXPECT_EQ(snippet->id(), "foo");
- EXPECT_EQ(GURL(), snippet->salient_image_url());
- EXPECT_EQ("", snippet->snippet());
- }
- TEST(RemoteSuggestionTest, NotifcationInfoAllSpecified) {
- auto json = TestSnippetJsonValue();
- json.SetByDottedPath("notificationInfo.shouldNotify", true);
- json.SetByDottedPath("notificationInfo.deadline", "2016-06-30T13:01:37.000Z");
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- EXPECT_TRUE(snippet->should_notify());
- EXPECT_EQ(7200.0f,
- (snippet->notification_deadline() - snippet->publish_date())
- .InSecondsF());
- }
- TEST(RemoteSuggestionTest, NotificationInfoDeadlineInvalid) {
- auto json = TestSnippetJsonValue();
- json.SetByDottedPath("notificationInfo.shouldNotify", true);
- json.SetByDottedPath("notificationInfo.deadline", "abcd");
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- EXPECT_TRUE(snippet->should_notify());
- EXPECT_EQ(base::Time::Max(), snippet->notification_deadline());
- }
- TEST(RemoteSuggestionTest, NotificationInfoDeadlineAbsent) {
- auto json = TestSnippetJsonValue();
- json.SetByDottedPath("notificationInfo.shouldNotify", true);
- json.RemoveByDottedPath("notificationInfo.deadline");
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- EXPECT_TRUE(snippet->should_notify());
- EXPECT_EQ(base::Time::Max(), snippet->notification_deadline());
- }
- TEST(RemoteSuggestionTest, NotificationInfoShouldNotifyInvalid) {
- auto json = TestSnippetJsonValue();
- json.SetByDottedPath("notificationInfo.shouldNotify", "non-bool");
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- EXPECT_FALSE(snippet->should_notify());
- }
- TEST(RemoteSuggestionTest, NotificationInfoAbsent) {
- auto json = TestSnippetJsonValue();
- json.SetByDottedPath("notificationInfo.shouldNotify", false);
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- EXPECT_FALSE(snippet->should_notify());
- }
- TEST(RemoteSuggestionTest, ToContentSuggestionWithoutNotificationInfo) {
- auto json = TestSnippetJsonValue();
- json.Remove("notificationInfo");
- const base::Time fetch_date = DeserializeTime(1466634774L);
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, fetch_date);
- ASSERT_THAT(snippet, NotNull());
- ContentSuggestion sugg = snippet->ToContentSuggestion(
- Category::FromKnownCategory(KnownCategories::ARTICLES));
- EXPECT_THAT(sugg.id().category(),
- Eq(Category::FromKnownCategory(KnownCategories::ARTICLES)));
- EXPECT_THAT(sugg.id().id_within_category(), Eq("foo"));
- EXPECT_THAT(sugg.url(), Eq(GURL("http://cdn.ampproject.org/c/foo/")));
- EXPECT_THAT(sugg.title(), Eq(u"a suggestion title"));
- EXPECT_THAT(sugg.snippet_text(),
- Eq(u"the snippet describing the suggestion."));
- EXPECT_THAT(sugg.publish_date().ToJavaTime(), Eq(1467284497000));
- EXPECT_THAT(sugg.publisher_name(), Eq(u"Great Suggestions Inc."));
- EXPECT_THAT(sugg.score(), Eq(1.5));
- EXPECT_THAT(sugg.salient_image_url(), Eq(GURL("http://google.com/logo/")));
- EXPECT_THAT(sugg.notification_extra(), IsNull());
- EXPECT_THAT(sugg.fetch_date(), Eq(fetch_date));
- }
- TEST(RemoteSuggestionTest, ToContentSuggestionWithNotificationInfo) {
- auto json = TestSnippetJsonValue();
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- ASSERT_THAT(snippet, NotNull());
- ContentSuggestion sugg = snippet->ToContentSuggestion(
- Category::FromKnownCategory(KnownCategories::ARTICLES));
- EXPECT_THAT(sugg.id().category(),
- Eq(Category::FromKnownCategory(KnownCategories::ARTICLES)));
- EXPECT_THAT(sugg.id().id_within_category(), Eq("foo"));
- EXPECT_THAT(sugg.url(), Eq(GURL("http://cdn.ampproject.org/c/foo/")));
- EXPECT_THAT(sugg.title(), Eq(u"a suggestion title"));
- EXPECT_THAT(sugg.snippet_text(),
- Eq(u"the snippet describing the suggestion."));
- EXPECT_THAT(sugg.publish_date().ToJavaTime(), Eq(1467284497000));
- EXPECT_THAT(sugg.publisher_name(), Eq(u"Great Suggestions Inc."));
- EXPECT_THAT(sugg.score(), Eq(1.5));
- ASSERT_THAT(sugg.notification_extra(), NotNull());
- EXPECT_THAT(sugg.notification_extra()->deadline.ToJavaTime(),
- Eq(1467291697000));
- }
- TEST(RemoteSuggestionTest, ToContentSuggestionWithContentTypeVideo) {
- auto json = TestSnippetJsonValue();
- json.Set("contentType", "VIDEO");
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- ASSERT_THAT(snippet, NotNull());
- ContentSuggestion content_suggestion = snippet->ToContentSuggestion(
- Category::FromKnownCategory(KnownCategories::ARTICLES));
- EXPECT_THAT(content_suggestion.is_video_suggestion(), Eq(true));
- }
- TEST(RemoteSuggestionTest, ToContentSuggestionWithContentTypeUnknown) {
- auto json = TestSnippetJsonValue();
- json.Set("contentType", "UNKNOWN");
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- ASSERT_THAT(snippet, NotNull());
- ContentSuggestion content_suggestion = snippet->ToContentSuggestion(
- Category::FromKnownCategory(KnownCategories::ARTICLES));
- EXPECT_THAT(content_suggestion.is_video_suggestion(), Eq(false));
- }
- TEST(RemoteSuggestionTest, ToContentSuggestionWithMissingContentType) {
- auto json = TestSnippetJsonValue();
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- ASSERT_THAT(snippet, NotNull());
- ContentSuggestion content_suggestion = snippet->ToContentSuggestion(
- Category::FromKnownCategory(KnownCategories::ARTICLES));
- EXPECT_THAT(content_suggestion.is_video_suggestion(), Eq(false));
- }
- TEST(RemoteSuggestionTest, ToContentSuggestionWithLargeImageDominantColor) {
- auto json = TestSnippetJsonValue();
- // JSON does not support unsigned types. As a result the value is parsed as
- // int if it fits and as double otherwise.
- json.Set("imageDominantColor", 4289379276.);
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- ASSERT_THAT(snippet, NotNull());
- ContentSuggestion content_suggestion = snippet->ToContentSuggestion(
- Category::FromKnownCategory(KnownCategories::ARTICLES));
- ASSERT_TRUE(content_suggestion.optional_image_dominant_color().has_value());
- EXPECT_THAT(*content_suggestion.optional_image_dominant_color(),
- Eq(4289379276u));
- }
- TEST(RemoteSuggestionTest, ToContentSuggestionWithSmallImageDominantColor) {
- auto json = TestSnippetJsonValue();
- // JSON does not support unsigned types. As a result the value is parsed as
- // int if it fits and as double otherwise.
- json.Set("imageDominantColor", 16777216 /*=0x1000000*/);
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- ASSERT_THAT(snippet, NotNull());
- ContentSuggestion content_suggestion = snippet->ToContentSuggestion(
- Category::FromKnownCategory(KnownCategories::ARTICLES));
- ASSERT_TRUE(content_suggestion.optional_image_dominant_color().has_value());
- EXPECT_THAT(*content_suggestion.optional_image_dominant_color(),
- Eq(16777216u));
- }
- TEST(RemoteSuggestionTest, ToContentSuggestionWithoutImageDominantColor) {
- auto json = TestSnippetJsonValue();
- json.Remove("imageDominantColor");
- auto snippet = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
- json, 0, base::Time());
- ASSERT_THAT(snippet, NotNull());
- ContentSuggestion content_suggestion = snippet->ToContentSuggestion(
- Category::FromKnownCategory(KnownCategories::ARTICLES));
- EXPECT_FALSE(content_suggestion.optional_image_dominant_color().has_value());
- }
- } // namespace
- } // namespace ntp_snippets
|