Index: test/TimeoutTest.cpp |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/test/TimeoutTest.cpp |
@@ -0,0 +1,220 @@ |
+/* |
+ * This file is part of Adblock Plus <https://adblockplus.org/>, |
+ * Copyright (C) 2006-2017 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 "../src/Timeout.h" |
+#include "BaseJsTest.h" |
+#include "PausePoint.h" |
+#include <thread> |
+ |
+TEST(Timeout, Instance) |
+{ |
+ DelayedExecutor x([]() {}); |
+} |
+ |
+TEST(Timeout, ConstructorBodyMayNotBeNull) |
+{ |
+ auto nullBody = std::function<void()>(); |
+ ASSERT_ANY_THROW({ DelayedExecutor x(nullBody); }); |
+} |
+ |
+TEST(Timeout, ImmediateRunSimple) |
+{ |
+ int count = 0; |
+ DelayedExecutor x([&]() { ++count; }); |
+ x.RunImmediately(); |
+ EXPECT_EQ(1, count); |
+} |
+ |
+TEST(Timeout, DelayedRunSimple) |
+{ |
+ int count = 0; |
+ DelayedExecutor x([&]() { ++count; }); |
+ x.RunWithDelay(std::chrono::milliseconds(1)); |
+ EXPECT_EQ(1, count); |
+} |
+ |
+/** |
+ * Test class exposes each aspect point in the constructor. |
+ */ |
+class DelayedExecutorTestAspect |
+ : public AspectBase<DelayedExecutorTestAspect> |
+{ |
+ std::function<void()> runnableToSleeping; |
+ std::function<void()> runFinish; |
+ |
+public: |
+ template<class LockType> |
+ void RunnableToSleeping(LockType& lock) |
+ { |
+ AntiLock<LockType> x(lock); |
+ runnableToSleeping(); |
+ }; |
+ |
+ void RunFinish() |
+ { |
+ runFinish(); |
+ } |
+ |
+ DelayedExecutorTestAspect(std::function<void()> runnableToSleeping, |
+ std::function<void()> runFinish) |
+ : runnableToSleeping(runnableToSleeping), |
+ runFinish(runFinish) |
+ {} |
+}; |
+ |
+class DelayedExecutorForTesting |
+ : public DelayedExecutorAspected<DelayedExecutorTestAspect> |
+{ |
+public: |
+ typedef DelayedExecutorAspected<DelayedExecutorTestAspect> Base; |
+ DelayedExecutorForTesting(std::function<void()> body, |
+ std::function<void()> runnableToSleeping, |
+ std::function<void()> runFinish) |
+ : Base(DelayedExecutorTestAspect(runnableToSleeping, runFinish), body) |
+ {} |
+}; |
+ |
+/** |
+ * Exercises an ordinary sequence for RunWithDelay(). |
+ * This version tracks the sequence through test aspect points. |
+ */ |
+TEST(Timeout, DelayedRunAspected) |
+{ |
+ int sequence = 0; |
+ auto body = [&]() |
+ { |
+ ASSERT_EQ(1, sequence); |
+ sequence = 2; |
+ }; |
+ auto runnableToSleeping = [&]() |
+ { |
+ ASSERT_EQ(0, sequence); |
+ sequence = 1; |
+ }; |
+ auto runFinish = [&]() |
+ { |
+ ASSERT_EQ(2, sequence); |
+ sequence = 3; |
+ }; |
+ |
+ DelayedExecutorForTesting x(body, runnableToSleeping, runFinish); |
+ ASSERT_EQ(0, sequence); |
+ x.RunWithDelay(std::chrono::milliseconds(1)); |
+ ASSERT_EQ(3, sequence); |
+} |
+ |
+/** |
+ * Exercises an ordinary sequence for RunWithDelay(). |
+ * This version is in a separate thread. |
+ */ |
+TEST(Timeout, DelayedRunAspectedThreaded) |
+{ |
+ PausePoint pp; |
+ int sequence = 0; |
+ auto body = [&]() |
+ { |
+ sequence = 3; |
+ pp.Pause(); |
+ }; |
+ auto runnableToSleeping = [&]() |
+ { |
+ sequence = 2; |
+ pp.Pause(); |
+ }; |
+ auto runFinish = [&]() |
+ { |
+ sequence = 4; |
+ pp.Pause(); |
+ }; |
+ |
+ std::thread th([&]() |
+ { |
+ DelayedExecutorForTesting x(body, runnableToSleeping, runFinish); |
+ sequence = 1; |
+ pp.Pause(); |
+ x.RunWithDelay(std::chrono::milliseconds(1)); |
+ sequence = 5; |
+ pp.Pause(); |
+ sequence = 6; |
+ }); |
+ for (int j = 1; j <= 5; ++j) { |
+ pp.WaitUntilPaused(); |
+ ASSERT_EQ(j, sequence); |
+ pp.Resume(); |
+ } |
+ th.join(); |
+ ASSERT_EQ(6, sequence); |
+} |
+ |
+TEST(Timeout, DelayedRunInterrupted) |
+{ |
+ PausePoint pp; |
+ int sequence = 0; |
+ auto body = [&]() |
+ { |
+ sequence = 3; |
+ pp.Pause(); |
+ }; |
+ auto runnableToSleeping = [&]() |
+ { |
+ sequence = 2; |
+ pp.Pause(); |
+ }; |
+ auto runFinish = [&]() |
+ { |
+ sequence = 4; |
+ pp.Pause(); |
+ }; |
+ |
+ DelayedExecutorForTesting x(body, runnableToSleeping, runFinish); |
+ ASSERT_EQ(0, sequence); |
+ std::thread th([&]() |
+ { |
+ sequence = 1; |
+ pp.Pause(); |
+ x.RunWithDelay(std::chrono::minutes(2)); // long delay to interrupt |
+ sequence = 5; |
+ pp.Pause(); |
+ sequence = 6; |
+ }); |
+ pp.WaitUntilPaused(); |
+ ASSERT_EQ(1, sequence); |
+ pp.Resume(); |
+ pp.WaitUntilPaused(); |
+ ASSERT_EQ(2, sequence); |
+ /* |
+ * We are at an aspect point where we're about to wait. |
+ * We don't have the capability of interrupting the wait itself, |
+ * so we rely on a heuristic. |
+ * We resume and sleep for a bit to give the scheduler a chance to wake up |
+ * the thread and for the condition variable to begin waiting. |
+ * It's possible to get occasional failures with this algorithm. |
+ */ |
+ pp.ResumeAndSleep(2); |
+ // Assume the condition variable is waiting. |
+ x.ForestallExecution(); |
+ // We skip sequence 3, where the body executes. |
+ // We skip sequence 4, where DelayedExecutor changes state after execution |
+ pp.WaitUntilPaused(); |
+ ASSERT_EQ(5, sequence); |
+ pp.Resume(); |
+ th.join(); |
+ ASSERT_EQ(6, sequence); |
+} |
+ |
+ |
+ |