Skip to content

Commit

Permalink
tests: cpp: tests for std::thread and std::this_thread
Browse files Browse the repository at this point in the history
This commit adds tests for std::thread and std::this_thread.

Fixes zephyrproject-rtos#25569

Signed-off-by: Christopher Friedt <[email protected]>
  • Loading branch information
cfriedt committed Apr 10, 2022
1 parent f14929d commit b5f0b91
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 0 deletions.
8 changes: 8 additions & 0 deletions tests/subsys/cpp/thread/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(thread)

FILE(GLOB_RECURSE app_sources src/*.cpp)
target_sources(app PRIVATE ${app_sources})
6 changes: 6 additions & 0 deletions tests/subsys/cpp/thread/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CONFIG_NEWLIB_LIBC=y
CONFIG_CPLUSPLUS=y
CONFIG_LIB_CPLUSPLUS=y
CONFIG_STD_CPP17=y
CONFIG_ZTEST=y
CONFIG_EXCEPTIONS=y
38 changes: 38 additions & 0 deletions tests/subsys/cpp/thread/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <ztest.h>

BUILD_ASSERT(__cplusplus == 201703);

extern void test_this_thread_get_id(void);
extern void test_this_thread_yield(void);
extern void test_this_thread_sleep_until(void);
extern void test_this_thread_sleep_for(void);

extern void test_thread_get_id(void);
extern void test_thread_native_handle(void);
extern void test_thread_hardware_concurrency(void);
extern void test_thread_join(void);
extern void test_thread_detach(void);
extern void test_thread_swap(void);

void test_main(void)
{
TC_PRINT("version %u\n", (uint32_t)__cplusplus);
ztest_test_suite(std_thread_tests, ztest_unit_test(test_this_thread_get_id),
ztest_unit_test(test_this_thread_yield),
ztest_unit_test(test_this_thread_sleep_until),
ztest_unit_test(test_this_thread_sleep_for),

ztest_unit_test(test_thread_get_id),
ztest_unit_test(test_thread_native_handle),
ztest_unit_test(test_thread_hardware_concurrency),
ztest_unit_test(test_thread_join), ztest_unit_test(test_thread_detach),
ztest_unit_test(test_thread_swap));

ztest_run_test_suite(std_thread_tests);
}
40 changes: 40 additions & 0 deletions tests/subsys/cpp/thread/src/this_thread.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <chrono>
#include <system_error>
#include <thread>
#include <ztest.h>

BUILD_ASSERT(__cplusplus == 201703);

using namespace std;
using namespace std::chrono_literals;

auto now()
{
return chrono::steady_clock::now();
}

void test_this_thread_get_id(void)
{
thread::id id = this_thread::get_id();
}

void test_this_thread_yield(void)
{
this_thread::yield();
}

void test_this_thread_sleep_until(void)
{
this_thread::sleep_until(now() + 10ms);
}

void test_this_thread_sleep_for(void)
{
this_thread::sleep_for(10ms);
}
200 changes: 200 additions & 0 deletions tests/subsys/cpp/thread/src/thread.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <system_error>
#include <thread>
#include <ztest.h>

BUILD_ASSERT(__cplusplus == 201703);

using namespace std;

void test_thread_joinable(void)
{
if (!IS_ENABLED(CONFIG_ARCH_POSIX)) {
// cannot autoallocate thread stacks yet in Zephyr
ztest_test_skip();
}

// implicitly tests the move assignment operator (operator=)
thread th = thread([]() {});
zassert_true(th.joinable(), "non-default constructed thread should be joinable");

if (IS_ENABLED(__cpp_exceptions)) {
try {
th.join();
} catch (const system_error &e) {
zassert_true(false, "joinable thread should join");
}
} else {
th.join();
}

zassert_false(th.joinable(), "previously joined thread should not be joinable");

th = thread([]() {});
th.detach();
zassert_false(th.joinable(), "detached thread should not be joinable");
}

void test_thread_get_id(void)
{
thread::id tid = this_thread::get_id();
}

void test_thread_native_handle(void)
{
if (!IS_ENABLED(CONFIG_ARCH_POSIX)) {
// cannot autoallocate thread stacks yet in Zephyr
ztest_test_skip();
}

thread th([]() {});
th.native_handle();
th.join();
}

void test_thread_hardware_concurrency(void)
{
if (IS_ENABLED(CONFIG_ARCH_POSIX)) {
zassert_true(thread::hardware_concurrency() >= 1, "actual: %u, expected: >= 1",
thread::hardware_concurrency());
} else {
zassert_equal(thread::hardware_concurrency(), CONFIG_MP_NUM_CPUS,
"actual: %u, expected: %u", thread::hardware_concurrency(),
CONFIG_MP_NUM_CPUS);
}
}

void test_thread_join(void)
{
thread th;
bool caught = false;

if (IS_ENABLED(__cpp_exceptions)) {
try {
th.join();
} catch (const system_error &e) {
caught = true;
zassert_equal(e.code(), errc::no_such_process,
"expected errc::no_such_process");
}
zassert_true(caught, "join of default-constructed thread should throw");
}

if (!IS_ENABLED(CONFIG_ARCH_POSIX)) {
// cannot autoallocate thread stacks yet in Zephyr
ztest_test_skip();
}

th = thread([]() {});
if (IS_ENABLED(__cpp_exceptions)) {
caught = false;
try {
th.join();
} catch (const system_error &e) {
caught = true;
}
zassert_false(caught, "join() should not throw");
} else {
th.join();
}

if (IS_ENABLED(__cpp_exceptions)) {
caught = false;
try {
th.join();
} catch (const system_error &e) {
caught = true;
zassert_equal(e.code(), errc::invalid_argument,
"expected errc::invalid_argument");
}
zassert_true(caught, "join should throw with already-joined thread");
}
}

void test_thread_detach(void)
{
thread th;
bool caught = false;

if (IS_ENABLED(__cpp_exceptions)) {
// this is the behaviour in linux for detach() with an invalid thread object
caught = false;
try {
th.detach();
} catch (const system_error &e) {
caught = true;
zassert_equal(e.code(), errc::invalid_argument,
"expected errc::invalid_argument");
}
zassert_true(caught, "detach should throw with default-constructed thread");
}

if (!IS_ENABLED(CONFIG_ARCH_POSIX)) {
// cannot autoallocate thread stacks yet in Zephyr
ztest_test_skip();
}

th = thread([]() {});
if (IS_ENABLED(__cpp_exceptions)) {
caught = false;
try {
th.detach();
} catch (const system_error &e) {
caught = true;
}
zassert_false(caught, "detach on a valid thread should not throw");
} else {
th.detach();
}

if (IS_ENABLED(__cpp_exceptions)) {
caught = false;
try {
th.detach();
} catch (const system_error &e) {
caught = true;
}
zassert_true(caught, "detach on an already-detached thread should throw");
}
}

void test_thread_swap(void)
{
thread th1;
thread th2;

if (IS_ENABLED(__cpp_exceptions)) {
bool caught = false;
try {
th1.swap(th2);
} catch (...) {
caught = true;
}
zassert_false(caught, "thread::swap() is noexcept");
}

if (!IS_ENABLED(CONFIG_ARCH_POSIX)) {
// cannot autoallocate thread stacks yet in Zephyr
ztest_test_skip();
}

th1 = thread([]() {});
th2 = thread([]() {});

thread::id th1_id = th1.get_id();
thread::id th2_id = th2.get_id();

th1.swap(th2);

zassert_equal(th2.get_id(), th1_id, "expected ids to be swapped");

zassert_equal(th1.get_id(), th2_id, "expected ids to be swapped");

th1.join();
th2.join();
}
7 changes: 7 additions & 0 deletions tests/subsys/cpp/thread/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
tests:
cpp.thread:
tags: cpp
cpp.thread.exceptions:
tags: cpp
extra_configs:
- CONFIG_EXCEPTIONS=y

0 comments on commit b5f0b91

Please sign in to comment.