blob: 7f1fcca080864f7ae814697fddf9f92c5aa7d528 [file] [log] [blame]
// Copyright 2013 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 "base/stl_util.h"
#include "content/browser/service_worker/embedded_worker_instance.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_context_wrapper.h"
#include "content/common/service_worker/embedded_worker_messages.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
static const int kRenderProcessId = 11;
class EmbeddedWorkerInstanceTest : public testing::Test {
protected:
EmbeddedWorkerInstanceTest()
: thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
virtual void SetUp() OVERRIDE {
helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId));
}
virtual void TearDown() OVERRIDE {
helper_.reset();
}
ServiceWorkerContextCore* context() { return helper_->context(); }
EmbeddedWorkerRegistry* embedded_worker_registry() {
DCHECK(context());
return context()->embedded_worker_registry();
}
IPC::TestSink* ipc_sink() { return helper_->ipc_sink(); }
TestBrowserThreadBundle thread_bundle_;
scoped_ptr<EmbeddedWorkerTestHelper> helper_;
private:
DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerInstanceTest);
};
static void SaveStatusAndCall(ServiceWorkerStatusCode* out,
const base::Closure& callback,
ServiceWorkerStatusCode status) {
*out = status;
callback.Run();
}
TEST_F(EmbeddedWorkerInstanceTest, StartAndStop) {
scoped_ptr<EmbeddedWorkerInstance> worker =
embedded_worker_registry()->CreateWorker();
EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status());
const int embedded_worker_id = worker->embedded_worker_id();
const int64 service_worker_version_id = 55L;
const GURL scope("http://example.com/");
const GURL url("http://example.com/worker.js");
// Simulate adding one process to the worker.
helper_->SimulateAddProcessToWorker(embedded_worker_id, kRenderProcessId);
// Start should succeed.
ServiceWorkerStatusCode status;
base::RunLoop run_loop;
worker->Start(
service_worker_version_id,
scope,
url,
false,
std::vector<int>(),
base::Bind(&SaveStatusAndCall, &status, run_loop.QuitClosure()));
run_loop.Run();
EXPECT_EQ(SERVICE_WORKER_OK, status);
EXPECT_EQ(EmbeddedWorkerInstance::STARTING, worker->status());
base::RunLoop().RunUntilIdle();
// Worker started message should be notified (by EmbeddedWorkerTestHelper).
EXPECT_EQ(EmbeddedWorkerInstance::RUNNING, worker->status());
EXPECT_EQ(kRenderProcessId, worker->process_id());
// Stop the worker.
EXPECT_EQ(SERVICE_WORKER_OK, worker->Stop());
EXPECT_EQ(EmbeddedWorkerInstance::STOPPING, worker->status());
base::RunLoop().RunUntilIdle();
// Worker stopped message should be notified (by EmbeddedWorkerTestHelper).
EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status());
// Verify that we've sent two messages to start and terminate the worker.
ASSERT_TRUE(ipc_sink()->GetUniqueMessageMatching(
EmbeddedWorkerMsg_StartWorker::ID));
ASSERT_TRUE(ipc_sink()->GetUniqueMessageMatching(
EmbeddedWorkerMsg_StopWorker::ID));
}
TEST_F(EmbeddedWorkerInstanceTest, InstanceDestroyedBeforeStartFinishes) {
scoped_ptr<EmbeddedWorkerInstance> worker =
embedded_worker_registry()->CreateWorker();
EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status());
const int64 service_worker_version_id = 55L;
const GURL scope("http://example.com/");
const GURL url("http://example.com/worker.js");
ServiceWorkerStatusCode status;
base::RunLoop run_loop;
// Begin starting the worker.
std::vector<int> available_process;
available_process.push_back(kRenderProcessId);
worker->Start(
service_worker_version_id,
scope,
url,
false,
available_process,
base::Bind(&SaveStatusAndCall, &status, run_loop.QuitClosure()));
// But destroy it before it gets a chance to complete.
worker.reset();
run_loop.Run();
EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT, status);
// Verify that we didn't send the message to start the worker.
ASSERT_FALSE(
ipc_sink()->GetUniqueMessageMatching(EmbeddedWorkerMsg_StartWorker::ID));
}
TEST_F(EmbeddedWorkerInstanceTest, SortProcesses) {
scoped_ptr<EmbeddedWorkerInstance> worker =
embedded_worker_registry()->CreateWorker();
EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status());
// Simulate adding processes to the worker.
// Process 1 has 1 ref, 2 has 2 refs and 3 has 3 refs.
const int embedded_worker_id = worker->embedded_worker_id();
helper_->SimulateAddProcessToWorker(embedded_worker_id, 1);
helper_->SimulateAddProcessToWorker(embedded_worker_id, 2);
helper_->SimulateAddProcessToWorker(embedded_worker_id, 2);
helper_->SimulateAddProcessToWorker(embedded_worker_id, 3);
helper_->SimulateAddProcessToWorker(embedded_worker_id, 3);
helper_->SimulateAddProcessToWorker(embedded_worker_id, 3);
// Process 3 has the biggest # of references and it should be chosen.
EXPECT_THAT(worker->SortProcesses(std::vector<int>()),
testing::ElementsAre(3, 2, 1));
EXPECT_EQ(-1, worker->process_id());
// Argument processes are added to the existing set, but only for a single
// call.
std::vector<int> registering_processes;
registering_processes.push_back(1);
registering_processes.push_back(1);
registering_processes.push_back(1);
registering_processes.push_back(4);
EXPECT_THAT(worker->SortProcesses(registering_processes),
testing::ElementsAre(1, 3, 2, 4));
EXPECT_THAT(worker->SortProcesses(std::vector<int>()),
testing::ElementsAre(3, 2, 1));
}
} // namespace content