summaryrefslogtreecommitdiffstats
path: root/chromium/content/browser/service_worker/service_worker_version_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/service_worker/service_worker_version_unittest.cc')
-rw-r--r--chromium/content/browser/service_worker/service_worker_version_unittest.cc355
1 files changed, 355 insertions, 0 deletions
diff --git a/chromium/content/browser/service_worker/service_worker_version_unittest.cc b/chromium/content/browser/service_worker/service_worker_version_unittest.cc
new file mode 100644
index 00000000000..6622e5aba83
--- /dev/null
+++ b/chromium/content/browser/service_worker/service_worker_version_unittest.cc
@@ -0,0 +1,355 @@
+// Copyright 2014 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 "base/basictypes.h"
+#include "base/run_loop.h"
+#include "content/browser/service_worker/embedded_worker_registry.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_test_utils.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// IPC messages for testing ---------------------------------------------------
+
+#define IPC_MESSAGE_IMPL
+#include "ipc/ipc_message_macros.h"
+
+#define IPC_MESSAGE_START TestMsgStart
+
+IPC_MESSAGE_CONTROL0(TestMsg_Message);
+IPC_MESSAGE_ROUTED1(TestMsg_MessageFromWorker, int);
+
+// ---------------------------------------------------------------------------
+
+namespace content {
+
+namespace {
+
+static const int kRenderProcessId = 1;
+
+class MessageReceiver : public EmbeddedWorkerTestHelper {
+ public:
+ MessageReceiver()
+ : EmbeddedWorkerTestHelper(kRenderProcessId),
+ current_embedded_worker_id_(0) {}
+ virtual ~MessageReceiver() {}
+
+ virtual bool OnMessageToWorker(int thread_id,
+ int embedded_worker_id,
+ const IPC::Message& message) OVERRIDE {
+ if (EmbeddedWorkerTestHelper::OnMessageToWorker(
+ thread_id, embedded_worker_id, message)) {
+ return true;
+ }
+ current_embedded_worker_id_ = embedded_worker_id;
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(MessageReceiver, message)
+ IPC_MESSAGE_HANDLER(TestMsg_Message, OnMessage)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+ }
+
+ void SimulateSendValueToBrowser(int embedded_worker_id, int value) {
+ SimulateSend(new TestMsg_MessageFromWorker(embedded_worker_id, value));
+ }
+
+ private:
+ void OnMessage() {
+ // Do nothing.
+ }
+
+ int current_embedded_worker_id_;
+ DISALLOW_COPY_AND_ASSIGN(MessageReceiver);
+};
+
+void VerifyCalled(bool* called) {
+ *called = true;
+}
+
+void ObserveStatusChanges(ServiceWorkerVersion* version,
+ std::vector<ServiceWorkerVersion::Status>* statuses) {
+ statuses->push_back(version->status());
+ version->RegisterStatusChangeCallback(
+ base::Bind(&ObserveStatusChanges, base::Unretained(version), statuses));
+}
+
+// A specialized listener class to receive test messages from a worker.
+class MessageReceiverFromWorker : public EmbeddedWorkerInstance::Listener {
+ public:
+ explicit MessageReceiverFromWorker(EmbeddedWorkerInstance* instance)
+ : instance_(instance) {
+ instance_->AddListener(this);
+ }
+ virtual ~MessageReceiverFromWorker() {
+ instance_->RemoveListener(this);
+ }
+
+ virtual void OnStarted() OVERRIDE { NOTREACHED(); }
+ virtual void OnStopped() OVERRIDE { NOTREACHED(); }
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(MessageReceiverFromWorker, message)
+ IPC_MESSAGE_HANDLER(TestMsg_MessageFromWorker, OnMessageFromWorker)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+ }
+
+ void OnMessageFromWorker(int value) { received_values_.push_back(value); }
+ const std::vector<int>& received_values() const { return received_values_; }
+
+ private:
+ EmbeddedWorkerInstance* instance_;
+ std::vector<int> received_values_;
+ DISALLOW_COPY_AND_ASSIGN(MessageReceiverFromWorker);
+};
+
+} // namespace
+
+class ServiceWorkerVersionTest : public testing::Test {
+ protected:
+ ServiceWorkerVersionTest()
+ : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
+
+ virtual void SetUp() OVERRIDE {
+ helper_.reset(new MessageReceiver());
+
+ registration_ = new ServiceWorkerRegistration(
+ GURL("http://www.example.com/*"),
+ GURL("http://www.example.com/service_worker.js"),
+ 1L,
+ helper_->context()->AsWeakPtr());
+ version_ = new ServiceWorkerVersion(
+ registration_, 1L, helper_->context()->AsWeakPtr());
+
+ // Simulate adding one process to the worker.
+ int embedded_worker_id = version_->embedded_worker()->embedded_worker_id();
+ helper_->SimulateAddProcessToWorker(embedded_worker_id, kRenderProcessId);
+ ASSERT_TRUE(version_->HasProcessToRun());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ version_ = 0;
+ registration_ = 0;
+ helper_.reset();
+ }
+
+ TestBrowserThreadBundle thread_bundle_;
+ scoped_ptr<MessageReceiver> helper_;
+ scoped_refptr<ServiceWorkerRegistration> registration_;
+ scoped_refptr<ServiceWorkerVersion> version_;
+ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerVersionTest);
+};
+
+TEST_F(ServiceWorkerVersionTest, ConcurrentStartAndStop) {
+ // Call StartWorker() multiple times.
+ ServiceWorkerStatusCode status1 = SERVICE_WORKER_ERROR_FAILED;
+ ServiceWorkerStatusCode status2 = SERVICE_WORKER_ERROR_FAILED;
+ ServiceWorkerStatusCode status3 = SERVICE_WORKER_ERROR_FAILED;
+ version_->StartWorker(CreateReceiverOnCurrentThread(&status1));
+ version_->StartWorker(CreateReceiverOnCurrentThread(&status2));
+
+ EXPECT_EQ(ServiceWorkerVersion::STARTING, version_->running_status());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
+
+ // Call StartWorker() after it's started.
+ version_->StartWorker(CreateReceiverOnCurrentThread(&status3));
+ base::RunLoop().RunUntilIdle();
+
+ // All should just succeed.
+ EXPECT_EQ(SERVICE_WORKER_OK, status1);
+ EXPECT_EQ(SERVICE_WORKER_OK, status2);
+ EXPECT_EQ(SERVICE_WORKER_OK, status3);
+
+ // Call StopWorker() multiple times.
+ status1 = SERVICE_WORKER_ERROR_FAILED;
+ status2 = SERVICE_WORKER_ERROR_FAILED;
+ status3 = SERVICE_WORKER_ERROR_FAILED;
+ version_->StopWorker(CreateReceiverOnCurrentThread(&status1));
+ version_->StopWorker(CreateReceiverOnCurrentThread(&status2));
+
+ // Also try calling StartWorker while StopWorker is in queue.
+ version_->StartWorker(CreateReceiverOnCurrentThread(&status3));
+
+ EXPECT_EQ(ServiceWorkerVersion::STOPPING, version_->running_status());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status());
+
+ // All StopWorker should just succeed, while StartWorker fails.
+ EXPECT_EQ(SERVICE_WORKER_OK, status1);
+ EXPECT_EQ(SERVICE_WORKER_OK, status2);
+ EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, status3);
+}
+
+TEST_F(ServiceWorkerVersionTest, SendMessage) {
+ EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status());
+
+ // Send a message without starting the worker.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ version_->SendMessage(TestMsg_Message(),
+ CreateReceiverOnCurrentThread(&status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+
+ // The worker should be now started.
+ EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
+
+ // Stop the worker, and then send the message immediately.
+ ServiceWorkerStatusCode msg_status = SERVICE_WORKER_ERROR_FAILED;
+ ServiceWorkerStatusCode stop_status = SERVICE_WORKER_ERROR_FAILED;
+ version_->StopWorker(CreateReceiverOnCurrentThread(&stop_status));
+ version_->SendMessage(TestMsg_Message(),
+ CreateReceiverOnCurrentThread(&msg_status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, stop_status);
+
+ // SendMessage should return START_WORKER_FAILED error since it tried to
+ // start a worker while it was stopping.
+ EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, msg_status);
+}
+
+TEST_F(ServiceWorkerVersionTest, ReSendMessageAfterStop) {
+ EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status());
+
+ // Start the worker.
+ ServiceWorkerStatusCode start_status = SERVICE_WORKER_ERROR_FAILED;
+ version_->StartWorker(CreateReceiverOnCurrentThread(&start_status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, start_status);
+ EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
+
+ // Stop the worker, and then send the message immediately.
+ ServiceWorkerStatusCode msg_status = SERVICE_WORKER_ERROR_FAILED;
+ ServiceWorkerStatusCode stop_status = SERVICE_WORKER_ERROR_FAILED;
+ version_->StopWorker(CreateReceiverOnCurrentThread(&stop_status));
+ version_->SendMessage(TestMsg_Message(),
+ CreateReceiverOnCurrentThread(&msg_status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, stop_status);
+
+ // SendMessage should return START_WORKER_FAILED error since it tried to
+ // start a worker while it was stopping.
+ EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, msg_status);
+
+ // Resend the message, which should succeed and restart the worker.
+ version_->SendMessage(TestMsg_Message(),
+ CreateReceiverOnCurrentThread(&msg_status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, msg_status);
+ EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
+}
+
+TEST_F(ServiceWorkerVersionTest, ReceiveMessageFromWorker) {
+ MessageReceiverFromWorker receiver(version_->embedded_worker());
+
+ // Simulate sending some dummy values from the worker.
+ helper_->SimulateSendValueToBrowser(
+ version_->embedded_worker()->embedded_worker_id(), 555);
+ helper_->SimulateSendValueToBrowser(
+ version_->embedded_worker()->embedded_worker_id(), 777);
+
+ // Verify the receiver received the values.
+ ASSERT_EQ(2U, receiver.received_values().size());
+ EXPECT_EQ(555, receiver.received_values()[0]);
+ EXPECT_EQ(777, receiver.received_values()[1]);
+}
+
+TEST_F(ServiceWorkerVersionTest, InstallAndWaitCompletion) {
+ EXPECT_EQ(ServiceWorkerVersion::NEW, version_->status());
+
+ // Dispatch an install event.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ version_->DispatchInstallEvent(-1, CreateReceiverOnCurrentThread(&status));
+
+ // Wait for the completion.
+ bool status_change_called = false;
+ version_->RegisterStatusChangeCallback(
+ base::Bind(&VerifyCalled, &status_change_called));
+
+ base::RunLoop().RunUntilIdle();
+
+ // After successful completion, version's status must be changed to
+ // INSTALLED, and status change callback must have been fired.
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+ EXPECT_TRUE(status_change_called);
+ EXPECT_EQ(ServiceWorkerVersion::INSTALLED, version_->status());
+}
+
+TEST_F(ServiceWorkerVersionTest, ActivateAndWaitCompletion) {
+ version_->SetStatus(ServiceWorkerVersion::INSTALLED);
+ EXPECT_EQ(ServiceWorkerVersion::INSTALLED, version_->status());
+
+ // Dispatch an activate event.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ version_->DispatchActivateEvent(CreateReceiverOnCurrentThread(&status));
+
+ // Wait for the completion.
+ bool status_change_called = false;
+ version_->RegisterStatusChangeCallback(
+ base::Bind(&VerifyCalled, &status_change_called));
+
+ base::RunLoop().RunUntilIdle();
+
+ // After successful completion, version's status must be changed to
+ // ACTIVE, and status change callback must have been fired.
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+ EXPECT_TRUE(status_change_called);
+ EXPECT_EQ(ServiceWorkerVersion::ACTIVE, version_->status());
+}
+
+TEST_F(ServiceWorkerVersionTest, RepeatedlyObserveStatusChanges) {
+ EXPECT_EQ(ServiceWorkerVersion::NEW, version_->status());
+
+ // Repeatedly observe status changes (the callback re-registers itself).
+ std::vector<ServiceWorkerVersion::Status> statuses;
+ version_->RegisterStatusChangeCallback(
+ base::Bind(&ObserveStatusChanges, version_, &statuses));
+
+ // Dispatch some events.
+ ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
+ version_->DispatchInstallEvent(-1, CreateReceiverOnCurrentThread(&status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+
+ status = SERVICE_WORKER_ERROR_FAILED;
+ version_->DispatchActivateEvent(CreateReceiverOnCurrentThread(&status));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(SERVICE_WORKER_OK, status);
+
+ // Verify that we could successfully observe repeated status changes.
+ ASSERT_EQ(4U, statuses.size());
+ ASSERT_EQ(ServiceWorkerVersion::INSTALLING, statuses[0]);
+ ASSERT_EQ(ServiceWorkerVersion::INSTALLED, statuses[1]);
+ ASSERT_EQ(ServiceWorkerVersion::ACTIVATING, statuses[2]);
+ ASSERT_EQ(ServiceWorkerVersion::ACTIVE, statuses[3]);
+}
+
+TEST_F(ServiceWorkerVersionTest, AddAndRemoveProcesses) {
+ // Preparation (to reset the process count to 0).
+ ASSERT_TRUE(version_->HasProcessToRun());
+ version_->RemoveProcessFromWorker(kRenderProcessId);
+ ASSERT_FALSE(version_->HasProcessToRun());
+
+ // Add another process to the worker twice, and then remove process once.
+ const int another_process_id = kRenderProcessId + 1;
+ version_->AddProcessToWorker(another_process_id);
+ version_->AddProcessToWorker(another_process_id);
+ version_->RemoveProcessFromWorker(another_process_id);
+
+ // We're ref-counting the process internally, so adding the same process
+ // multiple times should be handled correctly.
+ ASSERT_TRUE(version_->HasProcessToRun());
+
+ // Removing the process again (so that # of AddProcess == # of RemoveProcess
+ // for the process) should remove all process references.
+ version_->RemoveProcessFromWorker(another_process_id);
+ ASSERT_FALSE(version_->HasProcessToRun());
+}
+
+} // namespace content