| /* |
| * Copyright 2004 The WebRTC Project Authors. All rights reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "webrtc/base/messagequeue.h" |
| |
| #include "webrtc/base/bind.h" |
| #include "webrtc/base/gunit.h" |
| #include "webrtc/base/logging.h" |
| #include "webrtc/base/thread.h" |
| #include "webrtc/base/timeutils.h" |
| #include "webrtc/base/nullsocketserver.h" |
| #include "webrtc/test/testsupport/gtest_disable.h" |
| |
| using namespace rtc; |
| |
| class MessageQueueForTest : public MessageQueue { |
| public: |
| bool IsLocked_Worker() { |
| if (!crit_.TryEnter()) { |
| return true; |
| } |
| crit_.Leave(); |
| return false; |
| } |
| |
| bool IsLocked() { |
| // We have to do this on a worker thread, or else the TryEnter will |
| // succeed, since our critical sections are reentrant. |
| Thread worker; |
| worker.Start(); |
| return worker.Invoke<bool>( |
| rtc::Bind(&MessageQueueForTest::IsLocked_Worker, this)); |
| } |
| |
| size_t GetDmsgqSize() { |
| return dmsgq_.size(); |
| } |
| |
| const DelayedMessage& GetDmsgqTop() { |
| return dmsgq_.top(); |
| } |
| }; |
| |
| class MessageQueueTest : public testing::Test { |
| protected: |
| MessageQueueForTest q_; |
| }; |
| |
| struct DeletedLockChecker { |
| DeletedLockChecker(MessageQueueForTest* q, bool* was_locked, bool* deleted) |
| : q_(q), was_locked(was_locked), deleted(deleted) { } |
| ~DeletedLockChecker() { |
| *deleted = true; |
| *was_locked = q_->IsLocked(); |
| } |
| MessageQueueForTest* q_; |
| bool* was_locked; |
| bool* deleted; |
| }; |
| |
| static void DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder( |
| MessageQueue* q) { |
| EXPECT_TRUE(q != NULL); |
| TimeStamp now = Time(); |
| q->PostAt(now, NULL, 3); |
| q->PostAt(now - 2, NULL, 0); |
| q->PostAt(now - 1, NULL, 1); |
| q->PostAt(now, NULL, 4); |
| q->PostAt(now - 1, NULL, 2); |
| |
| Message msg; |
| for (size_t i=0; i<5; ++i) { |
| memset(&msg, 0, sizeof(msg)); |
| EXPECT_TRUE(q->Get(&msg, 0)); |
| EXPECT_EQ(i, msg.message_id); |
| } |
| |
| EXPECT_FALSE(q->Get(&msg, 0)); // No more messages |
| } |
| |
| TEST_F(MessageQueueTest, |
| DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder) { |
| DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder(&q_); |
| NullSocketServer nullss; |
| MessageQueue q_nullss(&nullss); |
| DelayedPostsWithIdenticalTimesAreProcessedInFifoOrder(&q_nullss); |
| } |
| |
| TEST_F(MessageQueueTest, DisposeNotLocked) { |
| bool was_locked = true; |
| bool deleted = false; |
| DeletedLockChecker* d = new DeletedLockChecker(&q_, &was_locked, &deleted); |
| q_.Dispose(d); |
| Message msg; |
| EXPECT_FALSE(q_.Get(&msg, 0)); |
| EXPECT_TRUE(deleted); |
| EXPECT_FALSE(was_locked); |
| } |
| |
| class DeletedMessageHandler : public MessageHandler { |
| public: |
| explicit DeletedMessageHandler(bool* deleted) : deleted_(deleted) { } |
| ~DeletedMessageHandler() { |
| *deleted_ = true; |
| } |
| void OnMessage(Message* msg) { } |
| private: |
| bool* deleted_; |
| }; |
| |
| // TODO(decurtis): Test that ordering of elements is done properly. |
| // TODO(decurtis): Test that timestamps are being properly set. |
| |
| TEST_F(MessageQueueTest, DisposeHandlerWithPostedMessagePending) { |
| bool deleted = false; |
| DeletedMessageHandler *handler = new DeletedMessageHandler(&deleted); |
| // First, post a dispose. |
| q_.Dispose(handler); |
| // Now, post a message, which should *not* be returned by Get(). |
| q_.Post(handler, 1); |
| Message msg; |
| EXPECT_FALSE(q_.Get(&msg, 0)); |
| EXPECT_TRUE(deleted); |
| } |
| |
| // Test Clear for removing messages that have been posted for times in |
| // the past. |
| TEST_F(MessageQueueTest, ClearPast) { |
| TimeStamp now = Time(); |
| Message msg; |
| |
| // Test removing the only element. |
| q_.PostAt(now - 4, NULL, 1); |
| q_.Clear(NULL, 1, NULL); |
| |
| // Make sure the queue is empty now. |
| EXPECT_FALSE(q_.Get(&msg, 0)); |
| |
| // Test removing the one element with a two element list. |
| q_.PostAt(now - 4, NULL, 1); |
| q_.PostAt(now - 2, NULL, 3); |
| |
| q_.Clear(NULL, 1, NULL); |
| |
| EXPECT_TRUE(q_.Get(&msg, 0)); |
| EXPECT_EQ(3U, msg.message_id); |
| |
| // Make sure the queue is empty now. |
| EXPECT_FALSE(q_.Get(&msg, 0)); |
| |
| |
| // Test removing the three element with a two element list. |
| q_.PostAt(now - 4, NULL, 1); |
| q_.PostAt(now - 2, NULL, 3); |
| |
| q_.Clear(NULL, 3, NULL); |
| |
| EXPECT_TRUE(q_.Get(&msg, 0)); |
| EXPECT_EQ(1U, msg.message_id); |
| |
| // Make sure the queue is empty now. |
| EXPECT_FALSE(q_.Get(&msg, 0)); |
| |
| |
| // Test removing the two element in a three element list. |
| q_.PostAt(now - 4, NULL, 1); |
| q_.PostAt(now - 3, NULL, 2); |
| q_.PostAt(now - 2, NULL, 3); |
| |
| q_.Clear(NULL, 2, NULL); |
| |
| EXPECT_TRUE(q_.Get(&msg, 0)); |
| EXPECT_EQ(1U, msg.message_id); |
| |
| EXPECT_TRUE(q_.Get(&msg, 0)); |
| EXPECT_EQ(3U, msg.message_id); |
| |
| // Make sure the queue is empty now. |
| EXPECT_FALSE(q_.Get(&msg, 0)); |
| |
| |
| // Test not clearing any messages. |
| q_.PostAt(now - 4, NULL, 1); |
| q_.PostAt(now - 3, NULL, 2); |
| q_.PostAt(now - 2, NULL, 3); |
| |
| // Remove nothing. |
| q_.Clear(NULL, 0, NULL); |
| q_.Clear(NULL, 4, NULL); |
| |
| EXPECT_TRUE(q_.Get(&msg, 0)); |
| EXPECT_EQ(1U, msg.message_id); |
| |
| EXPECT_TRUE(q_.Get(&msg, 0)); |
| EXPECT_EQ(2U, msg.message_id); |
| |
| EXPECT_TRUE(q_.Get(&msg, 0)); |
| EXPECT_EQ(3U, msg.message_id); |
| |
| // Make sure the queue is empty now. |
| EXPECT_FALSE(q_.Get(&msg, 0)); |
| } |
| |
| // Test clearing messages that have been posted for the future. |
| TEST_F(MessageQueueTest, ClearFuture) { |
| EXPECT_EQ(0U, q_.GetDmsgqSize()); |
| q_.PostDelayed(10, NULL, 4); |
| EXPECT_EQ(1U, q_.GetDmsgqSize()); |
| q_.PostDelayed(13, NULL, 4); |
| EXPECT_EQ(2U, q_.GetDmsgqSize()); |
| q_.PostDelayed(9, NULL, 2); |
| EXPECT_EQ(3U, q_.GetDmsgqSize()); |
| q_.PostDelayed(11, NULL, 10); |
| EXPECT_EQ(4U, q_.GetDmsgqSize()); |
| |
| EXPECT_EQ(9, q_.GetDmsgqTop().cmsDelay_); |
| |
| MessageList removed; |
| q_.Clear(NULL, 10, &removed); |
| EXPECT_EQ(1U, removed.size()); |
| EXPECT_EQ(3U, q_.GetDmsgqSize()); |
| |
| removed.clear(); |
| q_.Clear(NULL, 4, &removed); |
| EXPECT_EQ(2U, removed.size()); |
| EXPECT_EQ(1U, q_.GetDmsgqSize()); |
| |
| removed.clear(); |
| q_.Clear(NULL, 4, &removed); |
| EXPECT_EQ(0U, removed.size()); |
| EXPECT_EQ(1U, q_.GetDmsgqSize()); |
| |
| removed.clear(); |
| q_.Clear(NULL, 2, &removed); |
| EXPECT_EQ(1U, removed.size()); |
| EXPECT_EQ(0U, q_.GetDmsgqSize()); |
| |
| Message msg; |
| EXPECT_FALSE(q_.Get(&msg, 0)); |
| } |
| |
| |
| struct UnwrapMainThreadScope { |
| UnwrapMainThreadScope() : rewrap_(Thread::Current() != NULL) { |
| if (rewrap_) ThreadManager::Instance()->UnwrapCurrentThread(); |
| } |
| ~UnwrapMainThreadScope() { |
| if (rewrap_) ThreadManager::Instance()->WrapCurrentThread(); |
| } |
| private: |
| bool rewrap_; |
| }; |
| |
| TEST(MessageQueueManager, DeletedHandler) { |
| UnwrapMainThreadScope s; |
| if (MessageQueueManager::IsInitialized()) { |
| LOG(LS_INFO) << "Unable to run MessageQueueManager::Clear test, since the " |
| << "MessageQueueManager was already initialized by some " |
| << "other test in this run."; |
| return; |
| } |
| bool deleted = false; |
| DeletedMessageHandler* handler = new DeletedMessageHandler(&deleted); |
| delete handler; |
| EXPECT_TRUE(deleted); |
| EXPECT_FALSE(MessageQueueManager::IsInitialized()); |
| } |