1// Copyright 2013 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 "base/prefs/pref_service.h"
6#include "base/run_loop.h"
7#include "chrome/browser/extensions/api/gcm/gcm_api.h"
8#include "chrome/browser/extensions/extension_apitest.h"
9#include "chrome/browser/extensions/extension_gcm_app_handler.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/services/gcm/fake_gcm_profile_service.h"
12#include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
13#include "chrome/common/chrome_switches.h"
14#include "chrome/common/pref_names.h"
15#include "chrome/test/base/ui_test_utils.h"
16#include "extensions/test/result_catcher.h"
17
18using extensions::ResultCatcher;
19
20namespace {
21
22const char kEventsExtension[] = "gcm/events";
23
24gcm::GCMClient::SendErrorDetails CreateErrorDetails(
25    const std::string& message_id,
26    const gcm::GCMClient::Result result,
27    const std::string& total_messages) {
28  gcm::GCMClient::SendErrorDetails error;
29  error.message_id = message_id;
30  error.result = result;
31  error.additional_data["expectedMessageId"] = message_id;
32  switch (result) {
33    case gcm::GCMClient::ASYNC_OPERATION_PENDING:
34      error.additional_data["expectedErrorMessage"] =
35          "Asynchronous operation is pending.";
36      break;
37    case gcm::GCMClient::SERVER_ERROR:
38      error.additional_data["expectedErrorMessage"] = "Server error occurred.";
39      break;
40    case gcm::GCMClient::NETWORK_ERROR:
41      error.additional_data["expectedErrorMessage"] = "Network error occurred.";
42      break;
43    case gcm::GCMClient::TTL_EXCEEDED:
44      error.additional_data["expectedErrorMessage"] = "Time-to-live exceeded.";
45      break;
46    case gcm::GCMClient::UNKNOWN_ERROR:
47    default:  // Default case is the same as UNKNOWN_ERROR
48      error.additional_data["expectedErrorMessage"] = "Unknown error occurred.";
49      break;
50  }
51  error.additional_data["totalMessages"] = total_messages;
52  return error;
53}
54
55}  // namespace
56
57namespace extensions {
58
59class GcmApiTest : public ExtensionApiTest {
60 public:
61  GcmApiTest() : fake_gcm_profile_service_(NULL) {}
62
63 protected:
64  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE;
65  virtual void SetUpOnMainThread() OVERRIDE;
66
67  void StartCollecting();
68
69  const Extension* LoadTestExtension(const std::string& extension_path,
70                                     const std::string& page_name);
71  gcm::FakeGCMProfileService* service() const;
72
73 private:
74  gcm::FakeGCMProfileService* fake_gcm_profile_service_;
75};
76
77void GcmApiTest::SetUpCommandLine(CommandLine* command_line) {
78  // We now always create the GCMProfileService instance in
79  // ProfileSyncServiceFactory that is called when a profile is being
80  // initialized. In order to prevent it from being created, we add the switch
81  // to disable the sync logic.
82  command_line->AppendSwitch(switches::kDisableSync);
83
84  ExtensionApiTest::SetUpCommandLine(command_line);
85}
86
87void GcmApiTest::SetUpOnMainThread() {
88  // Enable GCM such that tests could be run on all channels.
89  browser()->profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, true);
90
91  gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactory(
92      browser()->profile(), &gcm::FakeGCMProfileService::Build);
93  fake_gcm_profile_service_ = static_cast<gcm::FakeGCMProfileService*>(
94      gcm::GCMProfileServiceFactory::GetInstance()->GetForProfile(
95          browser()->profile()));
96
97  ExtensionApiTest::SetUpOnMainThread();
98}
99
100void GcmApiTest::StartCollecting() {
101  service()->set_collect(true);
102}
103
104gcm::FakeGCMProfileService* GcmApiTest::service() const {
105  return fake_gcm_profile_service_;
106}
107
108const Extension* GcmApiTest::LoadTestExtension(
109    const std::string& extension_path,
110    const std::string& page_name) {
111  const Extension* extension =
112      LoadExtension(test_data_dir_.AppendASCII(extension_path));
113  if (extension) {
114    ui_test_utils::NavigateToURL(
115        browser(), extension->GetResourceURL(page_name));
116  }
117  return extension;
118}
119
120IN_PROC_BROWSER_TEST_F(GcmApiTest, RegisterValidation) {
121  ASSERT_TRUE(RunExtensionTest("gcm/functions/register_validation"));
122}
123
124IN_PROC_BROWSER_TEST_F(GcmApiTest, Register) {
125  StartCollecting();
126  ASSERT_TRUE(RunExtensionTest("gcm/functions/register"));
127
128  const std::vector<std::string>& sender_ids =
129      service()->last_registered_sender_ids();
130  EXPECT_TRUE(std::find(sender_ids.begin(), sender_ids.end(), "Sender1") !=
131                  sender_ids.end());
132  EXPECT_TRUE(std::find(sender_ids.begin(), sender_ids.end(), "Sender2") !=
133                  sender_ids.end());
134}
135
136IN_PROC_BROWSER_TEST_F(GcmApiTest, Unregister) {
137  service()->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
138  service()->AddExpectedUnregisterResponse(gcm::GCMClient::SERVER_ERROR);
139
140  ASSERT_TRUE(RunExtensionTest("gcm/functions/unregister"));
141}
142
143IN_PROC_BROWSER_TEST_F(GcmApiTest, SendValidation) {
144  ASSERT_TRUE(RunExtensionTest("gcm/functions/send"));
145}
146
147IN_PROC_BROWSER_TEST_F(GcmApiTest, SendMessageData) {
148  StartCollecting();
149  ASSERT_TRUE(RunExtensionTest("gcm/functions/send_message_data"));
150
151  EXPECT_EQ("destination-id", service()->last_receiver_id());
152  const gcm::GCMClient::OutgoingMessage& message =
153      service()->last_sent_message();
154  gcm::GCMClient::MessageData::const_iterator iter;
155
156  EXPECT_EQ(100, message.time_to_live);
157
158  EXPECT_TRUE((iter = message.data.find("key1")) != message.data.end());
159  EXPECT_EQ("value1", iter->second);
160
161  EXPECT_TRUE((iter = message.data.find("key2")) != message.data.end());
162  EXPECT_EQ("value2", iter->second);
163}
164
165IN_PROC_BROWSER_TEST_F(GcmApiTest, SendMessageDefaultTTL) {
166  StartCollecting();
167  ASSERT_TRUE(RunExtensionTest("gcm/functions/send_message_default_ttl"));
168
169  EXPECT_EQ("destination-id", service()->last_receiver_id());
170  const gcm::GCMClient::OutgoingMessage& message =
171      service()->last_sent_message();
172  gcm::GCMClient::MessageData::const_iterator iter;
173
174  EXPECT_EQ(gcm::GCMClient::OutgoingMessage::kMaximumTTL, message.time_to_live);
175}
176
177IN_PROC_BROWSER_TEST_F(GcmApiTest, OnMessagesDeleted) {
178  ResultCatcher catcher;
179  catcher.RestrictToBrowserContext(profile());
180
181  const extensions::Extension* extension =
182      LoadTestExtension(kEventsExtension, "on_messages_deleted.html");
183  ASSERT_TRUE(extension);
184
185  extensions::ExtensionGCMAppHandler app_handler(profile());
186  app_handler.OnMessagesDeleted(extension->id());
187  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
188}
189
190IN_PROC_BROWSER_TEST_F(GcmApiTest, OnMessage) {
191  ResultCatcher catcher;
192  catcher.RestrictToBrowserContext(profile());
193
194  const extensions::Extension* extension =
195      LoadTestExtension(kEventsExtension, "on_message.html");
196  ASSERT_TRUE(extension);
197
198  extensions::ExtensionGCMAppHandler app_handler(profile());
199
200  gcm::GCMClient::IncomingMessage message;
201  message.data["property1"] = "value1";
202  message.data["property2"] = "value2";
203  // First message is sent without a collapse key.
204  app_handler.OnMessage(extension->id(), message);
205
206  // Second message carries the same data and a collapse key.
207  message.collapse_key = "collapseKeyValue";
208  app_handler.OnMessage(extension->id(), message);
209
210  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
211}
212
213IN_PROC_BROWSER_TEST_F(GcmApiTest, OnSendError) {
214  ResultCatcher catcher;
215  catcher.RestrictToBrowserContext(profile());
216
217  const extensions::Extension* extension =
218      LoadTestExtension(kEventsExtension, "on_send_error.html");
219  ASSERT_TRUE(extension);
220
221  std::string total_expected_messages = "5";
222  extensions::ExtensionGCMAppHandler app_handler(profile());
223  app_handler.OnSendError(
224      extension->id(),
225      CreateErrorDetails("error_message_1",
226                         gcm::GCMClient::ASYNC_OPERATION_PENDING,
227                         total_expected_messages));
228  app_handler.OnSendError(
229      extension->id(),
230      CreateErrorDetails("error_message_2",
231                         gcm::GCMClient::SERVER_ERROR,
232                         total_expected_messages));
233  app_handler.OnSendError(
234      extension->id(),
235      CreateErrorDetails("error_message_3",
236                         gcm::GCMClient::NETWORK_ERROR,
237                         total_expected_messages));
238  app_handler.OnSendError(
239      extension->id(),
240      CreateErrorDetails("error_message_4",
241                         gcm::GCMClient::UNKNOWN_ERROR,
242                         total_expected_messages));
243  app_handler.OnSendError(
244      extension->id(),
245      CreateErrorDetails("error_message_5",
246                         gcm::GCMClient::TTL_EXCEEDED,
247                         total_expected_messages));
248
249  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
250}
251
252IN_PROC_BROWSER_TEST_F(GcmApiTest, Incognito) {
253  ResultCatcher catcher;
254  catcher.RestrictToBrowserContext(profile());
255  ResultCatcher incognito_catcher;
256  incognito_catcher.RestrictToBrowserContext(
257      profile()->GetOffTheRecordProfile());
258
259  ASSERT_TRUE(RunExtensionTestIncognito("gcm/functions/incognito"));
260
261  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
262  EXPECT_TRUE(incognito_catcher.GetNextResult()) << incognito_catcher.message();
263}
264
265}  // namespace extensions
266