This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/celix_err in repository https://gitbox.apache.org/repos/asf/celix.git
commit 40b85559e78d05174bf16a163d71525f92e34b98 Author: Pepijn Noltes <pepijnnol...@gmail.com> AuthorDate: Tue May 2 14:04:43 2023 +0200 Refactor threads cpputest to gtest and add a tss test --- libs/utils/CMakeLists.txt | 6 - libs/utils/gtest/CMakeLists.txt | 1 + libs/utils/gtest/src/ThreadsTestSuite.cc | 438 ++++++++++++++++++++ libs/utils/private/test/celix_threads_test.cpp | 537 ------------------------- 4 files changed, 439 insertions(+), 543 deletions(-) diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index d57c0c7c..83db2b41 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -128,10 +128,6 @@ if (ENABLE_TESTING) target_include_directories(array_list_test PRIVATE include_deprecated) target_link_libraries(array_list_test Celix::utils CppUTest::CppUTest pthread) - add_executable(celix_threads_test private/test/celix_threads_test.cpp) - target_include_directories(celix_threads_test PRIVATE include_deprecated) - target_link_libraries(celix_threads_test Celix::utils CppUTest::CppUTest ${CppUTest_EXT_LIBRARIES} pthread) - add_executable(linked_list_test private/test/linked_list_test.cpp) target_include_directories(linked_list_test PRIVATE include_deprecated) target_link_libraries(linked_list_test Celix::utils CppUTest::CppUTest pthread) @@ -160,7 +156,6 @@ if (ENABLE_TESTING) add_test(NAME run_array_list_test COMMAND array_list_test) add_test(NAME run_hash_map_test COMMAND hash_map_test) - add_test(NAME run_celix_threads_test COMMAND celix_threads_test) add_test(NAME run_linked_list_test COMMAND linked_list_test) add_test(NAME run_properties_test COMMAND properties_test) add_test(NAME run_ip_utils_test COMMAND ip_utils_test) @@ -168,7 +163,6 @@ if (ENABLE_TESTING) setup_target_for_coverage(array_list_test) setup_target_for_coverage(hash_map_test) - setup_target_for_coverage(celix_threads_test) setup_target_for_coverage(linked_list_test) setup_target_for_coverage(properties_test) setup_target_for_coverage(ip_utils_test) diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index e0c2951f..79c14238 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(test_utils src/CelixUtilsTestSuite.cc src/ConvertUtilsTestSuite.cc src/ErrTestSuite.cc + src/ThreadsTestSuite.cc ${CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS} ) diff --git a/libs/utils/gtest/src/ThreadsTestSuite.cc b/libs/utils/gtest/src/ThreadsTestSuite.cc new file mode 100644 index 00000000..6c1773b9 --- /dev/null +++ b/libs/utils/gtest/src/ThreadsTestSuite.cc @@ -0,0 +1,438 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <gtest/gtest.h> + +#include "celix_threads.h" + +class ThreadsTestSuite : public ::testing::Test { +public: +}; + +//----------------------TEST THREAD FUNCTION DECLARATIONS---------------------- +static void * thread_test_func_create(void *); +static void * thread_test_func_exit(void *); +static void * thread_test_func_detach(void *); +static void * thread_test_func_self(void *); +static void * thread_test_func_lock(void *); +static void * thread_test_func_cond_wait(void *); +static void * thread_test_func_cond_broadcast(void *); +static int thread_test_func_recur_lock(celix_thread_mutex_t*, int); +static void * thread_test_func_kill(void*); + +static void * thread_test_func_once(void*); +static void thread_test_func_once_init(); +static int thread_test_func_once_init_times_called = 0; + +struct func_param{ + int i, i2; + celix_thread_mutex_t mu, mu2; + celix_thread_cond_t cond, cond2; + celix_thread_once_t once_control; + celix_thread_rwlock_t rwlock; +}; + +//----------------------CELIX THREADS TESTS---------------------- + +TEST_F(ThreadsTestSuite, CreateTest) { + celix_thread_t thread; + int ret; + char* testStr = nullptr; + + ret = celixThread_create(&thread, nullptr, &thread_test_func_create, + &testStr); + EXPECT_EQ(CELIX_SUCCESS, ret); + celixThread_join(thread, nullptr); + + EXPECT_STREQ("SUCCESS", testStr); + + free(testStr); +} + +TEST_F(ThreadsTestSuite, ExitTest) { + int ret, *status; + celix_thread_t thread; + + ret = celixThread_create(&thread, nullptr, &thread_test_func_exit, nullptr); + EXPECT_EQ(CELIX_SUCCESS, ret); + celixThread_join(thread, (void**) &status); + EXPECT_EQ(666, *status); + free(status); +} + +TEST_F(ThreadsTestSuite, DetachTest) { + int ret; + celix_thread_t thread; + + celixThread_create(&thread, nullptr, thread_test_func_detach, nullptr); + ret = celixThread_detach(thread); + EXPECT_EQ(CELIX_SUCCESS, ret); +} + +TEST_F(ThreadsTestSuite, SelfTest) { + celix_thread_t thread; + celix_thread_t thread2; + + celixThread_create(&thread, nullptr, thread_test_func_self, &thread2); + celixThread_join(thread, nullptr); + EXPECT_TRUE(celixThread_equals(thread, thread2)); +} + +TEST_F(ThreadsTestSuite, InitializedTest) { + celix_thread_t thread; + EXPECT_FALSE(celixThread_initialized(thread)); + + celixThread_create(&thread, nullptr, thread_test_func_detach, nullptr); + EXPECT_TRUE(celixThread_initialized(thread)); + celixThread_join(thread, nullptr); +} + +TEST_F(ThreadsTestSuite, CnceTest) { + int *status; + celix_thread_t thread; + celix_thread_t thread2; + struct func_param* params = (struct func_param*) calloc(1,sizeof(struct func_param)); + + celixThread_create(&thread, nullptr, &thread_test_func_once, params); + celixThread_join(thread, (void**) &status); + + celixThread_create(&thread2, nullptr, &thread_test_func_once, params); + celixThread_join(thread2, (void**) &status); + + free(params); + + EXPECT_EQ(1, thread_test_func_once_init_times_called); +} + +//----------------------CELIX THREADS KILL TESTS---------------------- + +TEST_F(ThreadsTestSuite, KillTest){ + //setup signal handler to ignore SIGUSR1 + struct sigaction sigact; + struct sigaction sigactold; + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = [](int) {/*nop*/};; + sigaction(SIGUSR1, &sigact, &sigactold); + + + int * ret; + celix_thread_t thread; + celixThread_create(&thread, nullptr, thread_test_func_kill, nullptr); + sleep(1); + + celixThread_kill(thread, SIGUSR1); + celixThread_join(thread, (void**)&ret); + + EXPECT_EQ(-1, *ret); // -1 returned from usleep (interrupted by signal) + free(ret); + + sigaction(SIGUSR1, &sigactold, nullptr); +} + +//----------------------CELIX THREADS MUTEX TESTS---------------------- + +TEST_F(ThreadsTestSuite, CreateMutexTest) { + celix_thread_mutex_t mu; + EXPECT_EQ(CELIX_SUCCESS, celixThreadMutex_create(&mu, nullptr)); + EXPECT_EQ(CELIX_SUCCESS, celixThreadMutex_destroy(&mu)); +} + + +TEST_F(ThreadsTestSuite, LockTest) { + celix_thread_t thread; + struct func_param * params = (struct func_param*) calloc(1, + sizeof(struct func_param)); + + celixThreadMutex_create(¶ms->mu, nullptr); + + celixThreadMutex_lock(¶ms->mu); + celixThread_create(&thread, nullptr, thread_test_func_lock, params); + + sleep(1); + + EXPECT_EQ(0, params->i); + + //possible race condition, not perfect test + celixThreadMutex_unlock(¶ms->mu); + + sleep(1); + + celixThreadMutex_lock(¶ms->mu2); + EXPECT_EQ(666, params->i); + celixThreadMutex_unlock(¶ms->mu2); + celixThread_join(thread, nullptr); + free(params); +} + +TEST_F(ThreadsTestSuite, AttrCreateTest) { + celix_thread_mutexattr_t mu_attr; + EXPECT_EQ(CELIX_SUCCESS, celixThreadMutexAttr_create(&mu_attr)); + EXPECT_EQ(CELIX_SUCCESS, celixThreadMutexAttr_destroy(&mu_attr)); +} + +TEST_F(ThreadsTestSuite, AttrSettypeTest) { + celix_thread_mutex_t mu; + celix_thread_mutexattr_t mu_attr; + celixThreadMutexAttr_create(&mu_attr); + + //test recursive mutex + celixThreadMutexAttr_settype(&mu_attr, CELIX_THREAD_MUTEX_RECURSIVE); + celixThreadMutex_create(&mu, &mu_attr); + //if program doesnt deadlock: success! also check factorial of 10, for reasons unknown + EXPECT_EQ(3628800, thread_test_func_recur_lock(&mu, 10)); + celixThreadMutex_destroy(&mu); + + //test deadlock check mutex + celixThreadMutexAttr_settype(&mu_attr, CELIX_THREAD_MUTEX_ERRORCHECK); + celixThreadMutex_create(&mu, &mu_attr); + //get deadlock error + celixThreadMutex_lock(&mu); + EXPECT_TRUE(celixThreadMutex_lock(&mu) != CELIX_SUCCESS); + //do not get deadlock error + celixThreadMutex_unlock(&mu); + EXPECT_EQ(CELIX_SUCCESS, celixThreadMutex_lock(&mu)); + //get unlock error + celixThreadMutex_unlock(&mu); + EXPECT_TRUE(celixThreadMutex_unlock(&mu) != CELIX_SUCCESS); + + celixThreadMutex_destroy(&mu); + celixThreadMutexAttr_destroy(&mu_attr); +} + +//----------------------CELIX THREAD CONDITIONS TESTS---------------------- + +TEST_F(ThreadsTestSuite, InitCondTest) { + celix_thread_cond_t cond; + EXPECT_EQ(CELIX_SUCCESS, celixThreadCondition_init(&cond, nullptr)); + EXPECT_EQ(CELIX_SUCCESS, celixThreadCondition_destroy(&cond)); +} + +//test wait and signal +TEST_F(ThreadsTestSuite, WaitCondTest) { + celix_thread_t thread; + struct func_param * param = (struct func_param*) calloc(1, + sizeof(struct func_param)); + celixThreadMutex_create(¶m->mu, nullptr); + celixThreadCondition_init(¶m->cond, nullptr); + + celixThreadMutex_lock(¶m->mu); + + sleep(2); + + celixThread_create(&thread, nullptr, thread_test_func_cond_wait, param); + EXPECT_EQ(0, param->i); + + celixThreadCondition_wait(¶m->cond, ¶m->mu); + EXPECT_EQ(666, param->i); + celixThreadMutex_unlock(¶m->mu); + + celixThread_join(thread, nullptr); + free(param); +} + +//test wait and broadcast on multiple threads +TEST_F(ThreadsTestSuite, CondBroadcastTest) { + celix_thread_t thread; + celix_thread_t thread2; + struct func_param * param = (struct func_param*) calloc(1,sizeof(struct func_param)); + celixThreadMutex_create(¶m->mu, nullptr); + celixThreadMutex_create(¶m->mu2, nullptr); + celixThreadCondition_init(¶m->cond, nullptr); + + celixThread_create(&thread, nullptr, thread_test_func_cond_broadcast, param); + celixThread_create(&thread2, nullptr, thread_test_func_cond_broadcast, param); + + sleep(1); + celixThreadMutex_lock(¶m->mu); + EXPECT_EQ(0, param->i); + celixThreadMutex_unlock(¶m->mu); + + celixThreadMutex_lock(¶m->mu); + celixThreadCondition_broadcast(¶m->cond); + celixThreadMutex_unlock(¶m->mu); + sleep(1); + celixThreadMutex_lock(¶m->mu); + EXPECT_EQ(2, param->i); + celixThreadMutex_unlock(¶m->mu); + + celixThread_join(thread, nullptr); + celixThread_join(thread2, nullptr); + free(param); +} + +//----------------------CELIX READ-WRITE LOCK TESTS---------------------- + +TEST_F(ThreadsTestSuite, CreateRwLockTest) { + celix_thread_rwlock_t alock; + celix_status_t status; + status = celixThreadRwlock_create(&alock, nullptr); + if (status != CELIX_SUCCESS) { + fprintf(stderr, "Found error '%s'\n", strerror(status)); + } + EXPECT_EQ(CELIX_SUCCESS, status); + status = celixThreadRwlock_destroy(&alock); + EXPECT_EQ(CELIX_SUCCESS, status); +} + +TEST_F(ThreadsTestSuite, ReadLockTest) { + int status; + celix_thread_rwlock_t lock; + memset(&lock, 0x00, sizeof(celix_thread_rwlock_t)); + //struct func_param * param = (struct func_param*) calloc(1,sizeof(struct func_param)); + + celixThreadRwlock_create(&lock, nullptr); + + status = celixThreadRwlock_readLock(&lock); + EXPECT_EQ(0, status); + status = celixThreadRwlock_readLock(&lock); + EXPECT_EQ(0, status); + status = celixThreadRwlock_unlock(&lock); + EXPECT_EQ(0, status); + status = celixThreadRwlock_unlock(&lock); + EXPECT_EQ(0, status); + + celixThreadRwlock_destroy(&lock); +} + +TEST_F(ThreadsTestSuite, WriteLockTest) { + int status; + celix_thread_rwlock_t lock; + celixThreadRwlock_create(&lock, nullptr); + + status = celixThreadRwlock_writeLock(&lock); + EXPECT_EQ(0, status); + status = celixThreadRwlock_writeLock(&lock); + //EDEADLK ErNo: Resource deadlock avoided + EXPECT_EQ(EDEADLK, status); + + celixThreadRwlock_unlock(&lock); +} + +TEST_F(ThreadsTestSuite, RwLockAttrTest) { + celix_thread_rwlockattr_t attr; + celixThreadRwlockAttr_create(&attr); + celixThreadRwlockAttr_destroy(&attr); +} + +TEST_F(ThreadsTestSuite, TssTest) { + celix_tss_key_t key; + celix_status_t status = celix_tss_create(&key, [](void* ptr) { free(ptr); }); + EXPECT_EQ(CELIX_SUCCESS, status); + + int* value = (int*)malloc(sizeof(int)); + *value = 123; + status = celix_tss_set(key, value); + EXPECT_EQ(CELIX_SUCCESS, status); + + value = (int*)celix_tss_get(key); + EXPECT_EQ(123, *value); + + status = celix_tss_delete(key); + EXPECT_EQ(CELIX_SUCCESS, status); +} + +static void * thread_test_func_create(void * arg) { + char ** test_str = (char**) arg; + *test_str = strdup("SUCCESS"); + + return nullptr; +} + +static void * thread_test_func_exit(void *) { + int *pi = (int*) calloc(1, sizeof(int)); + *pi = 666; + + celixThread_exit(pi); + return nullptr; +} + +static void * thread_test_func_detach(void *) { + return nullptr; +} + +static void * thread_test_func_self(void * arg) { + *((celix_thread*) arg) = celixThread_self(); + return nullptr; +} + +static void * thread_test_func_once(void * arg) { + struct func_param *param = (struct func_param *) arg; + celixThread_once(¶m->once_control, thread_test_func_once_init); + return nullptr; +} + +static void thread_test_func_once_init() { + thread_test_func_once_init_times_called += 1; +} + +static void * thread_test_func_lock(void *arg) { + struct func_param *param = (struct func_param *) arg; + + celixThreadMutex_lock(¶m->mu2); + celixThreadMutex_lock(¶m->mu); + param->i = 666; + celixThreadMutex_unlock(¶m->mu); + celixThreadMutex_unlock(¶m->mu2); + + return nullptr; +} + +static void * thread_test_func_cond_wait(void *arg) { + struct func_param *param = (struct func_param *) arg; + + celixThreadMutex_lock(¶m->mu); + + param->i = 666; + + celixThreadCondition_signal(¶m->cond); + celixThreadMutex_unlock(¶m->mu); + return nullptr; +} + +static void * thread_test_func_cond_broadcast(void *arg) { + struct func_param *param = (struct func_param *) arg; + + celixThreadMutex_lock(¶m->mu); + celixThreadCondition_wait(¶m->cond, ¶m->mu); + celixThreadMutex_unlock(¶m->mu); + celixThreadMutex_lock(¶m->mu); + param->i++; + celixThreadMutex_unlock(¶m->mu); + return nullptr; +} + +static int thread_test_func_recur_lock(celix_thread_mutex_t *mu, int i) { + int temp; + if (i == 1) { + return 1; + } else { + celixThreadMutex_lock(mu); + temp = thread_test_func_recur_lock(mu, i - 1); + temp *= i; + celixThreadMutex_unlock(mu); + return temp; + } +} + +static void * thread_test_func_kill(void __attribute__((unused)) *arg){ + int * ret = (int*) malloc(sizeof(*ret)); + //sleep for about a minute, or until a kill signal (USR1) is received + *ret = usleep(60000000); + return ret; +} diff --git a/libs/utils/private/test/celix_threads_test.cpp b/libs/utils/private/test/celix_threads_test.cpp deleted file mode 100644 index b6c6cb64..00000000 --- a/libs/utils/private/test/celix_threads_test.cpp +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/** - * array_list_test.cpp - * - * \date Sep 15, 2015 - * \author <a href="mailto:d...@celix.apache.org">Apache Celix Project Team</a> - * \copyright Apache License, Version 2.0 - */ - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <signal.h> - -#include "CppUTest/TestHarness.h" -#include "CppUTest/TestHarness_c.h" -#include "CppUTest/CommandLineTestRunner.h" -#include "CppUTestExt/MockSupport.h" - -extern "C" { -#include "celix_threads.h" -#include "CppUTestExt/MockSupport_c.h" - - -static char* my_strdup(const char* s) { - if (s == NULL) { - return NULL; - } - - size_t len = strlen(s); - - char *d = (char*) calloc(len + 1, sizeof(char)); - - if (d == NULL) { - return NULL; - } - - strncpy(d, s, len); - return d; -} - -static int celix_thread_t_equals(const void * object, const void * compareTo){ - celix_thread_t * thread1 = (celix_thread_t*) object; - celix_thread_t * thread2 = (celix_thread_t*) compareTo; - - return __atomic_load_n(&thread1->thread, __ATOMIC_ACQUIRE) == __atomic_load_n(&thread2->thread, __ATOMIC_ACQUIRE) && - __atomic_load_n(&thread1->threadInitialized, __ATOMIC_ACQUIRE) == __atomic_load_n(&thread2->threadInitialized, __ATOMIC_ACQUIRE); -} - -static const char * celix_thread_t_toString(const void * object){ - celix_thread_t * thread = (celix_thread_t*) object; - char buff[512]; - snprintf(buff, 512, "thread: %lu, threadInitialized: %s", (unsigned long)thread->thread, (thread->threadInitialized ? "true" : "false")); - - return my_strdup(buff); -} - -//----------------------TEST THREAD FUNCTION DECLARATIONS---------------------- -static void * thread_test_func_create(void *); -static void * thread_test_func_exit(void *); -static void * thread_test_func_detach(void *); -static void * thread_test_func_self(void *); -static void * thread_test_func_once(void*); -static void thread_test_func_once_init(); -static void * thread_test_func_lock(void *); -static void * thread_test_func_cond_wait(void *); -static void * thread_test_func_cond_broadcast(void *); -static int thread_test_func_recur_lock(celix_thread_mutex_t*, int); -static void * thread_test_func_kill(void*); -static void thread_test_func_kill_handler(int); -struct func_param{ - int i, i2; - celix_thread_mutex_t mu, mu2; - celix_thread_cond_t cond, cond2; - celix_thread_once_t once_control; - celix_thread_rwlock_t rwlock; -}; -} - -int main(int argc, char** argv) { - MemoryLeakWarningPlugin::turnOffNewDeleteOverloads(); - return RUN_ALL_TESTS(argc, argv); -} - -//----------------------TESTGROUP DEFINES---------------------- - -TEST_GROUP(celix_thread) { - celix_thread thread; - - void setup() override { - } - - void teardown() override { - } -}; - -TEST_GROUP(celix_thread_kill) { - celix_thread thread; - struct sigaction sigact, sigactold; - - void setup() override { - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = thread_test_func_kill_handler; - sigaction(SIGUSR1, &sigact, &sigactold); - - mock_c()->installComparator("celix_thread_t", celix_thread_t_equals, celix_thread_t_toString); - } - - void teardown() override { - sigaction(SIGUSR1, &sigactold, &sigact); - - mock_c()->removeAllComparatorsAndCopiers(); - } -}; - -TEST_GROUP(celix_thread_mutex) { - celix_thread thread; - celix_thread_mutex_t mu; - - void setup() override { - } - - void teardown() override { - } -}; - -TEST_GROUP(celix_thread_condition) { - celix_thread thread; - celix_thread_mutex_t mu; - celix_thread_cond_t cond; - - void setup() { - } - - void teardown() { - } -}; - -TEST_GROUP(celix_thread_rwlock) { - celix_thread thread; - - void setup() { - } - - void teardown() { - } -}; - -//----------------------CELIX THREADS TESTS---------------------- - -TEST(celix_thread, create) { - int ret; - char * test_str; - - ret = celixThread_create(&thread, NULL, &thread_test_func_create, - &test_str); - LONGS_EQUAL(CELIX_SUCCESS, ret); - celixThread_join(thread, NULL); - - CHECK(test_str != NULL); - STRCMP_EQUAL("SUCCESS", test_str); - - free(test_str); -} - -TEST(celix_thread, exit) { - int ret, *status; - - ret = celixThread_create(&thread, NULL, &thread_test_func_exit, NULL); - LONGS_EQUAL(CELIX_SUCCESS, ret); - celixThread_join(thread, (void**) &status); - LONGS_EQUAL(666, *status); - free(status); -} - -//HORRIBLE TEST -TEST(celix_thread, detach) { - int ret; - - celixThread_create(&thread, NULL, thread_test_func_detach, NULL); - ret = celixThread_detach(thread); - LONGS_EQUAL(CELIX_SUCCESS, ret); -} - -TEST(celix_thread, self) { - celix_thread thread2; - - celixThread_create(&thread, NULL, thread_test_func_self, &thread2); - celixThread_join(thread, NULL); - CHECK(celixThread_equals(thread, thread2)); -} - -TEST(celix_thread, initialized) { - CHECK(!celixThread_initialized(thread)); - celixThread_create(&thread, NULL, thread_test_func_detach, NULL); - CHECK(celixThread_initialized(thread)); - celixThread_detach(thread); -} - -TEST(celix_thread, once) { - int *status; - celix_thread thread2; - struct func_param * params = (struct func_param*) calloc(1, - sizeof(struct func_param)); - - mock().expectOneCall("thread_test_func_once_init"); - - celixThread_create(&thread, NULL, &thread_test_func_once, params); - celixThread_join(thread, (void**) &status); - - celixThread_create(&thread2, NULL, &thread_test_func_once, params); - celixThread_join(thread2, (void**) &status); - - free(params); - - mock().checkExpectations(); - mock().clear(); -} -//----------------------CELIX THREADS KILL TESTS---------------------- -TEST(celix_thread_kill, kill){ - int * ret; - celixThread_create(&thread, NULL, thread_test_func_kill, NULL); - sleep(2); - - mock().expectOneCall("thread_test_func_kill_handler") - .withParameter("signo", SIGUSR1) - .withParameterOfType("celix_thread_t", "inThread", (const void*) &thread); - - celixThread_kill(thread, SIGUSR1); - celixThread_join(thread, (void**)&ret); - - LONGS_EQUAL(-1, *ret); - free(ret); - - mock().checkExpectations(); - mock().clear(); -} - -//----------------------CELIX THREADS MUTEX TESTS---------------------- - -TEST(celix_thread_mutex, create) { - LONGS_EQUAL(CELIX_SUCCESS, celixThreadMutex_create(&mu, NULL)); - LONGS_EQUAL(CELIX_SUCCESS, celixThreadMutex_destroy(&mu)); -} - -//check normal lock behaviour -TEST(celix_thread_mutex, lock) { - struct func_param * params = (struct func_param*) calloc(1, - sizeof(struct func_param)); - - celixThreadMutex_create(¶ms->mu, NULL); - - celixThreadMutex_lock(¶ms->mu); - celixThread_create(&thread, NULL, thread_test_func_lock, params); - - sleep(2); - - LONGS_EQUAL(0, params->i); - - //possible race condition, not perfect test - celixThreadMutex_unlock(¶ms->mu); - - sleep(2); - - celixThreadMutex_lock(¶ms->mu2); - LONGS_EQUAL(666, params->i); - celixThreadMutex_unlock(¶ms->mu2); - celixThread_join(thread, NULL); - free(params); -} - -TEST(celix_thread_mutex, attrCreate) { - celix_thread_mutexattr_t mu_attr; - LONGS_EQUAL(CELIX_SUCCESS, celixThreadMutexAttr_create(&mu_attr)); - LONGS_EQUAL(CELIX_SUCCESS, celixThreadMutexAttr_destroy(&mu_attr)); -} - -TEST(celix_thread_mutex, attrSettype) { - celix_thread_mutex_t mu; - celix_thread_mutexattr_t mu_attr; - celixThreadMutexAttr_create(&mu_attr); - - //test recursive mutex - celixThreadMutexAttr_settype(&mu_attr, CELIX_THREAD_MUTEX_RECURSIVE); - celixThreadMutex_create(&mu, &mu_attr); - //if program doesnt deadlock: success! also check factorial of 10, for reasons unknown - LONGS_EQUAL(3628800, thread_test_func_recur_lock(&mu, 10)); - celixThreadMutex_destroy(&mu); - - //test deadlock check mutex - celixThreadMutexAttr_settype(&mu_attr, CELIX_THREAD_MUTEX_ERRORCHECK); - celixThreadMutex_create(&mu, &mu_attr); - //get deadlock error - celixThreadMutex_lock(&mu); - CHECK(celixThreadMutex_lock(&mu) != CELIX_SUCCESS); - //do not get deadlock error - celixThreadMutex_unlock(&mu); - LONGS_EQUAL(CELIX_SUCCESS, celixThreadMutex_lock(&mu)); - //get unlock error - celixThreadMutex_unlock(&mu); - CHECK(celixThreadMutex_unlock(&mu) != CELIX_SUCCESS); - - celixThreadMutex_destroy(&mu); - celixThreadMutexAttr_destroy(&mu_attr); -} - -//----------------------CELIX THREAD CONDITIONS TESTS---------------------- - -TEST(celix_thread_condition, init) { - LONGS_EQUAL(CELIX_SUCCESS, celixThreadCondition_init(&cond, NULL)); - LONGS_EQUAL(CELIX_SUCCESS, celixThreadCondition_destroy(&cond)); -} - -//test wait and signal -TEST(celix_thread_condition, wait) { - struct func_param * param = (struct func_param*) calloc(1, - sizeof(struct func_param)); - celixThreadMutex_create(¶m->mu, NULL); - celixThreadCondition_init(¶m->cond, NULL); - - celixThreadMutex_lock(¶m->mu); - - sleep(2); - - celixThread_create(&thread, NULL, thread_test_func_cond_wait, param); - LONGS_EQUAL(0, param->i); - - celixThreadCondition_wait(¶m->cond, ¶m->mu); - LONGS_EQUAL(666, param->i); - celixThreadMutex_unlock(¶m->mu); - - celixThread_join(thread, NULL); - free(param); -} - -//test wait and broadcast on multiple threads -TEST(celix_thread_condition, broadcast) { - celix_thread_t thread2; - struct func_param * param = (struct func_param*) calloc(1,sizeof(struct func_param)); - celixThreadMutex_create(¶m->mu, NULL); - celixThreadMutex_create(¶m->mu2, NULL); - celixThreadCondition_init(¶m->cond, NULL); - - celixThread_create(&thread, NULL, thread_test_func_cond_broadcast, param); - celixThread_create(&thread2, NULL, thread_test_func_cond_broadcast, param); - - sleep(1); - celixThreadMutex_lock(¶m->mu); - LONGS_EQUAL(0, param->i); - celixThreadMutex_unlock(¶m->mu); - - celixThreadMutex_lock(¶m->mu); - celixThreadCondition_broadcast(¶m->cond); - celixThreadMutex_unlock(¶m->mu); - sleep(1); - celixThreadMutex_lock(¶m->mu); - LONGS_EQUAL(2, param->i); - celixThreadMutex_unlock(¶m->mu); - - celixThread_join(thread, NULL); - celixThread_join(thread2, NULL); - free(param); -} - -//----------------------CELIX READ-WRITE LOCK TESTS---------------------- - -TEST(celix_thread_rwlock, create){ - celix_thread_rwlock_t alock; - celix_status_t status; - status = celixThreadRwlock_create(&alock, NULL); - if (status != CELIX_SUCCESS) { - fprintf(stderr, "Found error '%s'\n", strerror(status)); - } - LONGS_EQUAL(CELIX_SUCCESS, status); - status = celixThreadRwlock_destroy(&alock); - LONGS_EQUAL(CELIX_SUCCESS, status); -} - -TEST(celix_thread_rwlock, readLock){ - int status; - celix_thread_rwlock_t lock; - memset(&lock, 0x00, sizeof(celix_thread_rwlock_t)); - //struct func_param * param = (struct func_param*) calloc(1,sizeof(struct func_param)); - - celixThreadRwlock_create(&lock, NULL); - - status = celixThreadRwlock_readLock(&lock); - LONGS_EQUAL(0, status); - status = celixThreadRwlock_readLock(&lock); - LONGS_EQUAL(0, status); - status = celixThreadRwlock_unlock(&lock); - LONGS_EQUAL(0, status); - status = celixThreadRwlock_unlock(&lock); - LONGS_EQUAL(0, status); - - celixThreadRwlock_destroy(&lock); -} - -TEST(celix_thread_rwlock, writeLock){ - int status; - celix_thread_rwlock_t lock; - celixThreadRwlock_create(&lock, NULL); - - status = celixThreadRwlock_writeLock(&lock); - LONGS_EQUAL(0, status); - status = celixThreadRwlock_writeLock(&lock); - //EDEADLK ErNo: Resource deadlock avoided - LONGS_EQUAL(EDEADLK, status); - - celixThreadRwlock_unlock(&lock); -} - -TEST(celix_thread_rwlock, attr){ - celix_thread_rwlockattr_t attr; - celixThreadRwlockAttr_create(&attr); - celixThreadRwlockAttr_destroy(&attr); -} - -//----------------------TEST THREAD FUNCTION DEFINES---------------------- -extern "C" { -static void * thread_test_func_create(void * arg) { - char ** test_str = (char**) arg; - *test_str = my_strdup("SUCCESS"); - - return NULL; -} - -static void * thread_test_func_exit(void *) { - int *pi = (int*) calloc(1, sizeof(int)); - *pi = 666; - - celixThread_exit(pi); - return NULL; -} - -static void * thread_test_func_detach(void *) { - return NULL; -} - -static void * thread_test_func_self(void * arg) { - *((celix_thread*) arg) = celixThread_self(); - return NULL; -} - -static void * thread_test_func_once(void * arg) { - struct func_param *param = (struct func_param *) arg; - celixThread_once(¶m->once_control, thread_test_func_once_init); - - return NULL; -} - -static void thread_test_func_once_init() { - mock_c()->actualCall("thread_test_func_once_init"); -} - -static void * thread_test_func_lock(void *arg) { - struct func_param *param = (struct func_param *) arg; - - celixThreadMutex_lock(¶m->mu2); - celixThreadMutex_lock(¶m->mu); - param->i = 666; - celixThreadMutex_unlock(¶m->mu); - celixThreadMutex_unlock(¶m->mu2); - - return NULL; -} - -static void * thread_test_func_cond_wait(void *arg) { - struct func_param *param = (struct func_param *) arg; - - celixThreadMutex_lock(¶m->mu); - - param->i = 666; - - celixThreadCondition_signal(¶m->cond); - celixThreadMutex_unlock(¶m->mu); - return NULL; -} - -static void * thread_test_func_cond_broadcast(void *arg) { - struct func_param *param = (struct func_param *) arg; - - celixThreadMutex_lock(¶m->mu); - celixThreadCondition_wait(¶m->cond, ¶m->mu); - celixThreadMutex_unlock(¶m->mu); - celixThreadMutex_lock(¶m->mu); - param->i++; - celixThreadMutex_unlock(¶m->mu); - return NULL; -} - -static int thread_test_func_recur_lock(celix_thread_mutex_t *mu, int i) { - int temp; - if (i == 1) { - return 1; - } else { - celixThreadMutex_lock(mu); - temp = thread_test_func_recur_lock(mu, i - 1); - temp *= i; - celixThreadMutex_unlock(mu); - return temp; - } -} - -static void * thread_test_func_kill(void __attribute__((unused)) *arg){ - int * ret = (int*) malloc(sizeof(*ret)); - //sleep for a about a minute, or until a kill signal (USR1) is received - *ret = usleep(60000000); - return ret; -} - -static void thread_test_func_kill_handler(int signo){ - celix_thread_t inThread = celixThread_self(); - - mock_c()->actualCall("thread_test_func_kill_handler") - ->withLongIntParameters("signo", signo) - ->withParameterOfType("celix_thread_t", "inThread", (const void*) &inThread); -} -}