| Index: test/plugin/ActiveQueueTest.cpp |
| =================================================================== |
| new file mode 100644 |
| --- /dev/null |
| +++ b/test/plugin/ActiveQueueTest.cpp |
| @@ -0,0 +1,183 @@ |
| +/* |
| + * This file is part of Adblock Plus <https://adblockplus.org/>, |
| + * Copyright (C) 2006-2015 Eyeo GmbH |
| + * |
| + * Adblock Plus is free software: you can redistribute it and/or modify |
| + * it under the terms of the GNU General Public License version 3 as |
| + * published by the Free Software Foundation. |
| + * |
| + * Adblock Plus is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| + */ |
| +#include <gtest/gtest.h> |
| +#include "../../src/plugin/ActiveQueue.h" |
| +#include <ostream> |
| +#include <memory> |
| + |
| +class TrivialIntMessageQueue |
| + : public MessageQueue<int> |
| +{ |
| +public: |
| + TrivialIntMessageQueue() |
| + : MessageQueue() |
| + {} |
| +}; |
| + |
| +TEST(MessageQueue, Instance) |
| +{ |
| + TrivialIntMessageQueue mq; |
| +} |
| + |
| +TEST(MessageQueue, Insert) |
| +{ |
| + TrivialIntMessageQueue mq; |
| + mq.Insert(5); |
| +} |
| + |
| +TEST(MessageQueue, RvalueInsertRemove) |
| +{ |
| + TrivialIntMessageQueue mq; |
| + mq.Insert(2+3); // int&& |
| + int result; |
| + auto haveResult = mq.Remove([&](int x) { result = x; }); |
| + ASSERT_TRUE(haveResult); |
| + ASSERT_EQ(5, result); |
| +} |
| + |
| +TEST(MessageQueue, ConstInsertRemove) |
| +{ |
| + TrivialIntMessageQueue mq; |
| + const int value = 4; |
| + mq.Insert(value); // const int& |
| + int result; |
| + auto haveResult = mq.Remove([&](int x) { result = x; }); |
| + ASSERT_TRUE(haveResult); |
| + ASSERT_EQ(4, result); |
| +} |
| + |
| +TEST(MessageQueue, Thread) |
| +{ |
| + bool threadTimeout = false; |
| + std::string threadFailMessage; |
| + MessageQueue<unsigned int> mq; |
| + static const unsigned int numberOfElements = 1000; |
| + unsigned int counter = 1; |
| + |
| + std::thread th([&]() -> void { |
| + // Begin body of notice thread |
| + try |
| + { |
| + /* |
| + * We use a nested loop with the same predicate because notice and wait are not related one-to-one. |
| + * Multiple calls to notify_one() may happen before the wait_for() statement is active. |
| + * Thus the outer loop manages the condition variable. |
| + * The inner loop removes all available queue elements, since there may be more than one per notice. |
| + */ |
| + while (counter < numberOfElements) |
| + { |
| + if (mq.WaitFor(std::chrono::seconds(10)) == std::cv_status::timeout) |
| + { |
| + threadFailMessage = "Thread timed out."; |
| + return; |
| + } |
| + bool noticeIsFresh = true; |
| + while (counter < numberOfElements) |
| + { |
| + unsigned int element; |
| + auto f = [&](unsigned int x) -> void { element = x; }; |
| + if (mq.Remove(f)) |
| + { |
| + if (element != counter) |
| + { |
| + std::ostringstream ss; |
| + ss << "Counter mismatch. expected = " << counter << ". actual = " << element; |
| + threadFailMessage = ss.str(); |
| + return; |
| + } |
| + noticeIsFresh = false; |
| + ++counter; // counter represents the number of elements removed, so only increment when remove() returns true |
| + } |
| + else if (noticeIsFresh) |
| + { |
| + threadFailMessage = "Notice received, but no element to remove from queue."; |
| + return; |
| + } |
| + } |
| + } |
| + } |
| + catch (std::exception &e) |
| + { |
| + threadFailMessage = std::string("Exception: ") + e.what(); |
| + } |
| + // End body of notice thread |
| + }); |
| + |
| + // Body of test thread |
| + for (unsigned int j = 1; j <= numberOfElements ; ++j) |
| + { |
| + mq.Insert(j); |
| + }; |
| + th.join(); |
| + if (!threadFailMessage.empty()) |
| + { |
| + FAIL() << threadFailMessage; |
| + } |
| + ASSERT_EQ(numberOfElements, counter); |
| +} |
| + |
| +/* |
| + * All the ActiveQueue tests instantiante the ActiveQueue in a separate block. |
| + * This ensures its internal thread has terminated before the end of the test, |
| + * and that the test will hang if it has not. |
| + */ |
| + |
| +TEST(ActiveQueue, Instance) |
| +{ |
| + bool seen = false; |
| + auto process = [&](unsigned int) { seen = true; }; |
| + { |
| + ActiveQueue<unsigned int, decltype(process)> aq(process); |
| + } |
| + ASSERT_FALSE(seen); |
| +} |
| + |
| +TEST(ActiveQueue, Single) |
| +{ |
| + bool seen = false; |
| + auto process = [&](unsigned int) { seen = true; }; |
| + { |
| + ActiveQueue<unsigned int, decltype(process)> aq(process); |
| + aq.Insert(0); |
| + } |
| + ASSERT_TRUE(seen); |
| +} |
| + |
| +TEST(ActiveQueue, Multiple) |
| +{ |
| + static const unsigned int numberOfElements = 1000; |
| + unsigned int counter = 0; |
| + std::string threadFailMessage; |
| + auto process = [&](unsigned int x) -> void { |
| + ++counter; |
| + if (x != counter) |
| + { |
| + std::ostringstream os; |
| + os << "Mismatch: expected counter=" << counter << ", received queue element=" << x << "."; |
| + threadFailMessage = os.str(); |
| + } |
| + }; |
| + { |
| + ActiveQueue<unsigned int, decltype(process)> aq(process); |
| + for (unsigned int j = 1; j <= numberOfElements; ++j) |
| + { |
| + aq.Insert(j); |
| + ASSERT_EQ("", threadFailMessage); |
| + }; |
| + } |
| + ASSERT_EQ(numberOfElements, counter); |
| +} |