1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "remoting/client/notification/notification_client.h"
6 
7 #include <memory>
8 
9 #include "base/memory/ptr_util.h"
10 #include "base/optional.h"
11 #include "base/test/mock_callback.h"
12 #include "base/values.h"
13 #include "remoting/client/notification/json_fetcher.h"
14 #include "remoting/client/notification/notification_message.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 namespace remoting {
19 
20 namespace {
21 
22 using ::testing::ByMove;
23 using ::testing::Return;
24 
25 constexpr char kTestEmail[] = "test@example.com";
26 constexpr char kTestPlatform[] = "IOS";
27 constexpr char kTestVersion[] = "76.0.3809.13";
28 constexpr char kTestLocale[] = "zh-CN";
29 
30 class MockJsonFetcher : public JsonFetcher {
31  public:
32   // GMock doesn't work with rvalue parameters. This works around it.
33   MOCK_CONST_METHOD1(FetchJsonFile,
34                      base::Optional<base::Value>(const std::string&));
FetchJsonFile(const std::string & relative_path,FetchJsonFileCallback done,const net::NetworkTrafficAnnotationTag &)35   void FetchJsonFile(const std::string& relative_path,
36                      FetchJsonFileCallback done,
37                      const net::NetworkTrafficAnnotationTag&) override {
38     auto value_opt = FetchJsonFile(relative_path);
39     std::move(done).Run(std::move(value_opt));
40   }
41 };
42 
43 MATCHER(NoMessage, "") {
44   return !arg.has_value();
45 }
46 
47 MATCHER_P(MessageMatches, expected, "") {
48   return arg->message_id == expected.message_id &&
49          arg->message_text == expected.message_text &&
50          arg->link_text == expected.link_text &&
51          arg->link_url == expected.link_url;
52 }
53 
54 template <typename T>
ReturnByMove(T t)55 decltype(auto) ReturnByMove(T t) {
56   return Return(ByMove(std::move(t)));
57 }
58 
CreateDefaultRule()59 base::Value CreateDefaultRule() {
60   base::Value rule(base::Value::Type::DICTIONARY);
61   rule.SetStringKey("target_platform", "IOS");
62   rule.SetStringKey("version", "[75-)");
63   rule.SetStringKey("message_id", "test_message");
64   rule.SetStringKey("message_text", "message_text.json");
65   rule.SetStringKey("link_text", "link_text.json");
66   rule.SetStringKey("link_url", "https://example.com/some_link");
67   rule.SetIntKey("percent", 100);
68   rule.SetBoolKey("allow_silence", true);
69   return rule;
70 }
71 
CreateDefaultTranslations(const std::string & text)72 base::Value CreateDefaultTranslations(const std::string& text) {
73   base::Value translations(base::Value::Type::DICTIONARY);
74   translations.SetStringKey("en-US", "en-US:" + text);
75   translations.SetStringKey("zh-CN", "zh-CN:" + text);
76   return translations;
77 }
78 
CreateDefaultNotification()79 NotificationMessage CreateDefaultNotification() {
80   NotificationMessage message;
81   message.message_id = "test_message";
82   message.message_text = "zh-CN:message";
83   message.link_text = "zh-CN:link";
84   message.link_url = "https://example.com/some_link";
85   message.allow_silence = true;
86   return message;
87 }
88 
89 }  // namespace
90 
91 class NotificationClientTest : public ::testing::Test {
92  public:
NotificationClientTest()93   NotificationClientTest() { Reset(true); }
94 
95   ~NotificationClientTest() override = default;
96 
97  protected:
Reset(bool should_ignore_dev_messages)98   void Reset(bool should_ignore_dev_messages) {
99     auto fetcher = std::make_unique<MockJsonFetcher>();
100     fetcher_ = fetcher.get();
101     client_ = base::WrapUnique(
102         new NotificationClient(std::move(fetcher), kTestPlatform, kTestVersion,
103                                kTestLocale, should_ignore_dev_messages));
104   }
105 
106   MockJsonFetcher* fetcher_;
107   std::unique_ptr<NotificationClient> client_;
108 };
109 
TEST_F(NotificationClientTest,NoRule)110 TEST_F(NotificationClientTest, NoRule) {
111   base::Value rules(base::Value::Type::LIST);
112   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
113       .WillOnce(ReturnByMove(std::move(rules)));
114 
115   base::MockCallback<NotificationClient::NotificationCallback> callback;
116   EXPECT_CALL(callback, Run(NoMessage()));
117   client_->GetNotification(kTestEmail, callback.Get());
118 }
119 
TEST_F(NotificationClientTest,DefaultRule)120 TEST_F(NotificationClientTest, DefaultRule) {
121   base::Value rules(base::Value::Type::LIST);
122   rules.Append(CreateDefaultRule());
123   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
124       .WillOnce(ReturnByMove(std::move(rules)));
125   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/message_text.json"))
126       .WillOnce(ReturnByMove(CreateDefaultTranslations("message")));
127   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/link_text.json"))
128       .WillOnce(ReturnByMove(CreateDefaultTranslations("link")));
129 
130   base::MockCallback<NotificationClient::NotificationCallback> callback;
131   EXPECT_CALL(callback, Run(MessageMatches(CreateDefaultNotification())));
132   client_->GetNotification(kTestEmail, callback.Get());
133 }
134 
TEST_F(NotificationClientTest,PlatformNotMatched)135 TEST_F(NotificationClientTest, PlatformNotMatched) {
136   base::Value rule = CreateDefaultRule();
137   rule.SetStringKey("target_platform", "ANDROID");
138   base::Value rules(base::Value::Type::LIST);
139   rules.Append(std::move(rule));
140   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
141       .WillOnce(ReturnByMove(std::move(rules)));
142 
143   base::MockCallback<NotificationClient::NotificationCallback> callback;
144   EXPECT_CALL(callback, Run(NoMessage()));
145   client_->GetNotification(kTestEmail, callback.Get());
146 }
147 
TEST_F(NotificationClientTest,VersionNotMatched)148 TEST_F(NotificationClientTest, VersionNotMatched) {
149   base::Value rule = CreateDefaultRule();
150   rule.SetStringKey("version", "[77-)");
151   base::Value rules(base::Value::Type::LIST);
152   rules.Append(std::move(rule));
153   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
154       .WillOnce(ReturnByMove(std::move(rules)));
155 
156   base::MockCallback<NotificationClient::NotificationCallback> callback;
157   EXPECT_CALL(callback, Run(NoMessage()));
158   client_->GetNotification(kTestEmail, callback.Get());
159 }
160 
TEST_F(NotificationClientTest,UserNotSelected)161 TEST_F(NotificationClientTest, UserNotSelected) {
162   base::Value rule = CreateDefaultRule();
163   rule.SetIntKey("percent", 0);
164   base::Value rules(base::Value::Type::LIST);
165   rules.Append(std::move(rule));
166   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
167       .WillOnce(ReturnByMove(std::move(rules)));
168 
169   base::MockCallback<NotificationClient::NotificationCallback> callback;
170   EXPECT_CALL(callback, Run(NoMessage()));
171   client_->GetNotification(kTestEmail, callback.Get());
172 }
173 
TEST_F(NotificationClientTest,SecondRuleMatches)174 TEST_F(NotificationClientTest, SecondRuleMatches) {
175   base::Value rules(base::Value::Type::LIST);
176   base::Value rule_1 = CreateDefaultRule();
177   rule_1.SetStringKey("target_platform", "ANDROID");
178   rule_1.SetStringKey("message_text", "message_text_1.json");
179   rules.Append(std::move(rule_1));
180   rules.Append(CreateDefaultRule());
181   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
182       .WillOnce(ReturnByMove(std::move(rules)));
183   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/message_text.json"))
184       .WillOnce(ReturnByMove(CreateDefaultTranslations("message")));
185   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/link_text.json"))
186       .WillOnce(ReturnByMove(CreateDefaultTranslations("link")));
187 
188   base::MockCallback<NotificationClient::NotificationCallback> callback;
189   EXPECT_CALL(callback, Run(MessageMatches(CreateDefaultNotification())));
190   client_->GetNotification(kTestEmail, callback.Get());
191 }
192 
TEST_F(NotificationClientTest,MultipleMatchingRules_FirstRuleSelected)193 TEST_F(NotificationClientTest, MultipleMatchingRules_FirstRuleSelected) {
194   base::Value rules(base::Value::Type::LIST);
195   rules.Append(CreateDefaultRule());
196   base::Value rule_2 = CreateDefaultRule();
197   rule_2.SetStringKey("message_text", "message_text_2.json");
198   rules.Append(std::move(rule_2));
199   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
200       .WillOnce(ReturnByMove(std::move(rules)));
201   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/message_text.json"))
202       .WillOnce(ReturnByMove(CreateDefaultTranslations("message")));
203   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/link_text.json"))
204       .WillOnce(ReturnByMove(CreateDefaultTranslations("link")));
205 
206   base::MockCallback<NotificationClient::NotificationCallback> callback;
207   EXPECT_CALL(callback, Run(MessageMatches(CreateDefaultNotification())));
208   client_->GetNotification(kTestEmail, callback.Get());
209 }
210 
TEST_F(NotificationClientTest,TextFilesNotFound)211 TEST_F(NotificationClientTest, TextFilesNotFound) {
212   base::Value rules(base::Value::Type::LIST);
213   rules.Append(CreateDefaultRule());
214   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
215       .WillOnce(ReturnByMove(std::move(rules)));
216 
217   base::Value translation = CreateDefaultTranslations("message");
218 
219   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/message_text.json"))
220       .WillOnce(ReturnByMove(base::nullopt));
221   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/link_text.json"))
222       .WillOnce(ReturnByMove(base::nullopt));
223 
224   base::MockCallback<NotificationClient::NotificationCallback> callback;
225   EXPECT_CALL(callback, Run(NoMessage()));
226   client_->GetNotification(kTestEmail, callback.Get());
227 }
228 
TEST_F(NotificationClientTest,TranslationNotFound_FallbackToEnglish)229 TEST_F(NotificationClientTest, TranslationNotFound_FallbackToEnglish) {
230   base::Value rules(base::Value::Type::LIST);
231   rules.Append(CreateDefaultRule());
232   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
233       .WillOnce(ReturnByMove(std::move(rules)));
234 
235   base::Value translations = CreateDefaultTranslations("message");
236   translations.RemoveKey("zh-CN");
237   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/message_text.json"))
238       .WillOnce(ReturnByMove(std::move(translations)));
239   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/link_text.json"))
240       .WillOnce(ReturnByMove(CreateDefaultTranslations("link")));
241 
242   NotificationMessage notification = CreateDefaultNotification();
243   notification.message_text = "en-US:message";
244   base::MockCallback<NotificationClient::NotificationCallback> callback;
245   EXPECT_CALL(callback, Run(MessageMatches(notification)));
246   client_->GetNotification(kTestEmail, callback.Get());
247 }
248 
TEST_F(NotificationClientTest,NoAvailableTranslation)249 TEST_F(NotificationClientTest, NoAvailableTranslation) {
250   base::Value rules(base::Value::Type::LIST);
251   rules.Append(CreateDefaultRule());
252   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
253       .WillOnce(ReturnByMove(std::move(rules)));
254 
255   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/message_text.json"))
256       .WillOnce(ReturnByMove(base::Value(base::Value::Type::DICTIONARY)));
257   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/link_text.json"))
258       .WillOnce(ReturnByMove(base::Value(base::Value::Type::DICTIONARY)));
259 
260   base::MockCallback<NotificationClient::NotificationCallback> callback;
261   EXPECT_CALL(callback, Run(NoMessage()));
262   client_->GetNotification(kTestEmail, callback.Get());
263 }
264 
TEST_F(NotificationClientTest,ReleaseBuildsIgnoreDevMessages)265 TEST_F(NotificationClientTest, ReleaseBuildsIgnoreDevMessages) {
266   base::Value rules(base::Value::Type::LIST);
267   base::Value rule = CreateDefaultRule();
268   rule.SetBoolKey("dev_mode", true);
269   rules.Append(std::move(rule));
270   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
271       .WillOnce(ReturnByMove(std::move(rules)));
272 
273   base::MockCallback<NotificationClient::NotificationCallback> callback;
274   EXPECT_CALL(callback, Run(NoMessage()));
275   client_->GetNotification(kTestEmail, callback.Get());
276 }
277 
TEST_F(NotificationClientTest,ReleaseBuildsDontIgnoreDevMessages)278 TEST_F(NotificationClientTest, ReleaseBuildsDontIgnoreDevMessages) {
279   base::Value rules(base::Value::Type::LIST);
280   base::Value rule = CreateDefaultRule();
281   rule.SetBoolKey("dev_mode", false);
282   rules.Append(std::move(rule));
283   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
284       .WillOnce(ReturnByMove(std::move(rules)));
285   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/message_text.json"))
286       .WillOnce(ReturnByMove(CreateDefaultTranslations("message")));
287   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/link_text.json"))
288       .WillOnce(ReturnByMove(CreateDefaultTranslations("link")));
289 
290   base::MockCallback<NotificationClient::NotificationCallback> callback;
291   EXPECT_CALL(callback, Run(MessageMatches(CreateDefaultNotification())));
292   client_->GetNotification(kTestEmail, callback.Get());
293 }
294 
TEST_F(NotificationClientTest,DebugBuildsDontIgnoreDevMessages)295 TEST_F(NotificationClientTest, DebugBuildsDontIgnoreDevMessages) {
296   Reset(/* should_ignore_dev_messages */ false);
297 
298   base::Value rules(base::Value::Type::LIST);
299   base::Value rule = CreateDefaultRule();
300   rule.SetBoolKey("dev_mode", true);
301   rules.Append(std::move(rule));
302   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
303       .WillOnce(ReturnByMove(std::move(rules)));
304   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/message_text.json"))
305       .WillOnce(ReturnByMove(CreateDefaultTranslations("message")));
306   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/link_text.json"))
307       .WillOnce(ReturnByMove(CreateDefaultTranslations("link")));
308 
309   base::MockCallback<NotificationClient::NotificationCallback> callback;
310   EXPECT_CALL(callback, Run(MessageMatches(CreateDefaultNotification())));
311   client_->GetNotification(kTestEmail, callback.Get());
312 }
313 
TEST_F(NotificationClientTest,AllowSilenceNotSet_DefaultToFalse)314 TEST_F(NotificationClientTest, AllowSilenceNotSet_DefaultToFalse) {
315   base::Value rules(base::Value::Type::LIST);
316   base::Value rule = CreateDefaultRule();
317   rule.RemoveKey("allow_silence");
318   rules.Append(std::move(rule));
319   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/rules.json"))
320       .WillOnce(ReturnByMove(std::move(rules)));
321   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/message_text.json"))
322       .WillOnce(ReturnByMove(CreateDefaultTranslations("message")));
323   EXPECT_CALL(*fetcher_, FetchJsonFile("notification/link_text.json"))
324       .WillOnce(ReturnByMove(CreateDefaultTranslations("link")));
325 
326   NotificationMessage notification = CreateDefaultNotification();
327   notification.allow_silence = false;
328   base::MockCallback<NotificationClient::NotificationCallback> callback;
329   EXPECT_CALL(callback, Run(MessageMatches(notification)));
330   client_->GetNotification(kTestEmail, callback.Get());
331 }
332 
333 }  // namespace remoting