summaryrefslogtreecommitdiff
path: root/src/unittest/test_threading.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/unittest/test_threading.cpp')
-rw-r--r--src/unittest/test_threading.cpp182
1 files changed, 182 insertions, 0 deletions
diff --git a/src/unittest/test_threading.cpp b/src/unittest/test_threading.cpp
new file mode 100644
index 000000000..f0df85b2d
--- /dev/null
+++ b/src/unittest/test_threading.cpp
@@ -0,0 +1,182 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "test.h"
+
+#include "threading/atomic.h"
+#include "threading/semaphore.h"
+#include "threading/thread.h"
+
+
+class TestThreading : public TestBase {
+public:
+ TestThreading() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestThreading"; }
+ void runTests(IGameDef *gamedef);
+
+ void testStartStopWait();
+ void testThreadKill();
+ void testAtomicSemaphoreThread();
+};
+
+static TestThreading g_test_instance;
+
+void TestThreading::runTests(IGameDef *gamedef)
+{
+ TEST(testStartStopWait);
+ TEST(testThreadKill);
+ TEST(testAtomicSemaphoreThread);
+}
+
+class SimpleTestThread : public Thread {
+public:
+ SimpleTestThread(unsigned int interval) :
+ Thread("SimpleTest"),
+ m_interval(interval)
+ {
+ }
+
+private:
+ void *run()
+ {
+ void *retval = this;
+
+ if (isCurrentThread() == false)
+ retval = (void *)0xBAD;
+
+ while (!stopRequested())
+ sleep_ms(m_interval);
+
+ return retval;
+ }
+
+ unsigned int m_interval;
+};
+
+void TestThreading::testStartStopWait()
+{
+ void *thread_retval;
+ SimpleTestThread *thread = new SimpleTestThread(25);
+
+ // Try this a couple times, since a Thread should be reusable after waiting
+ for (size_t i = 0; i != 5; i++) {
+ // Can't wait() on a joined, stopped thread
+ UASSERT(thread->wait() == false);
+
+ // start() should work the first time, but not the second.
+ UASSERT(thread->start() == true);
+ UASSERT(thread->start() == false);
+
+ UASSERT(thread->isRunning() == true);
+ UASSERT(thread->isCurrentThread() == false);
+
+ // Let it loop a few times...
+ sleep_ms(70);
+
+ // It's still running, so the return value shouldn't be available to us.
+ UASSERT(thread->getReturnValue(&thread_retval) == false);
+
+ // stop() should always succeed
+ UASSERT(thread->stop() == true);
+
+ // wait() only needs to wait the first time - the other two are no-ops.
+ UASSERT(thread->wait() == true);
+ UASSERT(thread->wait() == false);
+ UASSERT(thread->wait() == false);
+
+ // Now that the thread is stopped, we should be able to get the
+ // return value, and it should be the object itself.
+ thread_retval = NULL;
+ UASSERT(thread->getReturnValue(&thread_retval) == true);
+ UASSERT(thread_retval == thread);
+ }
+
+ delete thread;
+}
+
+
+void TestThreading::testThreadKill()
+{
+ SimpleTestThread *thread = new SimpleTestThread(300);
+
+ UASSERT(thread->start() == true);
+
+ // kill()ing is quite violent, so let's make sure our victim is sleeping
+ // before we do this... so we don't corrupt the rest of the program's state
+ sleep_ms(100);
+ UASSERT(thread->kill() == true);
+
+ // The state of the thread object should be reset if all went well
+ UASSERT(thread->isRunning() == false);
+ UASSERT(thread->start() == true);
+ UASSERT(thread->stop() == true);
+ UASSERT(thread->wait() == true);
+
+ // kill() after already waiting should fail.
+ UASSERT(thread->kill() == false);
+
+ delete thread;
+}
+
+
+class AtomicTestThread : public Thread {
+public:
+ AtomicTestThread(Atomic<u32> &v, Semaphore &trigger) :
+ Thread("AtomicTest"),
+ val(v),
+ trigger(trigger)
+ {
+ }
+
+private:
+ void *run()
+ {
+ trigger.wait();
+ for (u32 i = 0; i < 0x10000; ++i)
+ ++val;
+ return NULL;
+ }
+
+ Atomic<u32> &val;
+ Semaphore &trigger;
+};
+
+
+void TestThreading::testAtomicSemaphoreThread()
+{
+ Atomic<u32> val;
+ Semaphore trigger;
+ static const u8 num_threads = 4;
+
+ AtomicTestThread *threads[num_threads];
+ for (u8 i = 0; i < num_threads; ++i) {
+ threads[i] = new AtomicTestThread(val, trigger);
+ UASSERT(threads[i]->start());
+ }
+
+ trigger.post(num_threads);
+
+ for (u8 i = 0; i < num_threads; ++i) {
+ threads[i]->wait();
+ delete threads[i];
+ }
+
+ UASSERT(val == num_threads * 0x10000);
+}
+