| Index: test/Thread.cpp |
| =================================================================== |
| new file mode 100644 |
| --- /dev/null |
| +++ b/test/Thread.cpp |
| @@ -0,0 +1,146 @@ |
| +#include <gtest/gtest.h> |
| +#include <queue> |
| + |
| +#include "../src/Thread.h" |
| + |
| +namespace |
| +{ |
| +#ifndef WIN32 |
| + void Sleep(const int millis) |
| + { |
| + usleep(millis * 1000); |
| + } |
| +#endif |
| + |
| + class Mock : public AdblockPlus::Thread |
| + { |
| + public: |
| + int timesCalled; |
| + |
| + Mock() : timesCalled(0) |
| + { |
| + } |
| + |
| + void Run() |
| + { |
| + timesCalled++; |
|
Wladimir Palant
2013/04/03 13:14:47
This statement isn't thread-safe, you need a mutex
Felix Dahlke
2013/04/03 16:27:59
I thought that's pretty unlikely here, but fair en
|
| + } |
| + }; |
| + |
| + class LockingMock : public AdblockPlus::Thread |
| + { |
| + public: |
| + bool working; |
| + |
| + LockingMock(AdblockPlus::Thread::Mutex& mutex, const int millisToSleep) |
| + : mutex(mutex), millisToSleep(millisToSleep) |
| + { |
| + } |
| + |
| + void Run() |
| + { |
| + mutex.Lock(); |
| + working = true; |
| + Sleep(millisToSleep); |
| + working = false; |
| + mutex.Unlock(); |
| + } |
| + |
| + private: |
| + AdblockPlus::Thread::Mutex& mutex; |
| + int millisToSleep; |
| + }; |
| + |
| + class Enqueuer : public AdblockPlus::Thread |
| + { |
| + public: |
| + Enqueuer(std::queue<int>& queue, AdblockPlus::Thread::Mutex& mutex, |
| + AdblockPlus::Thread::Condition& notEmpty) |
| + : queue(queue), mutex(mutex), notEmpty(notEmpty) |
| + { |
| + } |
| + |
| + void Run() |
| + { |
| + Sleep(5); |
| + mutex.Lock(); |
| + queue.push(1); |
| + Sleep(10); |
| + notEmpty.Signal(); |
| + mutex.Unlock(); |
| + } |
| + |
| + private: |
| + std::queue<int>& queue; |
| + AdblockPlus::Thread::Mutex& mutex; |
| + AdblockPlus::Thread::Condition& notEmpty; |
| + }; |
| + |
| + class Dequeuer : public AdblockPlus::Thread |
| + { |
| + public: |
| + Dequeuer(std::queue<int>& queue, AdblockPlus::Thread::Mutex& mutex, |
| + AdblockPlus::Thread::Condition& notEmpty) |
| + : queue(queue), mutex(mutex), notEmpty(notEmpty) |
| + { |
| + } |
| + |
| + void Run() |
| + { |
| + mutex.Lock(); |
| + if (!queue.size()) |
| + notEmpty.Wait(mutex); |
| + queue.pop(); |
| + mutex.Unlock(); |
| + } |
| + |
| + private: |
| + std::queue<int>& queue; |
| + AdblockPlus::Thread::Mutex& mutex; |
| + AdblockPlus::Thread::Condition& notEmpty; |
| + }; |
| +} |
| + |
| +TEST(ThreadTest, Run) |
| +{ |
| + Mock mock; |
| + ASSERT_EQ(0, mock.timesCalled); |
| + mock.Start(); |
| + ASSERT_EQ(0, mock.timesCalled); |
|
Wladimir Palant
2013/04/03 13:14:47
This is a race condition - by the time you check t
Felix Dahlke
2013/04/03 16:27:59
Yes indeed, made it a bit more robust. It's still
|
| + mock.Join(); |
| + ASSERT_EQ(1, mock.timesCalled); |
| +} |
| + |
| +TEST(ThreadTest, Mutex) |
| +{ |
| + AdblockPlus::Thread::Mutex mutex; |
| + LockingMock mock1(mutex, 10); |
| + LockingMock mock2(mutex, 20); |
| + mock1.Start(); |
| + mock2.Start(); |
| + Sleep(5); |
| + ASSERT_TRUE(mock1.working); |
| + ASSERT_FALSE(mock2.working); |
|
Wladimir Palant
2013/04/03 13:14:47
Please note that Sleep() (both the Windows and the
Felix Dahlke
2013/04/03 16:27:59
Agreed, much more robust that way.
|
| + Sleep(10); |
| + ASSERT_TRUE(mock2.working); |
| + ASSERT_FALSE(mock1.working); |
| + mock1.Join(); |
| + mock2.Join(); |
| +} |
| + |
| +TEST(ThreadTest, ConditionVariable) |
| +{ |
| + std::queue<int> queue; |
| + AdblockPlus::Thread::Mutex mutex; |
| + AdblockPlus::Thread::Condition notEmpty; |
| + Enqueuer enqueuer(queue, mutex, notEmpty); |
| + Dequeuer dequeuer(queue, mutex, notEmpty); |
| + enqueuer.Start(); |
| + dequeuer.Start(); |
| + Sleep(10); |
| + ASSERT_EQ(1, queue.size()); |
|
Wladimir Palant
2013/04/03 13:14:47
Same flaw here as above.
Felix Dahlke
2013/04/03 16:27:59
Fixed as well.
|
| + Sleep(15); |
| + ASSERT_EQ(0, queue.size()); |
| + enqueuer.Join(); |
| + dequeuer.Join(); |
| +} |