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 pattern_ = GURL("http://www.example.com/"); 123 registration_ = new ServiceWorkerRegistration( 124 pattern_, 125 1L, 126 helper_->context()->AsWeakPtr()); 127 version_ = new ServiceWorkerVersion( 128 registration_.get(), 129 GURL("http://www.example.com/service_worker.js"), 130 1L, 131 helper_->context()->AsWeakPtr()); 132 133 // Simulate adding one process to the pattern. 134 helper_->SimulateAddProcessToPattern(pattern_, kRenderProcessId); 135 ASSERT_TRUE(helper_->context()->process_manager() 136 ->PatternHasProcessToRun(pattern_)); 137 } 138 139 virtual void TearDown() OVERRIDE { 140 version_ = 0; 141 registration_ = 0; 142 helper_.reset(); 143 } 144 145 TestBrowserThreadBundle thread_bundle_; 146 scoped_ptr<MessageReceiver> helper_; 147 scoped_refptr<ServiceWorkerRegistration> registration_; 148 scoped_refptr<ServiceWorkerVersion> version_; 149 GURL pattern_; 150 151 private: 152 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerVersionTest); 153}; 154 155TEST_F(ServiceWorkerVersionTest, ConcurrentStartAndStop) { 156 // Call StartWorker() multiple times. 157 ServiceWorkerStatusCode status1 = SERVICE_WORKER_ERROR_FAILED; 158 ServiceWorkerStatusCode status2 = SERVICE_WORKER_ERROR_FAILED; 159 ServiceWorkerStatusCode status3 = SERVICE_WORKER_ERROR_FAILED; 160 version_->StartWorker(CreateReceiverOnCurrentThread(&status1)); 161 version_->StartWorker(CreateReceiverOnCurrentThread(&status2)); 162 163 EXPECT_EQ(ServiceWorkerVersion::STARTING, version_->running_status()); 164 base::RunLoop().RunUntilIdle(); 165 EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status()); 166 167 // Call StartWorker() after it's started. 168 version_->StartWorker(CreateReceiverOnCurrentThread(&status3)); 169 base::RunLoop().RunUntilIdle(); 170 171 // All should just succeed. 172 EXPECT_EQ(SERVICE_WORKER_OK, status1); 173 EXPECT_EQ(SERVICE_WORKER_OK, status2); 174 EXPECT_EQ(SERVICE_WORKER_OK, status3); 175 176 // Call StopWorker() multiple times. 177 status1 = SERVICE_WORKER_ERROR_FAILED; 178 status2 = SERVICE_WORKER_ERROR_FAILED; 179 status3 = SERVICE_WORKER_ERROR_FAILED; 180 version_->StopWorker(CreateReceiverOnCurrentThread(&status1)); 181 version_->StopWorker(CreateReceiverOnCurrentThread(&status2)); 182 183 // Also try calling StartWorker while StopWorker is in queue. 184 version_->StartWorker(CreateReceiverOnCurrentThread(&status3)); 185 186 EXPECT_EQ(ServiceWorkerVersion::STOPPING, version_->running_status()); 187 base::RunLoop().RunUntilIdle(); 188 EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status()); 189 190 // All StopWorker should just succeed, while StartWorker fails. 191 EXPECT_EQ(SERVICE_WORKER_OK, status1); 192 EXPECT_EQ(SERVICE_WORKER_OK, status2); 193 EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, status3); 194} 195 196TEST_F(ServiceWorkerVersionTest, SendMessage) { 197 EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status()); 198 199 // Send a message without starting the worker. 200 ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; 201 version_->SendMessage(TestMsg_Message(), 202 CreateReceiverOnCurrentThread(&status)); 203 base::RunLoop().RunUntilIdle(); 204 EXPECT_EQ(SERVICE_WORKER_OK, status); 205 206 // The worker should be now started. 207 EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status()); 208 209 // Stop the worker, and then send the message immediately. 210 ServiceWorkerStatusCode msg_status = SERVICE_WORKER_ERROR_FAILED; 211 ServiceWorkerStatusCode stop_status = SERVICE_WORKER_ERROR_FAILED; 212 version_->StopWorker(CreateReceiverOnCurrentThread(&stop_status)); 213 version_->SendMessage(TestMsg_Message(), 214 CreateReceiverOnCurrentThread(&msg_status)); 215 base::RunLoop().RunUntilIdle(); 216 EXPECT_EQ(SERVICE_WORKER_OK, stop_status); 217 218 // SendMessage should return START_WORKER_FAILED error since it tried to 219 // start a worker while it was stopping. 220 EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, msg_status); 221} 222 223TEST_F(ServiceWorkerVersionTest, ReSendMessageAfterStop) { 224 EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status()); 225 226 // Start the worker. 227 ServiceWorkerStatusCode start_status = SERVICE_WORKER_ERROR_FAILED; 228 version_->StartWorker(CreateReceiverOnCurrentThread(&start_status)); 229 base::RunLoop().RunUntilIdle(); 230 EXPECT_EQ(SERVICE_WORKER_OK, start_status); 231 EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status()); 232 233 // Stop the worker, and then send the message immediately. 234 ServiceWorkerStatusCode msg_status = SERVICE_WORKER_ERROR_FAILED; 235 ServiceWorkerStatusCode stop_status = SERVICE_WORKER_ERROR_FAILED; 236 version_->StopWorker(CreateReceiverOnCurrentThread(&stop_status)); 237 version_->SendMessage(TestMsg_Message(), 238 CreateReceiverOnCurrentThread(&msg_status)); 239 base::RunLoop().RunUntilIdle(); 240 EXPECT_EQ(SERVICE_WORKER_OK, stop_status); 241 242 // SendMessage should return START_WORKER_FAILED error since it tried to 243 // start a worker while it was stopping. 244 EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, msg_status); 245 246 // Resend the message, which should succeed and restart the worker. 247 version_->SendMessage(TestMsg_Message(), 248 CreateReceiverOnCurrentThread(&msg_status)); 249 base::RunLoop().RunUntilIdle(); 250 EXPECT_EQ(SERVICE_WORKER_OK, msg_status); 251 EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status()); 252} 253 254TEST_F(ServiceWorkerVersionTest, ReceiveMessageFromWorker) { 255 MessageReceiverFromWorker receiver(version_->embedded_worker()); 256 257 // Simulate sending some dummy values from the worker. 258 helper_->SimulateSendValueToBrowser( 259 version_->embedded_worker()->embedded_worker_id(), 555); 260 helper_->SimulateSendValueToBrowser( 261 version_->embedded_worker()->embedded_worker_id(), 777); 262 263 // Verify the receiver received the values. 264 ASSERT_EQ(2U, receiver.received_values().size()); 265 EXPECT_EQ(555, receiver.received_values()[0]); 266 EXPECT_EQ(777, receiver.received_values()[1]); 267} 268 269TEST_F(ServiceWorkerVersionTest, InstallAndWaitCompletion) { 270 version_->SetStatus(ServiceWorkerVersion::INSTALLING); 271 272 // Dispatch an install event. 273 ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; 274 version_->DispatchInstallEvent(-1, CreateReceiverOnCurrentThread(&status)); 275 276 // Wait for the completion. 277 bool status_change_called = false; 278 version_->RegisterStatusChangeCallback( 279 base::Bind(&VerifyCalled, &status_change_called)); 280 281 base::RunLoop().RunUntilIdle(); 282 283 // Version's status must not have changed during installation. 284 EXPECT_EQ(SERVICE_WORKER_OK, status); 285 EXPECT_FALSE(status_change_called); 286 EXPECT_EQ(ServiceWorkerVersion::INSTALLING, version_->status()); 287} 288 289TEST_F(ServiceWorkerVersionTest, ActivateAndWaitCompletion) { 290 version_->SetStatus(ServiceWorkerVersion::ACTIVATING); 291 292 // Dispatch an activate event. 293 ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; 294 version_->DispatchActivateEvent(CreateReceiverOnCurrentThread(&status)); 295 296 // Wait for the completion. 297 bool status_change_called = false; 298 version_->RegisterStatusChangeCallback( 299 base::Bind(&VerifyCalled, &status_change_called)); 300 301 base::RunLoop().RunUntilIdle(); 302 303 // Version's status must not have changed during activation. 304 EXPECT_EQ(SERVICE_WORKER_OK, status); 305 EXPECT_FALSE(status_change_called); 306 EXPECT_EQ(ServiceWorkerVersion::ACTIVATING, version_->status()); 307} 308 309TEST_F(ServiceWorkerVersionTest, RepeatedlyObserveStatusChanges) { 310 EXPECT_EQ(ServiceWorkerVersion::NEW, version_->status()); 311 312 // Repeatedly observe status changes (the callback re-registers itself). 313 std::vector<ServiceWorkerVersion::Status> statuses; 314 version_->RegisterStatusChangeCallback( 315 base::Bind(&ObserveStatusChanges, version_, &statuses)); 316 317 version_->SetStatus(ServiceWorkerVersion::INSTALLING); 318 version_->SetStatus(ServiceWorkerVersion::INSTALLED); 319 version_->SetStatus(ServiceWorkerVersion::ACTIVATING); 320 version_->SetStatus(ServiceWorkerVersion::ACTIVATED); 321 version_->SetStatus(ServiceWorkerVersion::REDUNDANT); 322 323 // Verify that we could successfully observe repeated status changes. 324 ASSERT_EQ(5U, statuses.size()); 325 ASSERT_EQ(ServiceWorkerVersion::INSTALLING, statuses[0]); 326 ASSERT_EQ(ServiceWorkerVersion::INSTALLED, statuses[1]); 327 ASSERT_EQ(ServiceWorkerVersion::ACTIVATING, statuses[2]); 328 ASSERT_EQ(ServiceWorkerVersion::ACTIVATED, statuses[3]); 329 ASSERT_EQ(ServiceWorkerVersion::REDUNDANT, statuses[4]); 330} 331 332TEST_F(ServiceWorkerVersionTest, ScheduleStopWorker) { 333 // Verify the timer is not running when version initializes its status. 334 version_->SetStatus(ServiceWorkerVersion::ACTIVATED); 335 EXPECT_FALSE(version_->stop_worker_timer_.IsRunning()); 336 337 // Verify the timer is running when version status changes frome ACTIVATING 338 // to ACTIVATED. 339 ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; 340 version_->StartWorker(CreateReceiverOnCurrentThread(&status)); 341 base::RunLoop().RunUntilIdle(); 342 EXPECT_EQ(SERVICE_WORKER_OK, status); 343 version_->SetStatus(ServiceWorkerVersion::ACTIVATING); 344 version_->SetStatus(ServiceWorkerVersion::ACTIVATED); 345 EXPECT_TRUE(version_->stop_worker_timer_.IsRunning()); 346 347 // The timer should be running if the worker is restarted without controllee. 348 status = SERVICE_WORKER_ERROR_FAILED; 349 version_->StopWorker(CreateReceiverOnCurrentThread(&status)); 350 base::RunLoop().RunUntilIdle(); 351 EXPECT_EQ(SERVICE_WORKER_OK, status); 352 status = SERVICE_WORKER_ERROR_FAILED; 353 version_->StartWorker(CreateReceiverOnCurrentThread(&status)); 354 base::RunLoop().RunUntilIdle(); 355 EXPECT_EQ(SERVICE_WORKER_OK, status); 356 EXPECT_TRUE(version_->stop_worker_timer_.IsRunning()); 357 358 // The timer should not be running if a controllee is added. 359 scoped_ptr<ServiceWorkerProviderHost> host( 360 new ServiceWorkerProviderHost(33 /* dummy render process id */, 361 1 /* dummy provider_id */, 362 helper_->context()->AsWeakPtr(), 363 NULL)); 364 version_->AddControllee(host.get()); 365 EXPECT_FALSE(version_->stop_worker_timer_.IsRunning()); 366 367 // The timer should be running if the controllee is removed. 368 version_->RemoveControllee(host.get()); 369 EXPECT_TRUE(version_->stop_worker_timer_.IsRunning()); 370} 371 372} // namespace content 373