// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/message_handler.h" #include "vm/unit_test.h" namespace dart { class MessageHandlerTestPeer { public: explicit MessageHandlerTestPeer(MessageHandler* handler) : handler_(handler) {} void PostMessage(Message* message) { handler_->PostMessage(message); } void ClosePort(Dart_Port port) { handler_->ClosePort(port); } void CloseAllPorts() { handler_->CloseAllPorts(); } void increment_live_ports() { handler_->increment_live_ports(); } void decrement_live_ports() { handler_->decrement_live_ports(); } MessageQueue* queue() const { return handler_->queue_; } MessageQueue* oob_queue() const { return handler_->oob_queue_; } private: MessageHandler* handler_; DISALLOW_COPY_AND_ASSIGN(MessageHandlerTestPeer); }; class TestMessageHandler : public MessageHandler { public: TestMessageHandler() : port_buffer_(strdup("")), notify_count_(0), message_count_(0), result_(true) { } ~TestMessageHandler() { free(port_buffer_); } void MessageNotify(Message::Priority priority) { notify_count_++; } bool HandleMessage(Message* message) { // For testing purposes, keep a string with a list of the ports // for all messages we receive. intptr_t len = OS::SNPrint(NULL, 0, "%s %d", port_buffer_, message->dest_port()) + 1; char* buffer = reinterpret_cast(malloc(len)); OS::SNPrint(buffer, len, "%s %d", port_buffer_, message->dest_port()); free(port_buffer_); port_buffer_ = buffer; delete message; message_count_++; return result_; } bool Start() { intptr_t len = OS::SNPrint(NULL, 0, "%s start", port_buffer_) + 1; char* buffer = reinterpret_cast(malloc(len)); OS::SNPrint(buffer, len, "%s start", port_buffer_); free(port_buffer_); port_buffer_ = buffer; return true; } void End() { intptr_t len = OS::SNPrint(NULL, 0, "%s end", port_buffer_) + 1; char* buffer = reinterpret_cast(malloc(len)); OS::SNPrint(buffer, len, "%s end", port_buffer_); free(port_buffer_); port_buffer_ = buffer; } const char* port_buffer() const { return port_buffer_; } int notify_count() const { return notify_count_; } int message_count() const { return message_count_; } void set_result(bool result) { result_ = result; } private: char* port_buffer_; int notify_count_; int message_count_; bool result_; DISALLOW_COPY_AND_ASSIGN(TestMessageHandler); }; bool TestStartFunction(uword data) { return (reinterpret_cast(data))->Start(); } void TestEndFunction(uword data) { return (reinterpret_cast(data))->End(); } UNIT_TEST_CASE(MessageHandler_PostMessage) { TestMessageHandler handler; MessageHandlerTestPeer handler_peer(&handler); EXPECT_EQ(0, handler.notify_count()); // Post a message. Message* message = new Message(0, 0, NULL, Message::kNormalPriority); handler_peer.PostMessage(message); // The notify callback is called. EXPECT_EQ(1, handler.notify_count()); // The message has been added to the correct queue. EXPECT(message == handler_peer.queue()->Dequeue()); EXPECT(NULL == handler_peer.oob_queue()->Dequeue()); delete message; // Post an oob message. message = new Message(0, 0, NULL, Message::kOOBPriority); handler_peer.PostMessage(message); // The notify callback is called. EXPECT_EQ(2, handler.notify_count()); // The message has been added to the correct queue. EXPECT(message == handler_peer.oob_queue()->Dequeue()); EXPECT(NULL == handler_peer.queue()->Dequeue()); delete message; } UNIT_TEST_CASE(MessageHandler_ClosePort) { TestMessageHandler handler; MessageHandlerTestPeer handler_peer(&handler); Message* message1 = new Message(1, 0, NULL, Message::kNormalPriority); handler_peer.PostMessage(message1); Message* message2 = new Message(2, 0, NULL, Message::kNormalPriority); handler_peer.PostMessage(message2); handler_peer.ClosePort(1); // The message on port 1 is dropped from the queue. EXPECT(message2 == handler_peer.queue()->Dequeue()); EXPECT(NULL == handler_peer.queue()->Dequeue()); delete message2; } UNIT_TEST_CASE(MessageHandler_CloseAllPorts) { TestMessageHandler handler; MessageHandlerTestPeer handler_peer(&handler); Message* message1 = new Message(1, 0, NULL, Message::kNormalPriority); handler_peer.PostMessage(message1); Message* message2 = new Message(2, 0, NULL, Message::kNormalPriority); handler_peer.PostMessage(message2); handler_peer.CloseAllPorts(); // All messages are dropped from the queue. EXPECT(NULL == handler_peer.queue()->Dequeue()); } UNIT_TEST_CASE(MessageHandler_HandleNextMessage) { TestMessageHandler handler; MessageHandlerTestPeer handler_peer(&handler); Message* message1 = new Message(1, 0, NULL, Message::kNormalPriority); handler_peer.PostMessage(message1); Message* oob_message1 = new Message(3, 0, NULL, Message::kOOBPriority); handler_peer.PostMessage(oob_message1); Message* message2 = new Message(2, 0, NULL, Message::kNormalPriority); handler_peer.PostMessage(message2); Message* oob_message2 = new Message(4, 0, NULL, Message::kOOBPriority); handler_peer.PostMessage(oob_message2); // We handle both oob messages and a single normal message. EXPECT(handler.HandleNextMessage()); EXPECT_STREQ(" 3 4 1", handler.port_buffer()); handler_peer.CloseAllPorts(); } UNIT_TEST_CASE(MessageHandler_HandleOOBMessages) { TestMessageHandler handler; MessageHandlerTestPeer handler_peer(&handler); Message* message1 = new Message(1, 0, NULL, Message::kNormalPriority); handler_peer.PostMessage(message1); Message* message2 = new Message(2, 0, NULL, Message::kNormalPriority); handler_peer.PostMessage(message2); Message* oob_message1 = new Message(3, 0, NULL, Message::kOOBPriority); handler_peer.PostMessage(oob_message1); Message* oob_message2 = new Message(4, 0, NULL, Message::kOOBPriority); handler_peer.PostMessage(oob_message2); // We handle both oob messages but no normal messages. EXPECT(handler.HandleOOBMessages()); EXPECT_STREQ(" 3 4", handler.port_buffer()); handler_peer.CloseAllPorts(); } struct ThreadStartInfo { MessageHandler* handler; int count; }; static void SendMessages(uword param) { ThreadStartInfo* info = reinterpret_cast(param); MessageHandler* handler = info->handler; MessageHandlerTestPeer handler_peer(handler); for (int i = 0; i < info->count; i++) { Message* message = new Message(i + 1, 0, NULL, Message::kNormalPriority); handler_peer.PostMessage(message); } } UNIT_TEST_CASE(MessageHandler_Run) { ThreadPool pool; TestMessageHandler handler; MessageHandlerTestPeer handler_peer(&handler); int sleep = 0; const int kMaxSleep = 20 * 1000; // 20 seconds. EXPECT(!handler.HasLivePorts()); handler_peer.increment_live_ports(); handler.Run(&pool, TestStartFunction, TestEndFunction, reinterpret_cast(&handler)); Message* message = new Message(100, 0, NULL, Message::kNormalPriority); handler_peer.PostMessage(message); // Wait for the first message to be handled. while (sleep < kMaxSleep && handler.message_count() < 1) { OS::Sleep(10); sleep += 10; } EXPECT_STREQ(" start 100", handler.port_buffer()); // Start a thread which sends more messages. ThreadStartInfo info; info.handler = &handler; info.count = 10; Thread::Start(SendMessages, reinterpret_cast(&info)); while (sleep < kMaxSleep && handler.message_count() < 11) { OS::Sleep(10); sleep += 10; } EXPECT_STREQ(" start 100 1 2 3 4 5 6 7 8 9 10", handler.port_buffer()); handler_peer.decrement_live_ports(); EXPECT(!handler.HasLivePorts()); } } // namespace dart