910e62b5创建于 1月15日历史提交
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "chrome/browser/extensions/api/gcm/gcm_api.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_gcm_app_handler.h"
#include "chrome/browser/gcm/gcm_profile_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "components/gcm_driver/fake_gcm_profile_service.h"
#include "components/sync/base/command_line_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/test/result_catcher.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

using extensions::ResultCatcher;

namespace {

const char kEventsExtension[] = "gcm/events";

gcm::GCMClient::SendErrorDetails CreateErrorDetails(
    const std::string& message_id,
    const gcm::GCMClient::Result result,
    const std::string& total_messages) {
  gcm::GCMClient::SendErrorDetails error;
  error.message_id = message_id;
  error.result = result;
  error.additional_data["expectedMessageId"] = message_id;
  switch (result) {
    case gcm::GCMClient::ASYNC_OPERATION_PENDING:
      error.additional_data["expectedErrorMessage"] =
          "Asynchronous operation is pending.";
      break;
    case gcm::GCMClient::SERVER_ERROR:
      error.additional_data["expectedErrorMessage"] = "Server error occurred.";
      break;
    case gcm::GCMClient::NETWORK_ERROR:
      error.additional_data["expectedErrorMessage"] = "Network error occurred.";
      break;
    case gcm::GCMClient::TTL_EXCEEDED:
      error.additional_data["expectedErrorMessage"] = "Time-to-live exceeded.";
      break;
    case gcm::GCMClient::UNKNOWN_ERROR:
    default:  // Default case is the same as UNKNOWN_ERROR
      error.additional_data["expectedErrorMessage"] = "Unknown error occurred.";
      break;
  }
  error.additional_data["totalMessages"] = total_messages;
  return error;
}

}  // namespace

namespace extensions {

class GcmApiTest : public ExtensionApiTest {
 protected:
  // BrowserTestBase overrides.
  void SetUpCommandLine(base::CommandLine* command_line) override;
  void SetUpBrowserContextKeyedServices(
      content::BrowserContext* context) override;

  void StartCollecting();

