Chromium Code Reviews| Index: content/browser/service_worker/embedded_worker_instance_unittest.cc |
| diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc |
| index 9aebb0d246d79af357fbcd5e464d69f66d3631ad..4de6c3b6b8793e0a2c828383eeb04425b9815835 100644 |
| --- a/content/browser/service_worker/embedded_worker_instance_unittest.cc |
| +++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc |
| @@ -6,6 +6,7 @@ |
| #include <stdint.h> |
| #include <utility> |
| +#include <vector> |
| #include "base/macros.h" |
| #include "base/run_loop.h" |
| @@ -24,13 +25,6 @@ namespace content { |
| namespace { |
| -void DestroyWorker(scoped_ptr<EmbeddedWorkerInstance> worker, |
| - ServiceWorkerStatusCode* out_status, |
| - ServiceWorkerStatusCode status) { |
| - *out_status = status; |
| - worker.reset(); |
| -} |
| - |
| void SaveStatusAndCall(ServiceWorkerStatusCode* out, |
| const base::Closure& callback, |
| ServiceWorkerStatusCode status) { |
| @@ -46,14 +40,36 @@ class EmbeddedWorkerInstanceTest : public testing::Test, |
| EmbeddedWorkerInstanceTest() |
| : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {} |
| - void OnStopped(EmbeddedWorkerInstance::Status old_status) override { |
| - stopped_ = true; |
| - stopped_old_status_ = old_status; |
| + enum EventType { |
| + PROCESS_ALLOCATED, |
| + START_WORKER_MESSAGE_SENT, |
| + STARTED, |
| + STOPPED, |
| + DETACHED, |
| + }; |
| + |
| + struct EventLog { |
| + EventType type; |
| + EmbeddedWorkerInstance::Status status; |
| + }; |
| + |
| + void RecordEvent( |
| + EventType type, |
| + EmbeddedWorkerInstance::Status status = EmbeddedWorkerInstance::STOPPED) { |
| + EventLog log = {type, status}; |
| + events_.push_back(log); |
| } |
| + void OnProcessAllocated() override { RecordEvent(PROCESS_ALLOCATED); } |
| + void OnStartWorkerMessageSent() override { |
| + RecordEvent(START_WORKER_MESSAGE_SENT); |
| + } |
| + void OnStarted() override { RecordEvent(STARTED); } |
| + void OnStopped(EmbeddedWorkerInstance::Status old_status) override { |
| + RecordEvent(STOPPED, old_status); |
| + } |
| void OnDetached(EmbeddedWorkerInstance::Status old_status) override { |
| - detached_ = true; |
| - detached_old_status_ = old_status; |
| + RecordEvent(DETACHED, old_status); |
| } |
| bool OnMessageReceived(const IPC::Message& message) override { return false; } |
| @@ -86,17 +102,39 @@ class EmbeddedWorkerInstanceTest : public testing::Test, |
| TestBrowserThreadBundle thread_bundle_; |
| scoped_ptr<EmbeddedWorkerTestHelper> helper_; |
| - bool detached_ = false; |
| - EmbeddedWorkerInstance::Status detached_old_status_ = |
| - EmbeddedWorkerInstance::STOPPED; |
| - bool stopped_ = false; |
| - EmbeddedWorkerInstance::Status stopped_old_status_ = |
| - EmbeddedWorkerInstance::STOPPED; |
| + std::vector<EventLog> events_; |
| private: |
| DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerInstanceTest); |
| }; |
| +// A helper to simulate the start worker sequence is stalled in a worker |
| +// process. |
| +class StalledInStartWorkerHelper : public EmbeddedWorkerTestHelper { |
| + public: |
| + StalledInStartWorkerHelper() : EmbeddedWorkerTestHelper(base::FilePath()) {} |
| + ~StalledInStartWorkerHelper() override{}; |
| + |
| + void OnStartWorker(int embedded_worker_id, |
| + int64_t service_worker_version_id, |
| + const GURL& scope, |
| + const GURL& script_url) override { |
| + if (!use_default_behavior_) { |
| + // Do nothing to simulate a stall in the worker process. |
|
falken
2016/01/14 07:30:00
nit: The negatives and meaning of "default" are a
nhiroki
2016/01/14 08:43:40
Done.
|
| + return; |
| + } |
| + EmbeddedWorkerTestHelper::OnStartWorker( |
| + embedded_worker_id, service_worker_version_id, scope, script_url); |
| + } |
| + |
| + void set_use_default_behavior(bool use_default_behavior) { |
| + use_default_behavior_ = use_default_behavior; |
| + } |
| + |
| + private: |
| + bool use_default_behavior_ = false; |
| +}; |
| + |
| class FailToSendIPCHelper : public EmbeddedWorkerTestHelper { |
| public: |
| FailToSendIPCHelper() : EmbeddedWorkerTestHelper(base::FilePath()) {} |
| @@ -254,58 +292,177 @@ TEST_F(EmbeddedWorkerInstanceTest, RemoveWorkerInSharedProcess) { |
| worker2->Stop(); |
| } |
| -// Test detaching in the middle of the start worker sequence. |
| -TEST_F(EmbeddedWorkerInstanceTest, DetachDuringStart) { |
| +TEST_F(EmbeddedWorkerInstanceTest, DetachDuringProcessAllocation) { |
| + const int64_t version_id = 55L; |
| + const GURL scope("http://example.com/"); |
| + const GURL url("http://example.com/worker.js"); |
| + |
| scoped_ptr<EmbeddedWorkerInstance> worker = |
| embedded_worker_registry()->CreateWorker(); |
| worker->AddListener(this); |
| - scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params( |
| - new EmbeddedWorkerMsg_StartWorker_Params()); |
| - ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; |
| + // Run the start worker sequence and detach during process allocation. |
| + ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; |
| + worker->Start( |
| + version_id, scope, url, |
| + base::Bind(&SaveStatusAndCall, &status, base::Bind(&base::DoNothing))); |
| + worker->Detach(); |
| + base::RunLoop().RunUntilIdle(); |
| - // Pretend the worker got stopped before the start sequence reached |
| - // SendStartWorker. |
| - worker->status_ = EmbeddedWorkerInstance::STOPPED; |
| - base::RunLoop run_loop; |
| - worker->SendStartWorker( |
| - std::move(params), |
| - base::Bind(&SaveStatusAndCall, &status, run_loop.QuitClosure()), |
| - true /* is_new_process */, MSG_ROUTING_NONE, |
| - false /* wait_for_debugger */); |
| - run_loop.Run(); |
| - EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT, status); |
| - // Don't expect SendStartWorker() to dispatch an OnStopped/Detached() message |
| - // since the worker was already stopped. |
| - EXPECT_FALSE(stopped_); |
| - EXPECT_FALSE(detached_); |
| - |
| - // Repeat, this time have the start callback destroy the worker, as is |
| - // usual when starting a worker fails, and ensure a crash doesn't occur. |
| - worker->status_ = EmbeddedWorkerInstance::STOPPED; |
| - EmbeddedWorkerInstance* worker_ptr = worker.get(); |
| - worker_ptr->SendStartWorker( |
| - std::move(params), |
| - base::Bind(&DestroyWorker, base::Passed(&worker), &status), |
| - true /* is_new_process */, MSG_ROUTING_NONE, |
| - false /* wait_for_debugger */); |
| - // No crash. |
| - EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT, status); |
| + EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status()); |
| + EXPECT_EQ(ChildProcessHost::kInvalidUniqueID, worker->process_id()); |
| + |
| + // The start callback should not be aborted by detach (see a comment on the |
| + // dtor of EmbeddedWorkerInstance::StartTask). |
| + EXPECT_EQ(SERVICE_WORKER_ERROR_MAX_VALUE, status); |
| + |
| + // "PROCESS_ALLOCATED" event should not be recorded. |
| + ASSERT_EQ(1u, events_.size()); |
| + EXPECT_EQ(DETACHED, events_[0].type); |
| + EXPECT_EQ(EmbeddedWorkerInstance::STARTING, events_[0].status); |
| } |
| -// Test stopping in the middle of the start worker sequence, before |
| -// a process is allocated. |
| -TEST_F(EmbeddedWorkerInstanceTest, StopDuringStart) { |
| +TEST_F(EmbeddedWorkerInstanceTest, DetachAfterSendingStartWorkerMessage) { |
| + const int64_t version_id = 55L; |
| + const GURL scope("http://example.com/"); |
| + const GURL url("http://example.com/worker.js"); |
| + |
| + helper_.reset(new StalledInStartWorkerHelper()); |
| scoped_ptr<EmbeddedWorkerInstance> worker = |
| embedded_worker_registry()->CreateWorker(); |
| worker->AddListener(this); |
| - // Pretend we stop during starting before we got a process allocated. |
| - worker->status_ = EmbeddedWorkerInstance::STARTING; |
| - worker->process_id_ = ChildProcessHost::kInvalidUniqueID; |
| + |
| + // Run the start worker sequence until a start worker message is sent. |
| + ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; |
| + worker->Start( |
| + version_id, scope, url, |
| + base::Bind(&SaveStatusAndCall, &status, base::Bind(base::DoNothing))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(2u, events_.size()); |
| + EXPECT_EQ(PROCESS_ALLOCATED, events_[0].type); |
| + EXPECT_EQ(START_WORKER_MESSAGE_SENT, events_[1].type); |
| + events_.clear(); |
| + |
| + worker->Detach(); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status()); |
| + EXPECT_EQ(ChildProcessHost::kInvalidUniqueID, worker->process_id()); |
| + |
| + // The start callback should not be aborted by detach (see a comment on the |
| + // dtor of EmbeddedWorkerInstance::StartTask). |
| + EXPECT_EQ(SERVICE_WORKER_ERROR_MAX_VALUE, status); |
| + |
| + // "STARTED" event should not be recorded. |
| + ASSERT_EQ(1u, events_.size()); |
| + EXPECT_EQ(DETACHED, events_[0].type); |
| + EXPECT_EQ(EmbeddedWorkerInstance::STARTING, events_[0].status); |
| +} |
| + |
| +TEST_F(EmbeddedWorkerInstanceTest, StopDuringProcessAllocation) { |
| + const int64_t version_id = 55L; |
| + const GURL scope("http://example.com/"); |
| + const GURL url("http://example.com/worker.js"); |
| + |
| + scoped_ptr<EmbeddedWorkerInstance> worker = |
| + embedded_worker_registry()->CreateWorker(); |
| + worker->AddListener(this); |
| + |
| + // Stop the start worker sequence before a process is allocated. |
| + ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; |
| + worker->Start( |
| + version_id, scope, url, |
| + base::Bind(&SaveStatusAndCall, &status, base::Bind(base::DoNothing))); |
| worker->Stop(); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status()); |
| - EXPECT_TRUE(detached_); |
| - EXPECT_EQ(EmbeddedWorkerInstance::STARTING, detached_old_status_); |
| + EXPECT_EQ(ChildProcessHost::kInvalidUniqueID, worker->process_id()); |
| + |
| + // The start callback should not be aborted by stop (see a comment on the dtor |
| + // of EmbeddedWorkerInstance::StartTask). |
| + EXPECT_EQ(SERVICE_WORKER_ERROR_MAX_VALUE, status); |
| + |
| + // "PROCESS_ALLOCATED" event should not be recorded. |
| + ASSERT_EQ(1u, events_.size()); |
| + EXPECT_EQ(DETACHED, events_[0].type); |
| + EXPECT_EQ(EmbeddedWorkerInstance::STARTING, events_[0].status); |
| + events_.clear(); |
| + |
| + // Restart the worker. |
| + status = SERVICE_WORKER_ERROR_MAX_VALUE; |
| + scoped_ptr<base::RunLoop> run_loop(new base::RunLoop); |
| + worker->Start(version_id, scope, url, base::Bind(&SaveStatusAndCall, &status, |
| + run_loop->QuitClosure())); |
| + run_loop->Run(); |
| + |
| + EXPECT_EQ(SERVICE_WORKER_OK, status); |
| + ASSERT_EQ(3u, events_.size()); |
| + EXPECT_EQ(PROCESS_ALLOCATED, events_[0].type); |
| + EXPECT_EQ(START_WORKER_MESSAGE_SENT, events_[1].type); |
| + EXPECT_EQ(STARTED, events_[2].type); |
| + |
| + // Tear down the worker. |
| + worker->Stop(); |
| +} |
| + |
| +TEST_F(EmbeddedWorkerInstanceTest, StopAfterSendingStartWorkerMessage) { |
| + const int64_t version_id = 55L; |
| + const GURL scope("http://example.com/"); |
| + const GURL url("http://example.com/worker.js"); |
| + |
| + StalledInStartWorkerHelper* helper_ptr = new StalledInStartWorkerHelper; |
| + helper_.reset(helper_ptr); |
|
falken
2016/01/14 07:30:00
Is there a reason to introduce the helper_ptr vari
nhiroki
2016/01/14 08:43:40
"helper_" is not StalledInStartWorkerHelper but Em
|
| + scoped_ptr<EmbeddedWorkerInstance> worker = |
| + embedded_worker_registry()->CreateWorker(); |
| + worker->AddListener(this); |
| + |
| + // Run the start worker sequence until a start worker message is sent. |
| + ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE; |
| + worker->Start( |
| + version_id, scope, url, |
| + base::Bind(&SaveStatusAndCall, &status, base::Bind(base::DoNothing))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(2u, events_.size()); |
| + EXPECT_EQ(PROCESS_ALLOCATED, events_[0].type); |
| + EXPECT_EQ(START_WORKER_MESSAGE_SENT, events_[1].type); |
| + events_.clear(); |
| + |
| + worker->Stop(); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status()); |
| + EXPECT_EQ(ChildProcessHost::kInvalidUniqueID, worker->process_id()); |
| + |
| + // The start callback should not be aborted by stop (see a comment on the dtor |
| + // of EmbeddedWorkerInstance::StartTask). |
| + EXPECT_EQ(SERVICE_WORKER_ERROR_MAX_VALUE, status); |
| + |
| + // "STARTED" event should not be recorded. |
| + ASSERT_EQ(1u, events_.size()); |
| + EXPECT_EQ(STOPPED, events_[0].type); |
| + EXPECT_EQ(EmbeddedWorkerInstance::STOPPING, events_[0].status); |
| + events_.clear(); |
| + |
| + // Restart the worker. |
| + helper_ptr->set_use_default_behavior(true); |
| + status = SERVICE_WORKER_ERROR_MAX_VALUE; |
| + scoped_ptr<base::RunLoop> run_loop(new base::RunLoop); |
| + worker->Start(version_id, scope, url, base::Bind(&SaveStatusAndCall, &status, |
| + run_loop->QuitClosure())); |
| + run_loop->Run(); |
| + |
| + // The worker should be started. |
| + EXPECT_EQ(SERVICE_WORKER_OK, status); |
| + ASSERT_EQ(3u, events_.size()); |
| + EXPECT_EQ(PROCESS_ALLOCATED, events_[0].type); |
| + EXPECT_EQ(START_WORKER_MESSAGE_SENT, events_[1].type); |
| + EXPECT_EQ(STARTED, events_[2].type); |
| + |
| + // Tear down the worker. |
| + worker->Stop(); |
| } |
| TEST_F(EmbeddedWorkerInstanceTest, Detach) { |
| @@ -362,8 +519,10 @@ TEST_F(EmbeddedWorkerInstanceTest, FailToSendStartIPC) { |
| // The callback should have run, and we should have got an OnStopped message. |
| EXPECT_EQ(SERVICE_WORKER_ERROR_IPC_FAILED, status); |
| - EXPECT_TRUE(stopped_); |
| - EXPECT_EQ(EmbeddedWorkerInstance::STARTING, stopped_old_status_); |
| + ASSERT_EQ(2u, events_.size()); |
| + EXPECT_EQ(PROCESS_ALLOCATED, events_[0].type); |
| + EXPECT_EQ(STOPPED, events_[1].type); |
| + EXPECT_EQ(EmbeddedWorkerInstance::STARTING, events_[1].status); |
| } |
| } // namespace content |