Index: mojo/public/cpp/bindings/tests/connector_unittest.cc |
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc |
index a85d8d323c5a4b45169b23aac60b1f454202d5b1..a0fafd4f2ca849938630369c6e771bbb941cc671 100644 |
--- a/mojo/public/cpp/bindings/tests/connector_unittest.cc |
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc |
@@ -5,10 +5,13 @@ |
#include <stdlib.h> |
#include <string.h> |
+#include <string> |
+ |
#include "mojo/public/cpp/bindings/lib/connector.h" |
#include "mojo/public/cpp/bindings/lib/message_builder.h" |
#include "mojo/public/cpp/bindings/tests/message_queue.h" |
#include "mojo/public/cpp/environment/environment.h" |
+#include "mojo/public/cpp/environment/logging.h" |
#include "mojo/public/cpp/system/macros.h" |
#include "mojo/public/cpp/utility/run_loop.h" |
#include "testing/gtest/include/gtest/gtest.h" |
@@ -17,60 +20,6 @@ namespace mojo { |
namespace test { |
namespace { |
-class MessageAccumulator : public MessageReceiver { |
- public: |
- MessageAccumulator() {} |
- |
- bool Accept(Message* message) override { |
- queue_.Push(message); |
- return true; |
- } |
- |
- bool IsEmpty() const { return queue_.IsEmpty(); } |
- |
- void Pop(Message* message) { queue_.Pop(message); } |
- |
- private: |
- MessageQueue queue_; |
-}; |
- |
-class ConnectorDeletingMessageAccumulator : public MessageAccumulator { |
- public: |
- explicit ConnectorDeletingMessageAccumulator(internal::Connector** connector) |
- : connector_(connector) {} |
- |
- bool Accept(Message* message) override { |
- delete *connector_; |
- *connector_ = 0; |
- return MessageAccumulator::Accept(message); |
- } |
- |
- private: |
- internal::Connector** connector_; |
-}; |
- |
-class ReentrantMessageAccumulator : public MessageAccumulator { |
- public: |
- explicit ReentrantMessageAccumulator(internal::Connector* connector) |
- : connector_(connector), number_of_calls_(0) {} |
- |
- bool Accept(Message* message) override { |
- if (!MessageAccumulator::Accept(message)) |
- return false; |
- number_of_calls_++; |
- if (number_of_calls_ == 1) { |
- return connector_->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE); |
- } |
- return true; |
- } |
- |
- int number_of_calls() { return number_of_calls_; } |
- |
- private: |
- internal::Connector* connector_; |
- int number_of_calls_; |
-}; |
- |
class ConnectorTest : public testing::Test { |
public: |
ConnectorTest() {} |
@@ -98,6 +47,27 @@ class ConnectorTest : public testing::Test { |
private: |
Environment env_; |
RunLoop loop_; |
+ |
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ConnectorTest); |
+}; |
+ |
+class MessageAccumulator : public MessageReceiver { |
+ public: |
+ MessageAccumulator() {} |
+ |
+ bool Accept(Message* message) override { |
+ queue_.Push(message); |
+ return true; |
+ } |
+ |
+ bool IsEmpty() const { return queue_.IsEmpty(); } |
+ |
+ void Pop(Message* message) { queue_.Pop(message); } |
+ |
+ private: |
+ MessageQueue queue_; |
+ |
+ MOJO_DISALLOW_COPY_AND_ASSIGN(MessageAccumulator); |
}; |
TEST_F(ConnectorTest, Basic) { |
@@ -331,6 +301,23 @@ TEST_F(ConnectorTest, WaitForIncomingMessageWithError) { |
ASSERT_FALSE(connector0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE)); |
} |
+class ConnectorDeletingMessageAccumulator : public MessageAccumulator { |
+ public: |
+ explicit ConnectorDeletingMessageAccumulator(internal::Connector** connector) |
+ : connector_(connector) {} |
+ |
+ bool Accept(Message* message) override { |
+ delete *connector_; |
+ *connector_ = 0; |
+ return MessageAccumulator::Accept(message); |
+ } |
+ |
+ private: |
+ internal::Connector** connector_; |
+ |
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ConnectorDeletingMessageAccumulator); |
+}; |
+ |
TEST_F(ConnectorTest, WaitForIncomingMessageWithDeletion) { |
internal::Connector connector0(handle0_.Pass()); |
internal::Connector* connector1 = new internal::Connector(handle1_.Pass()); |
@@ -358,6 +345,30 @@ TEST_F(ConnectorTest, WaitForIncomingMessageWithDeletion) { |
std::string(reinterpret_cast<const char*>(message_received.payload()))); |
} |
+class ReentrantMessageAccumulator : public MessageAccumulator { |
+ public: |
+ explicit ReentrantMessageAccumulator(internal::Connector* connector) |
+ : connector_(connector), number_of_calls_(0) {} |
+ |
+ bool Accept(Message* message) override { |
+ if (!MessageAccumulator::Accept(message)) |
+ return false; |
+ number_of_calls_++; |
+ if (number_of_calls_ == 1) { |
+ return connector_->WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE); |
+ } |
+ return true; |
+ } |
+ |
+ int number_of_calls() { return number_of_calls_; } |
+ |
+ private: |
+ internal::Connector* connector_; |
+ int number_of_calls_; |
+ |
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ReentrantMessageAccumulator); |
+}; |
+ |
TEST_F(ConnectorTest, WaitForIncomingMessageWithReentrancy) { |
internal::Connector connector0(handle0_.Pass()); |
internal::Connector connector1(handle1_.Pass()); |
@@ -390,6 +401,71 @@ TEST_F(ConnectorTest, WaitForIncomingMessageWithReentrancy) { |
ASSERT_EQ(2, accumulator.number_of_calls()); |
} |
+// This message receiver just accepts messages, and responds (to another fixed |
+// receiver) |
+class NoTaskStarvationReplier : public MessageReceiver { |
+ public: |
+ explicit NoTaskStarvationReplier(MessageReceiver* reply_to) |
+ : reply_to_(reply_to) { |
+ MOJO_CHECK(reply_to_ != this); |
+ } |
+ |
+ bool Accept(Message* message) override { |
+ num_accepted_++; |
+ |
+ uint32_t name = message->name(); |
+ |
+ if (name >= 10u) { |
+ RunLoop::current()->PostDelayedTask([]() { RunLoop::current()->Quit(); }, |
+ 0); |
+ } |
+ |
+ // We don't necessarily expect the quit task to be processed immediately, |
+ // but if some large number (say, ten thousand-ish) messages have been |
+ // processed, we can say that starvation has occurred. |
+ static const uint32_t kStarvationThreshold = 10000; |
+ EXPECT_LE(name, kStarvationThreshold); |
+ // We'd prefer our test not hang, so don't send the reply in the failing |
+ // case. |
+ if (name > kStarvationThreshold) |
+ return true; |
+ |
+ MessageBuilder builder(name + 1u, 0u); |
+ MOJO_CHECK(reply_to_->Accept(builder.message())); |
+ |
+ return true; |
+ } |
+ |
+ unsigned num_accepted() const { return num_accepted_; } |
+ |
+ private: |
+ MessageReceiver* const reply_to_; |
+ unsigned num_accepted_ = 0; |
+ |
+ MOJO_DISALLOW_COPY_AND_ASSIGN(NoTaskStarvationReplier); |
+}; |
+ |
+// TODO(vtl): This test currently fails. See the discussion on issue #604 |
+// (https://github.com/domokit/mojo/issues/604). |
+TEST_F(ConnectorTest, DISABLED_NoTaskStarvation) { |
+ internal::Connector connector0(handle0_.Pass()); |
+ internal::Connector connector1(handle1_.Pass()); |
+ |
+ // The replier will bounce messages to |connector0|, and will receiver |
+ // messages from |connector1|. |
+ NoTaskStarvationReplier replier(&connector0); |
+ connector1.set_incoming_receiver(&replier); |
+ |
+ // Kick things off by sending a messagge on |connector0| (starting with a |
+ // "name" of 1). |
+ MessageBuilder builder(1u, 0u); |
+ ASSERT_TRUE(connector0.Accept(builder.message())); |
+ |
+ PumpMessages(); |
+ |
+ EXPECT_GE(replier.num_accepted(), 10u); |
+} |
+ |
} // namespace |
} // namespace test |
} // namespace mojo |