| // Copyright 2014 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/public/cpp/utility/run_loop.h" |
| |
| #include <string> |
| |
| #include "mojo/public/cpp/system/macros.h" |
| #include "mojo/public/cpp/system/message_pipe.h" |
| #include "mojo/public/cpp/test_support/test_utils.h" |
| #include "mojo/public/cpp/utility/run_loop_handler.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace mojo { |
| namespace { |
| |
| class TestRunLoopHandler : public RunLoopHandler { |
| public: |
| TestRunLoopHandler() |
| : ready_count_(0), |
| error_count_(0), |
| last_error_result_(MOJO_RESULT_OK) { |
| } |
| ~TestRunLoopHandler() override {} |
| |
| void clear_ready_count() { ready_count_ = 0; } |
| int ready_count() const { return ready_count_; } |
| |
| void clear_error_count() { error_count_ = 0; } |
| int error_count() const { return error_count_; } |
| |
| MojoResult last_error_result() const { return last_error_result_; } |
| |
| // RunLoopHandler: |
| void OnHandleReady(const Handle& handle) override { ready_count_++; } |
| void OnHandleError(const Handle& handle, MojoResult result) override { |
| error_count_++; |
| last_error_result_ = result; |
| } |
| |
| private: |
| int ready_count_; |
| int error_count_; |
| MojoResult last_error_result_; |
| |
| MOJO_DISALLOW_COPY_AND_ASSIGN(TestRunLoopHandler); |
| }; |
| |
| // Trivial test to verify Run() with no added handles returns. |
| TEST(RunLoopTest, ExitsWithNoHandles) { |
| RunLoop run_loop; |
| run_loop.Run(); |
| } |
| |
| class RemoveOnReadyRunLoopHandler : public TestRunLoopHandler { |
| public: |
| RemoveOnReadyRunLoopHandler() : run_loop_(nullptr) {} |
| ~RemoveOnReadyRunLoopHandler() override {} |
| |
| void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } |
| |
| // RunLoopHandler: |
| void OnHandleReady(const Handle& handle) override { |
| run_loop_->RemoveHandler(handle); |
| TestRunLoopHandler::OnHandleReady(handle); |
| } |
| |
| private: |
| RunLoop* run_loop_; |
| |
| MOJO_DISALLOW_COPY_AND_ASSIGN(RemoveOnReadyRunLoopHandler); |
| }; |
| |
| // Verifies RunLoop quits when no more handles (handle is removed when ready). |
| TEST(RunLoopTest, HandleReady) { |
| RemoveOnReadyRunLoopHandler handler; |
| MessagePipe test_pipe; |
| EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string())); |
| |
| RunLoop run_loop; |
| handler.set_run_loop(&run_loop); |
| run_loop.AddHandler(&handler, test_pipe.handle0.get(), |
| MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE); |
| run_loop.Run(); |
| EXPECT_EQ(1, handler.ready_count()); |
| EXPECT_EQ(0, handler.error_count()); |
| EXPECT_FALSE(run_loop.HasHandler(test_pipe.handle0.get())); |
| } |
| |
| class QuitOnReadyRunLoopHandler : public TestRunLoopHandler { |
| public: |
| QuitOnReadyRunLoopHandler() : run_loop_(nullptr) {} |
| ~QuitOnReadyRunLoopHandler() override {} |
| |
| void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } |
| |
| // RunLoopHandler: |
| void OnHandleReady(const Handle& handle) override { |
| run_loop_->Quit(); |
| TestRunLoopHandler::OnHandleReady(handle); |
| } |
| |
| private: |
| RunLoop* run_loop_; |
| |
| MOJO_DISALLOW_COPY_AND_ASSIGN(QuitOnReadyRunLoopHandler); |
| }; |
| |
| // Verifies Quit() from OnHandleReady() quits the loop. |
| TEST(RunLoopTest, QuitFromReady) { |
| QuitOnReadyRunLoopHandler handler; |
| MessagePipe test_pipe; |
| EXPECT_TRUE(test::WriteTextMessage(test_pipe.handle1.get(), std::string())); |
| |
| RunLoop run_loop; |
| handler.set_run_loop(&run_loop); |
| run_loop.AddHandler(&handler, test_pipe.handle0.get(), |
| MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE); |
| run_loop.Run(); |
| EXPECT_EQ(1, handler.ready_count()); |
| EXPECT_EQ(0, handler.error_count()); |
| EXPECT_TRUE(run_loop.HasHandler(test_pipe.handle0.get())); |
| } |
| |
| class QuitOnErrorRunLoopHandler : public TestRunLoopHandler { |
| public: |
| QuitOnErrorRunLoopHandler() : run_loop_(nullptr) {} |
| ~QuitOnErrorRunLoopHandler() override {} |
| |
| void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } |
| |
| // RunLoopHandler: |
| void OnHandleError(const Handle& handle, MojoResult result) override { |
| run_loop_->Quit(); |
| TestRunLoopHandler::OnHandleError(handle, result); |
| } |
| |
| private: |
| RunLoop* run_loop_; |
| |
| MOJO_DISALLOW_COPY_AND_ASSIGN(QuitOnErrorRunLoopHandler); |
| }; |
| |
| // Verifies Quit() when the deadline is reached works. |
| TEST(RunLoopTest, QuitWhenDeadlineExpired) { |
| QuitOnErrorRunLoopHandler handler; |
| MessagePipe test_pipe; |
| RunLoop run_loop; |
| handler.set_run_loop(&run_loop); |
| run_loop.AddHandler(&handler, test_pipe.handle0.get(), |
| MOJO_HANDLE_SIGNAL_READABLE, |
| static_cast<MojoDeadline>(10000)); |
| run_loop.Run(); |
| EXPECT_EQ(0, handler.ready_count()); |
| EXPECT_EQ(1, handler.error_count()); |
| EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, handler.last_error_result()); |
| EXPECT_FALSE(run_loop.HasHandler(test_pipe.handle0.get())); |
| } |
| |
| // Test that handlers are notified of loop destruction. |
| TEST(RunLoopTest, Destruction) { |
| TestRunLoopHandler handler; |
| MessagePipe test_pipe; |
| { |
| RunLoop run_loop; |
| run_loop.AddHandler(&handler, |
| test_pipe.handle0.get(), |
| MOJO_HANDLE_SIGNAL_READABLE, |
| MOJO_DEADLINE_INDEFINITE); |
| } |
| EXPECT_EQ(1, handler.error_count()); |
| EXPECT_EQ(MOJO_RESULT_ABORTED, handler.last_error_result()); |
| } |
| |
| class RemoveManyRunLoopHandler : public TestRunLoopHandler { |
| public: |
| RemoveManyRunLoopHandler() : run_loop_(nullptr) {} |
| ~RemoveManyRunLoopHandler() override {} |
| |
| void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } |
| void add_handle(const Handle& handle) { handles_.push_back(handle); } |
| |
| // RunLoopHandler: |
| void OnHandleError(const Handle& handle, MojoResult result) override { |
| for (size_t i = 0; i < handles_.size(); i++) |
| run_loop_->RemoveHandler(handles_[i]); |
| TestRunLoopHandler::OnHandleError(handle, result); |
| } |
| |
| private: |
| std::vector<Handle> handles_; |
| RunLoop* run_loop_; |
| |
| MOJO_DISALLOW_COPY_AND_ASSIGN(RemoveManyRunLoopHandler); |
| }; |
| |
| // Test that handlers are notified of loop destruction. |
| TEST(RunLoopTest, MultipleHandleDestruction) { |
| RemoveManyRunLoopHandler odd_handler; |
| TestRunLoopHandler even_handler; |
| MessagePipe test_pipe1, test_pipe2, test_pipe3; |
| { |
| RunLoop run_loop; |
| odd_handler.set_run_loop(&run_loop); |
| odd_handler.add_handle(test_pipe1.handle0.get()); |
| odd_handler.add_handle(test_pipe3.handle0.get()); |
| run_loop.AddHandler(&odd_handler, |
| test_pipe1.handle0.get(), |
| MOJO_HANDLE_SIGNAL_READABLE, |
| MOJO_DEADLINE_INDEFINITE); |
| run_loop.AddHandler(&even_handler, |
| test_pipe2.handle0.get(), |
| MOJO_HANDLE_SIGNAL_READABLE, |
| MOJO_DEADLINE_INDEFINITE); |
| run_loop.AddHandler(&odd_handler, |
| test_pipe3.handle0.get(), |
| MOJO_HANDLE_SIGNAL_READABLE, |
| MOJO_DEADLINE_INDEFINITE); |
| } |
| EXPECT_EQ(1, odd_handler.error_count()); |
| EXPECT_EQ(1, even_handler.error_count()); |
| EXPECT_EQ(MOJO_RESULT_ABORTED, odd_handler.last_error_result()); |
| EXPECT_EQ(MOJO_RESULT_ABORTED, even_handler.last_error_result()); |
| } |
| |
| class AddHandlerOnErrorHandler : public TestRunLoopHandler { |
| public: |
| AddHandlerOnErrorHandler() : run_loop_(nullptr) {} |
| ~AddHandlerOnErrorHandler() override {} |
| |
| void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } |
| |
| // RunLoopHandler: |
| void OnHandleError(const Handle& handle, MojoResult result) override { |
| run_loop_->AddHandler(this, handle, |
| MOJO_HANDLE_SIGNAL_READABLE, |
| MOJO_DEADLINE_INDEFINITE); |
| TestRunLoopHandler::OnHandleError(handle, result); |
| } |
| |
| private: |
| RunLoop* run_loop_; |
| |
| MOJO_DISALLOW_COPY_AND_ASSIGN(AddHandlerOnErrorHandler); |
| }; |
| |
| TEST(RunLoopTest, AddHandlerOnError) { |
| AddHandlerOnErrorHandler handler; |
| MessagePipe test_pipe; |
| { |
| RunLoop run_loop; |
| handler.set_run_loop(&run_loop); |
| run_loop.AddHandler(&handler, |
| test_pipe.handle0.get(), |
| MOJO_HANDLE_SIGNAL_READABLE, |
| MOJO_DEADLINE_INDEFINITE); |
| } |
| EXPECT_EQ(1, handler.error_count()); |
| EXPECT_EQ(MOJO_RESULT_ABORTED, handler.last_error_result()); |
| } |
| |
| TEST(RunLoopTest, Current) { |
| EXPECT_TRUE(RunLoop::current() == nullptr); |
| { |
| RunLoop run_loop; |
| EXPECT_EQ(&run_loop, RunLoop::current()); |
| } |
| EXPECT_TRUE(RunLoop::current() == nullptr); |
| } |
| |
| class NestingRunLoopHandler : public TestRunLoopHandler { |
| public: |
| static const size_t kDepthLimit; |
| static const char kSignalMagic; |
| |
| NestingRunLoopHandler() |
| : run_loop_(nullptr), |
| pipe_(nullptr), |
| depth_(0), |
| reached_depth_limit_(false) {} |
| |
| ~NestingRunLoopHandler() override {} |
| |
| void set_run_loop(RunLoop* run_loop) { run_loop_ = run_loop; } |
| void set_pipe(MessagePipe* pipe) { pipe_ = pipe; } |
| bool reached_depth_limit() const { return reached_depth_limit_; } |
| |
| // RunLoopHandler: |
| void OnHandleReady(const Handle& handle) override { |
| TestRunLoopHandler::OnHandleReady(handle); |
| EXPECT_EQ(handle.value(), pipe_->handle0.get().value()); |
| |
| ReadSignal(); |
| size_t current_depth = ++depth_; |
| if (current_depth < kDepthLimit) { |
| WriteSignal(); |
| run_loop_->Run(); |
| if (current_depth == kDepthLimit - 1) { |
| // The topmost loop Quit()-ed, so its parent takes back the |
| // control without exeeding deadline. |
| EXPECT_EQ(error_count(), 0); |
| } else { |
| EXPECT_EQ(error_count(), 1); |
| } |
| |
| } else { |
| EXPECT_EQ(current_depth, kDepthLimit); |
| reached_depth_limit_ = true; |
| run_loop_->Quit(); |
| } |
| --depth_; |
| } |
| |
| void WriteSignal() { |
| char write_byte = kSignalMagic; |
| MojoResult write_result = |
| WriteMessageRaw(pipe_->handle1.get(), &write_byte, 1, nullptr, 0, |
| MOJO_WRITE_MESSAGE_FLAG_NONE); |
| EXPECT_EQ(write_result, MOJO_RESULT_OK); |
| } |
| |
| void ReadSignal() { |
| char read_byte = 0; |
| uint32_t bytes_read = 1; |
| uint32_t handles_read = 0; |
| MojoResult read_result = |
| ReadMessageRaw(pipe_->handle0.get(), &read_byte, &bytes_read, nullptr, |
| &handles_read, MOJO_READ_MESSAGE_FLAG_NONE); |
| EXPECT_EQ(read_result, MOJO_RESULT_OK); |
| EXPECT_EQ(read_byte, kSignalMagic); |
| } |
| |
| private: |
| RunLoop* run_loop_; |
| MessagePipe* pipe_; |
| size_t depth_; |
| bool reached_depth_limit_; |
| |
| MOJO_DISALLOW_COPY_AND_ASSIGN(NestingRunLoopHandler); |
| }; |
| |
| const size_t NestingRunLoopHandler::kDepthLimit = 10; |
| const char NestingRunLoopHandler::kSignalMagic = 'X'; |
| |
| TEST(RunLoopTest, NestedRun) { |
| NestingRunLoopHandler handler; |
| MessagePipe test_pipe; |
| RunLoop run_loop; |
| handler.set_run_loop(&run_loop); |
| handler.set_pipe(&test_pipe); |
| run_loop.AddHandler(&handler, test_pipe.handle0.get(), |
| MOJO_HANDLE_SIGNAL_READABLE, |
| static_cast<MojoDeadline>(10000)); |
| handler.WriteSignal(); |
| run_loop.Run(); |
| |
| EXPECT_TRUE(handler.reached_depth_limit()); |
| // Got MOJO_RESULT_DEADLINE_EXCEEDED once then removed from the |
| // RunLoop's handler list. |
| EXPECT_EQ(handler.error_count(), 1); |
| EXPECT_EQ(handler.last_error_result(), MOJO_RESULT_DEADLINE_EXCEEDED); |
| } |
| |
| struct Task { |
| Task(int num, std::vector<int>* sequence) : num(num), sequence(sequence) {} |
| |
| void Run() const { sequence->push_back(num); } |
| |
| int num; |
| std::vector<int>* sequence; |
| }; |
| |
| TEST(RunLoopTest, DelayedTaskOrder) { |
| std::vector<int> sequence; |
| RunLoop run_loop; |
| run_loop.PostDelayedTask(Closure(Task(1, &sequence)), 0); |
| run_loop.PostDelayedTask(Closure(Task(2, &sequence)), 0); |
| run_loop.PostDelayedTask(Closure(Task(3, &sequence)), 0); |
| run_loop.RunUntilIdle(); |
| |
| ASSERT_EQ(3u, sequence.size()); |
| EXPECT_EQ(1, sequence[0]); |
| EXPECT_EQ(2, sequence[1]); |
| EXPECT_EQ(3, sequence[2]); |
| } |
| |
| struct QuittingTask { |
| explicit QuittingTask(RunLoop* run_loop) : run_loop(run_loop) {} |
| |
| void Run() const { run_loop->Quit(); } |
| |
| RunLoop* run_loop; |
| }; |
| |
| TEST(RunLoopTest, QuitFromDelayedTask) { |
| TestRunLoopHandler handler; |
| MessagePipe test_pipe; |
| RunLoop run_loop; |
| run_loop.AddHandler(&handler, |
| test_pipe.handle0.get(), |
| MOJO_HANDLE_SIGNAL_READABLE, |
| MOJO_DEADLINE_INDEFINITE); |
| run_loop.PostDelayedTask(Closure(QuittingTask(&run_loop)), 0); |
| run_loop.Run(); |
| } |
| |
| } // namespace |
| } // namespace mojo |