1// Copyright 2014 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/basictypes.h" 6#include "base/run_loop.h" 7#include "content/browser/service_worker/embedded_worker_registry.h" 8#include "content/browser/service_worker/embedded_worker_test_helper.h" 9#include "content/browser/service_worker/service_worker_context_core.h" 10#include "content/browser/service_worker/service_worker_registration.h" 11#include "content/browser/service_worker/service_worker_test_utils.h" 12#include "content/browser/service_worker/service_worker_version.h" 13#include "content/public/test/test_browser_thread_bundle.h" 14#include "testing/gtest/include/gtest/gtest.h" 15 16// IPC messages for testing --------------------------------------------------- 17 18#define IPC_MESSAGE_IMPL 19#include "ipc/ipc_message_macros.h" 20 21#define IPC_MESSAGE_START TestMsgStart 22 23IPC_MESSAGE_CONTROL0(TestMsg_Message); 24IPC_MESSAGE_ROUTED1(TestMsg_MessageFromWorker, int); 25 26// --------------------------------------------------------------------------- 27 28namespace content { 29 30namespace { 31 32static const int kRenderProcessId = 1; 33 34class MessageReceiver : public EmbeddedWorkerTestHelper { 35 public: 36 MessageReceiver() 37 : EmbeddedWorkerTestHelper(kRenderProcessId), 38 current_embedded_worker_id_(0) {} 39 virtual ~MessageReceiver() {} 40 41 virtual bool OnMessageToWorker(int thread_id, 42 int embedded_worker_id, 43 const IPC::Message& message) OVERRIDE { 44 if (EmbeddedWorkerTestHelper::OnMessageToWorker( 45 thread_id, embedded_worker_id, message)) { 46 return true; 47 } 48 current_embedded_worker_id_ = embedded_worker_id; 49 bool handled = true; 50 IPC_BEGIN_MESSAGE_MAP(MessageReceiver, message) 51 IPC_MESSAGE_HANDLER(TestMsg_Message, OnMessage) 52 IPC_MESSAGE_UNHANDLED(handled = false) 53 IPC_END_MESSAGE_MAP() 54 return handled; 55 } 56 57 void SimulateSendValueToBrowser(int embedded_worker_id, int value) { 58 SimulateSend(new TestMsg_MessageFromWorker(embedded_worker_id, value)); 59 } 60 61 private: 62 void OnMessage() { 63 // Do nothing. 64 } 65 66 int current_embedded_worker_id_; 67 DISALLOW_COPY_AND_ASSIGN(MessageReceiver); 68}; 69 70void VerifyCalled(bool* called) { 71 *called = true; 72} 73 74void ObserveStatusChanges(ServiceWorkerVersion* version, 75 std::vector<ServiceWorkerVersion::Status>* statuses) { 76 statuses->push_back(version->status()); 77 version->RegisterStatusChangeCallback( 78 base::Bind(&ObserveStatusChanges, base::Unretained(version), statuses)); 79} 80 81// A specialized listener class to receive test messages from a worker. 82class MessageReceiverFromWorker : public EmbeddedWorkerInstance::Listener { 83 public: 84 explicit MessageReceiverFromWorker(EmbeddedWorkerInstance* instance) 85 : instance_(instance) { 86 instance_->AddListener(this); 87 } 88 virtual ~MessageReceiverFromWorker() { 89 instance_->RemoveListener(this); 90 } 91 92 virtual void OnStarted() OVERRIDE { NOTREACHED(); } 93 virtual void OnStopped() OVERRIDE { NOTREACHED(); } 94 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 95 bool handled = true; 96 IPC_BEGIN_MESSAGE_MAP(MessageReceiverFromWorker, message) 97 IPC_MESSAGE_HANDLER(TestMsg_MessageFromWorker, OnMessageFromWorker) 98 IPC_MESSAGE_UNHANDLED(handled = false) 99 IPC_END_MESSAGE_MAP() 100 return handled; 101 } 102 103 void OnMessageFromWorker(int value) { received_values_.push_back(value); } 104 const std::vector<int>& received_values() const { return received_values_; } 105 106 private: 107 EmbeddedWorkerInstance* instance_; 108 std::vector<int> received_values_; 109 DISALLOW_COPY_AND_ASSIGN(MessageReceiverFromWorker); 110}; 111 112} // namespace 113 114class ServiceWorkerVersionTest : public testing::Test { 115 protected: 116 ServiceWorkerVersionTest() 117 : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {} 118 119 virtual void SetUp() OVERRIDE { 120 helper_.reset(new MessageReceiver()); 121 122 registration_ = new ServiceWorkerRegistration( 123 GURL("http://www.example.com/*"), 124 GURL("http://www.example.com/service_worker.js"), 125 1L, 126 helper_->context()->AsWeakPtr()); 127 version_ = new ServiceWorkerVersion( 128 registration_, 1L, helper_->context()->AsWeakPtr()); 129 130 // Simulate adding one process to the worker. 131 int embedded_worker_id = version_->embedded_worker()->embedded_worker_id(); 132 helper_->SimulateAddProcessToWorker(embedded_worker_id, kRenderProcessId); 133 ASSERT_TRUE(version_->HasProcessToRun()); 134 } 135 136 virtual void TearDown() OVERRIDE { 137 version_ = 0; 138 registration_ = 0; 139 helper_.reset(); 140 } 141 142 TestBrowserThreadBundle thread_bundle_; 143 scoped_ptr<MessageReceiver> helper_; 144 scoped_refptr<ServiceWorkerRegistration> registration_; 145 scoped_refptr<ServiceWorkerVersion> version_; 146 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerVersionTest); 147}; 148 149TEST_F(ServiceWorkerVersionTest, ConcurrentStartAndStop) { 150 // Call StartWorker() multiple times. 151 ServiceWorkerStatusCode status1 = SERVICE_WORKER_ERROR_FAILED; 152 ServiceWorkerStatusCode status2 = SERVICE_WORKER_ERROR_FAILED; 153 ServiceWorkerStatusCode status3 = SERVICE_WORKER_ERROR_FAILED; 154 version_->StartWorker(CreateReceiverOnCurrentThread(&status1)); 155 version_->StartWorker(CreateReceiverOnCurrentThread(&status2)); 156 157 EXPECT_EQ(ServiceWorkerVersion::STARTING, version_->running_status()); 158 base::RunLoop().RunUntilIdle(); 159 EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status()); 160 161 // Call StartWorker() after it's started. 162 version_->StartWorker(CreateReceiverOnCurrentThread(&status3)); 163 base::RunLoop().RunUntilIdle(); 164 165 // All should just succeed. 166 EXPECT_EQ(SERVICE_WORKER_OK, status1); 167 EXPECT_EQ(SERVICE_WORKER_OK, status2); 168 EXPECT_EQ(SERVICE_WORKER_OK, status3); 169 170 // Call StopWorker() multiple times. 171 status1 = SERVICE_WORKER_ERROR_FAILED; 172 status2 = SERVICE_WORKER_ERROR_FAILED; 173 status3 = SERVICE_WORKER_ERROR_FAILED; 174 version_->StopWorker(CreateReceiverOnCurrentThread(&status1)); 175 version_->StopWorker(CreateReceiverOnCurrentThread(&status2)); 176 177 // Also try calling StartWorker while StopWorker is in queue. 178 version_->StartWorker(CreateReceiverOnCurrentThread(&status3)); 179 180 EXPECT_EQ(ServiceWorkerVersion::STOPPING, version_->running_status()); 181 base::RunLoop().RunUntilIdle(); 182 EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status()); 183 184 // All StopWorker should just succeed, while StartWorker fails. 185 EXPECT_EQ(SERVICE_WORKER_OK, status1); 186 EXPECT_EQ(SERVICE_WORKER_OK, status2); 187 EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, status3); 188} 189 190TEST_F(ServiceWorkerVersionTest, SendMessage) { 191 EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status()); 192 193 // Send a message without starting the worker. 194 ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; 195 version_->SendMessage(TestMsg_Message(), 196 CreateReceiverOnCurrentThread(&status)); 197 base::RunLoop().RunUntilIdle(); 198 EXPECT_EQ(SERVICE_WORKER_OK, status); 199 200 // The worker should be now started. 201 EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status()); 202 203 // Stop the worker, and then send the message immediately. 204 ServiceWorkerStatusCode msg_status = SERVICE_WORKER_ERROR_FAILED; 205 ServiceWorkerStatusCode stop_status = SERVICE_WORKER_ERROR_FAILED; 206 version_->StopWorker(CreateReceiverOnCurrentThread(&stop_status)); 207 version_->SendMessage(TestMsg_Message(), 208 CreateReceiverOnCurrentThread(&msg_status)); 209 base::RunLoop().RunUntilIdle(); 210 EXPECT_EQ(SERVICE_WORKER_OK, stop_status); 211 212 // SendMessage should return START_WORKER_FAILED error since it tried to 213 // start a worker while it was stopping. 214 EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, msg_status); 215} 216 217TEST_F(ServiceWorkerVersionTest, ReSendMessageAfterStop) { 218 EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status()); 219 220 // Start the worker. 221 ServiceWorkerStatusCode start_status = SERVICE_WORKER_ERROR_FAILED; 222 version_->StartWorker(CreateReceiverOnCurrentThread(&start_status)); 223 base::RunLoop().RunUntilIdle(); 224 EXPECT_EQ(SERVICE_WORKER_OK, start_status); 225 EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status()); 226 227 // Stop the worker, and then send the message immediately. 228 ServiceWorkerStatusCode msg_status = SERVICE_WORKER_ERROR_FAILED; 229 ServiceWorkerStatusCode stop_status = SERVICE_WORKER_ERROR_FAILED; 230 version_->StopWorker(CreateReceiverOnCurrentThread(&stop_status)); 231 version_->SendMessage(TestMsg_Message(), 232 CreateReceiverOnCurrentThread(&msg_status)); 233 base::RunLoop().RunUntilIdle(); 234 EXPECT_EQ(SERVICE_WORKER_OK, stop_status); 235 236 // SendMessage should return START_WORKER_FAILED error since it tried to 237 // start a worker while it was stopping. 238 EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, msg_status); 239 240 // Resend the message, which should succeed and restart the worker. 241 version_->SendMessage(TestMsg_Message(), 242 CreateReceiverOnCurrentThread(&msg_status)); 243 base::RunLoop().RunUntilIdle(); 244 EXPECT_EQ(SERVICE_WORKER_OK, msg_status); 245 EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status()); 246} 247 248TEST_F(ServiceWorkerVersionTest, ReceiveMessageFromWorker) { 249 MessageReceiverFromWorker receiver(version_->embedded_worker()); 250 251 // Simulate sending some dummy values from the worker. 252 helper_->SimulateSendValueToBrowser( 253 version_->embedded_worker()->embedded_worker_id(), 555); 254 helper_->SimulateSendValueToBrowser( 255 version_->embedded_worker()->embedded_worker_id(), 777); 256 257 // Verify the receiver received the values. 258 ASSERT_EQ(2U, receiver.received_values().size()); 259 EXPECT_EQ(555, receiver.received_values()[0]); 260 EXPECT_EQ(777, receiver.received_values()[1]); 261} 262 263TEST_F(ServiceWorkerVersionTest, InstallAndWaitCompletion) { 264 EXPECT_EQ(ServiceWorkerVersion::NEW, version_->status()); 265 266 // Dispatch an install event. 267 ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; 268 version_->DispatchInstallEvent(-1, CreateReceiverOnCurrentThread(&status)); 269 270 // Wait for the completion. 271 bool status_change_called = false; 272 version_->RegisterStatusChangeCallback( 273 base::Bind(&VerifyCalled, &status_change_called)); 274 275 base::RunLoop().RunUntilIdle(); 276 277 // After successful completion, version's status must be changed to 278 // INSTALLED, and status change callback must have been fired. 279 EXPECT_EQ(SERVICE_WORKER_OK, status); 280 EXPECT_TRUE(status_change_called); 281 EXPECT_EQ(ServiceWorkerVersion::INSTALLED, version_->status()); 282} 283 284TEST_F(ServiceWorkerVersionTest, ActivateAndWaitCompletion) { 285 version_->SetStatus(ServiceWorkerVersion::INSTALLED); 286 EXPECT_EQ(ServiceWorkerVersion::INSTALLED, version_->status()); 287 288 // Dispatch an activate event. 289 ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; 290 version_->DispatchActivateEvent(CreateReceiverOnCurrentThread(&status)); 291 292 // Wait for the completion. 293 bool status_change_called = false; 294 version_->RegisterStatusChangeCallback( 295 base::Bind(&VerifyCalled, &status_change_called)); 296 297 base::RunLoop().RunUntilIdle(); 298 299 // After successful completion, version's status must be changed to 300 // ACTIVE, and status change callback must have been fired. 301 EXPECT_EQ(SERVICE_WORKER_OK, status); 302 EXPECT_TRUE(status_change_called); 303 EXPECT_EQ(ServiceWorkerVersion::ACTIVE, version_->status()); 304} 305 306TEST_F(ServiceWorkerVersionTest, RepeatedlyObserveStatusChanges) { 307 EXPECT_EQ(ServiceWorkerVersion::NEW, version_->status()); 308 309 // Repeatedly observe status changes (the callback re-registers itself). 310 std::vector<ServiceWorkerVersion::Status> statuses; 311 version_->RegisterStatusChangeCallback( 312 base::Bind(&ObserveStatusChanges, version_, &statuses)); 313 314 // Dispatch some events. 315 ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; 316 version_->DispatchInstallEvent(-1, CreateReceiverOnCurrentThread(&status)); 317 base::RunLoop().RunUntilIdle(); 318 EXPECT_EQ(SERVICE_WORKER_OK, status); 319 320 status = SERVICE_WORKER_ERROR_FAILED; 321 version_->DispatchActivateEvent(CreateReceiverOnCurrentThread(&status)); 322 base::RunLoop().RunUntilIdle(); 323 EXPECT_EQ(SERVICE_WORKER_OK, status); 324 325 // Verify that we could successfully observe repeated status changes. 326 ASSERT_EQ(4U, statuses.size()); 327 ASSERT_EQ(ServiceWorkerVersion::INSTALLING, statuses[0]); 328 ASSERT_EQ(ServiceWorkerVersion::INSTALLED, statuses[1]); 329 ASSERT_EQ(ServiceWorkerVersion::ACTIVATING, statuses[2]); 330 ASSERT_EQ(ServiceWorkerVersion::ACTIVE, statuses[3]); 331} 332 333TEST_F(ServiceWorkerVersionTest, AddAndRemoveProcesses) { 334 // Preparation (to reset the process count to 0). 335 ASSERT_TRUE(version_->HasProcessToRun()); 336 version_->RemoveProcessFromWorker(kRenderProcessId); 337 ASSERT_FALSE(version_->HasProcessToRun()); 338 339 // Add another process to the worker twice, and then remove process once. 340 const int another_process_id = kRenderProcessId + 1; 341 version_->AddProcessToWorker(another_process_id); 342 version_->AddProcessToWorker(another_process_id); 343 version_->RemoveProcessFromWorker(another_process_id); 344 345 // We're ref-counting the process internally, so adding the same process 346 // multiple times should be handled correctly. 347 ASSERT_TRUE(version_->HasProcessToRun()); 348 349 // Removing the process again (so that # of AddProcess == # of RemoveProcess 350 // for the process) should remove all process references. 351 version_->RemoveProcessFromWorker(another_process_id); 352 ASSERT_FALSE(version_->HasProcessToRun()); 353} 354 355} // namespace content 356