| Index: mojo/common/handle_watcher_unittest.cc
|
| diff --git a/mojo/common/handle_watcher_unittest.cc b/mojo/common/handle_watcher_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..41d5ffbe52bd8744769f04c82e2b82e69075665e
|
| --- /dev/null
|
| +++ b/mojo/common/handle_watcher_unittest.cc
|
| @@ -0,0 +1,483 @@
|
| +// 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 "mojo/common/handle_watcher.h"
|
| +
|
| +#include <string>
|
| +
|
| +#include "base/at_exit.h"
|
| +#include "base/auto_reset.h"
|
| +#include "base/bind.h"
|
| +#include "base/memory/scoped_vector.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/test/simple_test_tick_clock.h"
|
| +#include "base/threading/thread.h"
|
| +#include "mojo/common/message_pump_mojo.h"
|
| +#include "mojo/common/time_helper.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "third_party/mojo/src/mojo/public/cpp/system/core.h"
|
| +#include "third_party/mojo/src/mojo/public/cpp/test_support/test_utils.h"
|
| +
|
| +namespace mojo {
|
| +namespace common {
|
| +namespace test {
|
| +
|
| +enum MessageLoopConfig {
|
| + MESSAGE_LOOP_CONFIG_DEFAULT = 0,
|
| + MESSAGE_LOOP_CONFIG_MOJO = 1
|
| +};
|
| +
|
| +void ObserveCallback(bool* was_signaled,
|
| + MojoResult* result_observed,
|
| + MojoResult result) {
|
| + *was_signaled = true;
|
| + *result_observed = result;
|
| +}
|
| +
|
| +void RunUntilIdle() {
|
| + base::RunLoop run_loop;
|
| + run_loop.RunUntilIdle();
|
| +}
|
| +
|
| +void DeleteWatcherAndForwardResult(
|
| + HandleWatcher* watcher,
|
| + base::Callback<void(MojoResult)> next_callback,
|
| + MojoResult result) {
|
| + delete watcher;
|
| + next_callback.Run(result);
|
| +}
|
| +
|
| +scoped_ptr<base::MessageLoop> CreateMessageLoop(MessageLoopConfig config) {
|
| + scoped_ptr<base::MessageLoop> loop;
|
| + if (config == MESSAGE_LOOP_CONFIG_DEFAULT)
|
| + loop.reset(new base::MessageLoop());
|
| + else
|
| + loop.reset(new base::MessageLoop(MessagePumpMojo::Create()));
|
| + return loop.Pass();
|
| +}
|
| +
|
| +// Helper class to manage the callback and running the message loop waiting for
|
| +// message to be received. Typical usage is something like:
|
| +// Schedule callback returned from GetCallback().
|
| +// RunUntilGotCallback();
|
| +// EXPECT_TRUE(got_callback());
|
| +// clear_callback();
|
| +class CallbackHelper {
|
| + public:
|
| + CallbackHelper()
|
| + : got_callback_(false),
|
| + run_loop_(NULL),
|
| + weak_factory_(this) {}
|
| + ~CallbackHelper() {}
|
| +
|
| + // See description above |got_callback_|.
|
| + bool got_callback() const { return got_callback_; }
|
| + void clear_callback() { got_callback_ = false; }
|
| +
|
| + // Runs the current MessageLoop until the callback returned from GetCallback()
|
| + // is notified.
|
| + void RunUntilGotCallback() {
|
| + ASSERT_TRUE(run_loop_ == NULL);
|
| + base::RunLoop run_loop;
|
| + base::AutoReset<base::RunLoop*> reseter(&run_loop_, &run_loop);
|
| + run_loop.Run();
|
| + }
|
| +
|
| + base::Callback<void(MojoResult)> GetCallback() {
|
| + return base::Bind(&CallbackHelper::OnCallback, weak_factory_.GetWeakPtr());
|
| + }
|
| +
|
| + void Start(HandleWatcher* watcher, const MessagePipeHandle& handle) {
|
| + StartWithCallback(watcher, handle, GetCallback());
|
| + }
|
| +
|
| + void StartWithCallback(HandleWatcher* watcher,
|
| + const MessagePipeHandle& handle,
|
| + const base::Callback<void(MojoResult)>& callback) {
|
| + watcher->Start(handle, MOJO_HANDLE_SIGNAL_READABLE,
|
| + MOJO_DEADLINE_INDEFINITE, callback);
|
| + }
|
| +
|
| + private:
|
| + void OnCallback(MojoResult result) {
|
| + got_callback_ = true;
|
| + if (run_loop_)
|
| + run_loop_->Quit();
|
| + }
|
| +
|
| + // Set to true when the callback is called.
|
| + bool got_callback_;
|
| +
|
| + // If non-NULL we're in RunUntilGotCallback().
|
| + base::RunLoop* run_loop_;
|
| +
|
| + base::WeakPtrFactory<CallbackHelper> weak_factory_;
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(CallbackHelper);
|
| +};
|
| +
|
| +class HandleWatcherTest : public testing::TestWithParam<MessageLoopConfig> {
|
| + public:
|
| + HandleWatcherTest() : message_loop_(CreateMessageLoop(GetParam())) {}
|
| + virtual ~HandleWatcherTest() {
|
| + test::SetTickClockForTest(NULL);
|
| + }
|
| +
|
| + protected:
|
| + void TearDownMessageLoop() {
|
| + message_loop_.reset();
|
| + }
|
| +
|
| + void InstallTickClock() {
|
| + test::SetTickClockForTest(&tick_clock_);
|
| + }
|
| +
|
| + base::SimpleTestTickClock tick_clock_;
|
| +
|
| + private:
|
| + base::ShadowingAtExitManager at_exit_;
|
| + scoped_ptr<base::MessageLoop> message_loop_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(HandleWatcherTest);
|
| +};
|
| +
|
| +INSTANTIATE_TEST_CASE_P(
|
| + MultipleMessageLoopConfigs, HandleWatcherTest,
|
| + testing::Values(MESSAGE_LOOP_CONFIG_DEFAULT, MESSAGE_LOOP_CONFIG_MOJO));
|
| +
|
| +// Trivial test case with a single handle to watch.
|
| +TEST_P(HandleWatcherTest, SingleHandler) {
|
| + MessagePipe test_pipe;
|
| + ASSERT_TRUE(test_pipe.handle0.is_valid());
|
| + CallbackHelper callback_helper;
|
| + HandleWatcher watcher;
|
| + callback_helper.Start(&watcher, test_pipe.handle0.get());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper.got_callback());
|
| + EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe.handle1.get(),
|
| + std::string()));
|
| + callback_helper.RunUntilGotCallback();
|
| + EXPECT_TRUE(callback_helper.got_callback());
|
| +}
|
| +
|
| +// Creates three handles and notfies them in reverse order ensuring each one is
|
| +// notified appropriately.
|
| +TEST_P(HandleWatcherTest, ThreeHandles) {
|
| + MessagePipe test_pipe1;
|
| + MessagePipe test_pipe2;
|
| + MessagePipe test_pipe3;
|
| + CallbackHelper callback_helper1;
|
| + CallbackHelper callback_helper2;
|
| + CallbackHelper callback_helper3;
|
| + ASSERT_TRUE(test_pipe1.handle0.is_valid());
|
| + ASSERT_TRUE(test_pipe2.handle0.is_valid());
|
| + ASSERT_TRUE(test_pipe3.handle0.is_valid());
|
| +
|
| + HandleWatcher watcher1;
|
| + callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| + EXPECT_FALSE(callback_helper3.got_callback());
|
| +
|
| + HandleWatcher watcher2;
|
| + callback_helper2.Start(&watcher2, test_pipe2.handle0.get());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| + EXPECT_FALSE(callback_helper3.got_callback());
|
| +
|
| + HandleWatcher watcher3;
|
| + callback_helper3.Start(&watcher3, test_pipe3.handle0.get());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| + EXPECT_FALSE(callback_helper3.got_callback());
|
| +
|
| + // Write to 3 and make sure it's notified.
|
| + EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe3.handle1.get(),
|
| + std::string()));
|
| + callback_helper3.RunUntilGotCallback();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| + EXPECT_TRUE(callback_helper3.got_callback());
|
| + callback_helper3.clear_callback();
|
| +
|
| + // Write to 1 and 3. Only 1 should be notified since 3 was is no longer
|
| + // running.
|
| + EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
|
| + std::string()));
|
| + EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe3.handle1.get(),
|
| + std::string()));
|
| + callback_helper1.RunUntilGotCallback();
|
| + EXPECT_TRUE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| + EXPECT_FALSE(callback_helper3.got_callback());
|
| + callback_helper1.clear_callback();
|
| +
|
| + // Write to 1 and 2. Only 2 should be notified (since 1 was already notified).
|
| + EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
|
| + std::string()));
|
| + EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe2.handle1.get(),
|
| + std::string()));
|
| + callback_helper2.RunUntilGotCallback();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_TRUE(callback_helper2.got_callback());
|
| + EXPECT_FALSE(callback_helper3.got_callback());
|
| +}
|
| +
|
| +// Verifies Start() invoked a second time works.
|
| +TEST_P(HandleWatcherTest, Restart) {
|
| + MessagePipe test_pipe1;
|
| + MessagePipe test_pipe2;
|
| + CallbackHelper callback_helper1;
|
| + CallbackHelper callback_helper2;
|
| + ASSERT_TRUE(test_pipe1.handle0.is_valid());
|
| + ASSERT_TRUE(test_pipe2.handle0.is_valid());
|
| +
|
| + HandleWatcher watcher1;
|
| + callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| +
|
| + HandleWatcher watcher2;
|
| + callback_helper2.Start(&watcher2, test_pipe2.handle0.get());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| +
|
| + // Write to 1 and make sure it's notified.
|
| + EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
|
| + std::string()));
|
| + callback_helper1.RunUntilGotCallback();
|
| + EXPECT_TRUE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| + callback_helper1.clear_callback();
|
| + EXPECT_TRUE(mojo::test::DiscardMessage(test_pipe1.handle0.get()));
|
| +
|
| + // Write to 2 and make sure it's notified.
|
| + EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe2.handle1.get(),
|
| + std::string()));
|
| + callback_helper2.RunUntilGotCallback();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_TRUE(callback_helper2.got_callback());
|
| + callback_helper2.clear_callback();
|
| +
|
| + // Listen on 1 again.
|
| + callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| +
|
| + // Write to 1 and make sure it's notified.
|
| + EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
|
| + std::string()));
|
| + callback_helper1.RunUntilGotCallback();
|
| + EXPECT_TRUE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| +}
|
| +
|
| +// Verifies Start() invoked a second time on the same handle works.
|
| +TEST_P(HandleWatcherTest, RestartOnSameHandle) {
|
| + MessagePipe test_pipe;
|
| + CallbackHelper callback_helper;
|
| + ASSERT_TRUE(test_pipe.handle0.is_valid());
|
| +
|
| + HandleWatcher watcher;
|
| + callback_helper.Start(&watcher, test_pipe.handle0.get());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper.got_callback());
|
| +
|
| + callback_helper.Start(&watcher, test_pipe.handle0.get());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper.got_callback());
|
| +}
|
| +
|
| +// Verifies deadline is honored.
|
| +TEST_P(HandleWatcherTest, Deadline) {
|
| + InstallTickClock();
|
| +
|
| + MessagePipe test_pipe1;
|
| + MessagePipe test_pipe2;
|
| + MessagePipe test_pipe3;
|
| + CallbackHelper callback_helper1;
|
| + CallbackHelper callback_helper2;
|
| + CallbackHelper callback_helper3;
|
| + ASSERT_TRUE(test_pipe1.handle0.is_valid());
|
| + ASSERT_TRUE(test_pipe2.handle0.is_valid());
|
| + ASSERT_TRUE(test_pipe3.handle0.is_valid());
|
| +
|
| + // Add a watcher with an infinite timeout.
|
| + HandleWatcher watcher1;
|
| + callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| + EXPECT_FALSE(callback_helper3.got_callback());
|
| +
|
| + // Add another watcher wth a timeout of 500 microseconds.
|
| + HandleWatcher watcher2;
|
| + watcher2.Start(test_pipe2.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, 500,
|
| + callback_helper2.GetCallback());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_FALSE(callback_helper2.got_callback());
|
| + EXPECT_FALSE(callback_helper3.got_callback());
|
| +
|
| + // Advance the clock passed the deadline. We also have to start another
|
| + // watcher to wake up the background thread.
|
| + tick_clock_.Advance(base::TimeDelta::FromMicroseconds(501));
|
| +
|
| + HandleWatcher watcher3;
|
| + callback_helper3.Start(&watcher3, test_pipe3.handle0.get());
|
| +
|
| + callback_helper2.RunUntilGotCallback();
|
| + EXPECT_FALSE(callback_helper1.got_callback());
|
| + EXPECT_TRUE(callback_helper2.got_callback());
|
| + EXPECT_FALSE(callback_helper3.got_callback());
|
| +}
|
| +
|
| +TEST_P(HandleWatcherTest, DeleteInCallback) {
|
| + MessagePipe test_pipe;
|
| + CallbackHelper callback_helper;
|
| +
|
| + HandleWatcher* watcher = new HandleWatcher();
|
| + callback_helper.StartWithCallback(watcher, test_pipe.handle1.get(),
|
| + base::Bind(&DeleteWatcherAndForwardResult,
|
| + watcher,
|
| + callback_helper.GetCallback()));
|
| + EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe.handle0.get(),
|
| + std::string()));
|
| + callback_helper.RunUntilGotCallback();
|
| + EXPECT_TRUE(callback_helper.got_callback());
|
| +}
|
| +
|
| +TEST_P(HandleWatcherTest, AbortedOnMessageLoopDestruction) {
|
| + bool was_signaled = false;
|
| + MojoResult result = MOJO_RESULT_OK;
|
| +
|
| + MessagePipe pipe;
|
| + HandleWatcher watcher;
|
| + watcher.Start(pipe.handle0.get(),
|
| + MOJO_HANDLE_SIGNAL_READABLE,
|
| + MOJO_DEADLINE_INDEFINITE,
|
| + base::Bind(&ObserveCallback, &was_signaled, &result));
|
| +
|
| + // Now, let the MessageLoop get torn down. We expect our callback to run.
|
| + TearDownMessageLoop();
|
| +
|
| + EXPECT_TRUE(was_signaled);
|
| + EXPECT_EQ(MOJO_RESULT_ABORTED, result);
|
| +}
|
| +
|
| +void NeverReached(MojoResult result) {
|
| + FAIL() << "Callback should never be invoked " << result;
|
| +}
|
| +
|
| +// Called on the main thread when a thread is done. Decrements |active_count|
|
| +// and if |active_count| is zero quits |run_loop|.
|
| +void StressThreadDone(base::RunLoop* run_loop, int* active_count) {
|
| + (*active_count)--;
|
| + EXPECT_GE(*active_count, 0);
|
| + if (*active_count == 0)
|
| + run_loop->Quit();
|
| +}
|
| +
|
| +// See description of StressTest. This is called on the background thread.
|
| +// |count| is the number of HandleWatchers to create. |active_count| is the
|
| +// number of outstanding threads, |task_runner| the task runner for the main
|
| +// thread and |run_loop| the run loop that should be quit when there are no more
|
| +// threads running. When done StressThreadDone() is invoked on the main thread.
|
| +// |active_count| and |run_loop| should only be used on the main thread.
|
| +void RunStressTest(int count,
|
| + scoped_refptr<base::TaskRunner> task_runner,
|
| + base::RunLoop* run_loop,
|
| + int* active_count) {
|
| + struct TestData {
|
| + MessagePipe pipe;
|
| + HandleWatcher watcher;
|
| + };
|
| + ScopedVector<TestData> data_vector;
|
| + for (int i = 0; i < count; ++i) {
|
| + if (i % 20 == 0) {
|
| + // Every so often we wait. This results in some level of thread balancing
|
| + // as well as making sure HandleWatcher has time to actually start some
|
| + // watches.
|
| + MessagePipe test_pipe;
|
| + ASSERT_TRUE(test_pipe.handle0.is_valid());
|
| + CallbackHelper callback_helper;
|
| + HandleWatcher watcher;
|
| + callback_helper.Start(&watcher, test_pipe.handle0.get());
|
| + RunUntilIdle();
|
| + EXPECT_FALSE(callback_helper.got_callback());
|
| + EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe.handle1.get(),
|
| + std::string()));
|
| + base::MessageLoop::ScopedNestableTaskAllower scoper(
|
| + base::MessageLoop::current());
|
| + callback_helper.RunUntilGotCallback();
|
| + EXPECT_TRUE(callback_helper.got_callback());
|
| + } else {
|
| + scoped_ptr<TestData> test_data(new TestData);
|
| + ASSERT_TRUE(test_data->pipe.handle0.is_valid());
|
| + test_data->watcher.Start(test_data->pipe.handle0.get(),
|
| + MOJO_HANDLE_SIGNAL_READABLE,
|
| + MOJO_DEADLINE_INDEFINITE,
|
| + base::Bind(&NeverReached));
|
| + data_vector.push_back(test_data.release());
|
| + }
|
| + if (i % 15 == 0)
|
| + data_vector.clear();
|
| + }
|
| + task_runner->PostTask(FROM_HERE,
|
| + base::Bind(&StressThreadDone, run_loop,
|
| + active_count));
|
| +}
|
| +
|
| +// This test is meant to stress HandleWatcher. It uses from various threads
|
| +// repeatedly starting and stopping watches. It spins up kThreadCount
|
| +// threads. Each thread creates kWatchCount watches. Every so often each thread
|
| +// writes to a pipe and waits for the response.
|
| +TEST(HandleWatcherCleanEnvironmentTest, StressTest) {
|
| +#if defined(NDEBUG)
|
| + const int kThreadCount = 15;
|
| + const int kWatchCount = 400;
|
| +#else
|
| + const int kThreadCount = 10;
|
| + const int kWatchCount = 250;
|
| +#endif
|
| +
|
| + base::ShadowingAtExitManager at_exit;
|
| + base::MessageLoop message_loop;
|
| + base::RunLoop run_loop;
|
| + ScopedVector<base::Thread> threads;
|
| + int threads_active_counter = kThreadCount;
|
| + // Starts the threads first and then post the task in hopes of having more
|
| + // threads running at once.
|
| + for (int i = 0; i < kThreadCount; ++i) {
|
| + scoped_ptr<base::Thread> thread(new base::Thread("test thread"));
|
| + if (i % 2) {
|
| + base::Thread::Options thread_options;
|
| + thread_options.message_pump_factory =
|
| + base::Bind(&MessagePumpMojo::Create);
|
| + thread->StartWithOptions(thread_options);
|
| + } else {
|
| + thread->Start();
|
| + }
|
| + threads.push_back(thread.release());
|
| + }
|
| + for (int i = 0; i < kThreadCount; ++i) {
|
| + threads[i]->task_runner()->PostTask(
|
| + FROM_HERE, base::Bind(&RunStressTest, kWatchCount,
|
| + message_loop.task_runner(),
|
| + &run_loop, &threads_active_counter));
|
| + }
|
| + run_loop.Run();
|
| + ASSERT_EQ(0, threads_active_counter);
|
| +}
|
| +
|
| +} // namespace test
|
| +} // namespace common
|
| +} // namespace mojo
|
|
|