  const Extension* LoadTestExtension(const std::string& extension_path,
                                     const std::string& page_name);
  gcm::FakeGCMProfileService* service();
};

void GcmApiTest::SetUpCommandLine(base::CommandLine* command_line) {
  // We now always create the GCMProfileService instance in
  // SyncServiceFactory that is called when a profile is being
  // initialized. In order to prevent it from being created, we add the switch
  // to disable the sync logic.
  command_line->AppendSwitch(syncer::kDisableSync);

  ExtensionApiTest::SetUpCommandLine(command_line);
}

void GcmApiTest::SetUpBrowserContextKeyedServices(
    content::BrowserContext* context) {
  ExtensionApiTest::SetUpBrowserContextKeyedServices(context);
  gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactory(
      context, base::BindRepeating(&gcm::FakeGCMProfileService::Build));
}

void GcmApiTest::StartCollecting() {
  service()->set_collect(true);
}

gcm::FakeGCMProfileService* GcmApiTest::service() {
  return static_cast<gcm::FakeGCMProfileService*>(
      gcm::GCMProfileServiceFactory::GetInstance()->GetForProfile(profile()));
}

const Extension* GcmApiTest::LoadTestExtension(
    const std::string& extension_path,
    const std::string& page_name) {
  const Extension* extension =
      LoadExtension(test_data_dir_.AppendASCII(extension_path));
  if (extension) {
    const GURL extension_url = extension->GetResourceURL(page_name);
    EXPECT_TRUE(extension_url.is_valid());
    auto* web_contents = GetActiveWebContents();
    EXPECT_TRUE(NavigateToURL(web_contents, extension_url));
  }
  return extension;
}

#if !BUILDFLAG(IS_ANDROID)
// Register and unregister are deprecated on the server. Don't test them.
// TODO(crbug.com/421235963): Consider deprecating on other platforms.
IN_PROC_BROWSER_TEST_F(GcmApiTest, RegisterValidation) {
  ASSERT_TRUE(RunExtensionTest("gcm/functions/register_validation"));
}

IN_PROC_BROWSER_TEST_F(GcmApiTest, Register) {
  StartCollecting();
  ASSERT_TRUE(RunExtensionTest("gcm/functions/register"));

  const std::vector<std::string>& sender_ids =
      service()->last_registered_sender_ids();
  EXPECT_TRUE(base::Contains(sender_ids, "Sender1"));
  EXPECT_TRUE(base::Contains(sender_ids, "Sender2"));
}

IN_PROC_BROWSER_TEST_F(GcmApiTest, Unregister) {
  service()->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
  service()->AddExpectedUnregisterResponse(gcm::GCMClient::SERVER_ERROR);

  ASSERT_TRUE(RunExtensionTest("gcm/functions/unregister"));
}
#endif  // !BUILDFLAG(IS_ANDROID)

IN_PROC_BROWSER_TEST_F(GcmApiTest, SendValidation) {
  ASSERT_TRUE(RunExtensionTest("gcm/functions/send"));
}

IN_PROC_BROWSER_TEST_F(GcmApiTest, SendMessageData) {
  StartCollecting();
  ASSERT_TRUE(RunExtensionTest("gcm/functions/send_message_data"));

  EXPECT_EQ("destination-id", service()->last_receiver_id());
  const gcm::OutgoingMessage& message = service()->last_sent_message();
  gcm::MessageData::const_iterator iter;

  EXPECT_EQ(100, message.time_to_live);

  EXPECT_TRUE((iter = message.data.find("key1")) != message.data.end());
  EXPECT_EQ("value1", iter->second);

  EXPECT_TRUE((iter = message.data.find("key2")) != message.data.end());
  EXPECT_EQ("value2", iter->second);
}

IN_PROC_BROWSER_TEST_F(GcmApiTest, SendMessageDefaultTTL) {
  StartCollecting();
  ASSERT_TRUE(RunExtensionTest("gcm/functions/send_message_default_ttl"));

  EXPECT_EQ("destination-id", service()->last_receiver_id());
  const gcm::OutgoingMessage& message = service()->last_sent_message();
  gcm::MessageData::const_iterator iter;

  EXPECT_EQ(gcm::OutgoingMessage::kMaximumTTL, message.time_to_live);
}

IN_PROC_BROWSER_TEST_F(GcmApiTest, OnMessagesDeleted) {
  ResultCatcher catcher;
  catcher.RestrictToBrowserContext(profile());

  const extensions::Extension* extension =
      LoadTestExtension(kEventsExtension, "on_messages_deleted.html");
  ASSERT_TRUE(extension);

  extensions::ExtensionGCMAppHandler app_handler(profile());
  app_handler.OnMessagesDeleted(extension->id());
  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}

IN_PROC_BROWSER_TEST_F(GcmApiTest, OnMessage) {
  ResultCatcher catcher;
  catcher.RestrictToBrowserContext(profile());

  const extensions::Extension* extension =
      LoadTestExtension(kEventsExtension, "on_message.html");
  ASSERT_TRUE(extension);

  extensions::ExtensionGCMAppHandler app_handler(profile());

  gcm::IncomingMessage message;
  message.data["property1"] = "value1";
  message.data["property2"] = "value2";
  // First message is sent without from and collapse key.
  app_handler.OnMessage(extension->id(), message);

  // Second message is send with from.
  message.sender_id = "12345678";
  app_handler.OnMessage(extension->id(), message);

  // Third message is send with a collapse key.
  message.sender_id.clear();
  message.collapse_key = "collapseKeyValue";
  app_handler.OnMessage(extension->id(), message);

  // Fourth message carries the same data, from and collapse key.
  message.sender_id = "12345678";
  app_handler.OnMessage(extension->id(), message);

  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}

IN_PROC_BROWSER_TEST_F(GcmApiTest, OnSendError) {
  ResultCatcher catcher;
  catcher.RestrictToBrowserContext(profile());

  const extensions::Extension* extension =
      LoadTestExtension(kEventsExtension, "on_send_error.html");
  ASSERT_TRUE(extension);

  std::string total_expected_messages = "5";
  extensions::ExtensionGCMAppHandler app_handler(profile());
  app_handler.OnSendError(
      extension->id(),
      CreateErrorDetails("error_message_1",
                         gcm::GCMClient::ASYNC_OPERATION_PENDING,
                         total_expected_messages));
  app_handler.OnSendError(
      extension->id(),
      CreateErrorDetails("error_message_2",
                         gcm::GCMClient::SERVER_ERROR,
                         total_expected_messages));
  app_handler.OnSendError(
      extension->id(),
      CreateErrorDetails("error_message_3",
                         gcm::GCMClient::NETWORK_ERROR,
                         total_expected_messages));
  app_handler.OnSendError(
      extension->id(),
      CreateErrorDetails("error_message_4",
                         gcm::GCMClient::UNKNOWN_ERROR,
                         total_expected_messages));
  app_handler.OnSendError(
      extension->id(),
      CreateErrorDetails("error_message_5",
                         gcm::GCMClient::TTL_EXCEEDED,
                         total_expected_messages));

  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
}

#if !BUILDFLAG(IS_ANDROID)
// Register and unregister are deprecated on the server. Don't test them.
// TODO(crbug.com/421235963): Consider deprecating on other platforms.
IN_PROC_BROWSER_TEST_F(GcmApiTest, Incognito) {
  ResultCatcher catcher;
  catcher.RestrictToBrowserContext(profile());
  ResultCatcher incognito_catcher;
  incognito_catcher.RestrictToBrowserContext(
      profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));

  ASSERT_TRUE(RunExtensionTest("gcm/functions/incognito", {},
                               {.allow_in_incognito = true}));

  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
  EXPECT_TRUE(incognito_catcher.GetNextResult()) << incognito_catcher.message();
}
#endif  // !BUILDFLAG(IS_ANDROID)

}  // namespace extensions