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); |
+} |