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