tests: kernel: pipe: Add new test cases for pipe API rework

This commit adds new test cases for the pipe API rework.
* basic.c: Sanity check for pipe operations.
* concurrency.c: Test pipe operations with multiple threads.
* stress.c: Test pipe operations under stress conditions.

And moves the old pipe test cases to the deprecated folder.

Signed-off-by: Måns Ansgariusson <Mansgariusson@gmail.com>
This commit is contained in:
Måns Ansgariusson 2024-12-23 00:18:25 +01:00 committed by Benjamin Cabé
parent 84584c579e
commit 6782c5381d
19 changed files with 454 additions and 17 deletions

View file

@ -10,6 +10,8 @@
#include <kthread.h>
#include <wait_q.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(os_pipe, LOG_LEVEL_DBG);
#ifdef CONFIG_OBJ_CORE_PIPE
static struct k_obj_type obj_type_pipe;
#endif /* CONFIG_OBJ_CORE_PIPE */

View file

@ -0,0 +1,7 @@
tests:
kernel.deprecated.pipe:
tags:
- kernel
- userspace
ignore_faults: true
extra_args: CMAKE_C_FLAGS="-D__deprecated='' -D__DEPRECATED_MACRO=''"

View file

@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(pipe_api)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

View file

@ -0,0 +1,7 @@
CONFIG_ZTEST=y
CONFIG_IRQ_OFFLOAD=y
CONFIG_TEST_USERSPACE=y
CONFIG_DYNAMIC_OBJECTS=y
CONFIG_MP_MAX_NUM_CPUS=1
CONFIG_ZTEST_FATAL_HOOK=y
CONFIG_PIPES=y

View file

@ -0,0 +1,6 @@
tests:
kernel.deprecated.pipe.api:
tags:
- kernel
- userspace
extra_args: CMAKE_C_FLAGS="-D__deprecated='' -D__DEPRECATED_MACRO=''"

View file

@ -1,6 +0,0 @@
tests:
kernel.pipe:
tags:
- kernel
- userspace
ignore_faults: true

View file

@ -1,8 +1,12 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(pipe_api)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
project(app)
target_sources(app
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/basic.c
${CMAKE_CURRENT_SOURCE_DIR}/src/stress.c
${CMAKE_CURRENT_SOURCE_DIR}/src/concurrency.c
)

View file

@ -1,7 +1,9 @@
CONFIG_ZTEST=y
CONFIG_IRQ_OFFLOAD=y
CONFIG_TEST_USERSPACE=y
CONFIG_DYNAMIC_OBJECTS=y
CONFIG_MP_MAX_NUM_CPUS=1
CONFIG_ZTEST_FATAL_HOOK=y
CONFIG_PIPES=y
CONFIG_ZTRESS=y
CONFIG_ZTEST_STACK_SIZE=4096
CONFIG_HEAP_MEM_POOL_SIZE=2048
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_ENTROPY_GENERATOR=y
CONFIG_TEST_RANDOM_GENERATOR=y

View file

@ -0,0 +1,169 @@
/*
* Copyright (c) 2024 Måns Ansgariusson <mansgariusson@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
#include <zephyr/random/random.h>
ZTEST_SUITE(k_pipe_basic, NULL, NULL, NULL, NULL, NULL);
static void mkrandom(uint8_t *buffer, size_t size)
{
sys_rand_get(buffer, size);
}
K_PIPE_DEFINE(test_define, 256, 4);
ZTEST(k_pipe_basic, test_init)
{
struct k_pipe pipe;
uint8_t buffer[10];
k_pipe_init(&pipe, buffer, sizeof(buffer));
zassert_true(pipe.flags == PIPE_FLAG_OPEN, "Unexpected pipe flags");
}
ZTEST(k_pipe_basic, test_write_read_one)
{
struct k_pipe pipe;
uint8_t buffer[10];
uint8_t data = 0x55;
uint8_t read_data;
k_pipe_init(&pipe, buffer, sizeof(buffer));
zassert_true(k_pipe_write(&pipe, &data, 1, K_NO_WAIT) == 1,
"Failed to write to pipe");
zassert_true(k_pipe_read(&pipe, &read_data, 1, K_NO_WAIT) == 1,
"Failed to read from pipe");
zassert_true(read_data == data, "Unexpected data received from pipe");
}
ZTEST(k_pipe_basic, test_write_read_multiple)
{
struct k_pipe pipe;
uint8_t buffer[10];
uint8_t data = 0x55;
uint8_t read_data;
k_pipe_init(&pipe, buffer, sizeof(buffer));
zassert_true(k_pipe_write(&pipe, &data, 1, K_NO_WAIT) == 1, "Failed to write to pipe");
zassert_true(k_pipe_write(&pipe, &data, 1, K_NO_WAIT) == 1, "Failed to write to pipe");
zassert_true(k_pipe_read(&pipe, &read_data, 1, K_NO_WAIT) == 1, "Failed to read from pipe");
zassert_true(read_data == data, "Unexpected data received from pipe");
zassert_true(k_pipe_read(&pipe, &read_data, 1, K_NO_WAIT) == 1, "Failed to read from pipe");
zassert_true(read_data == data, "Unexpected data received from pipe");
}
ZTEST(k_pipe_basic, test_write_full)
{
struct k_pipe pipe;
uint8_t buffer[10];
uint8_t data[10];
k_pipe_init(&pipe, buffer, sizeof(buffer));
zassert_true(k_pipe_write(&pipe, data, sizeof(data), K_NO_WAIT) == 10,
"Failed to write multiple bytes to pipe");
zassert_true(k_pipe_write(&pipe, data, sizeof(data), K_MSEC(1000)) == -EAGAIN,
"Should not be able to write to full pipe");
}
ZTEST(k_pipe_basic, test_read_empty)
{
struct k_pipe pipe;
uint8_t buffer[10];
uint8_t read_data;
k_pipe_init(&pipe, buffer, sizeof(buffer));
zassert_true(k_pipe_read(&pipe, &read_data, 1, K_MSEC(1000)) == -EAGAIN,
"Should not be able to read from empty pipe");
}
ZTEST(k_pipe_basic, test_read_write_full)
{
struct k_pipe pipe;
uint8_t buffer[10];
uint8_t input[10];
uint8_t res[10];
mkrandom(input, sizeof(input));
k_pipe_init(&pipe, buffer, sizeof(buffer));
zassert_true(k_pipe_write(&pipe, input, sizeof(input), K_NO_WAIT) == sizeof(input),
"Failed to write multiple bytes to pipe");
zassert_true(k_pipe_read(&pipe, res, sizeof(res), K_NO_WAIT) == sizeof(res),
"Failed to read multiple bytes from pipe");
zassert_true(memcmp(input, res, sizeof(input)) == 0,
"Unexpected data received from pipe");
}
ZTEST(k_pipe_basic, test_read_write_wrapp_around)
{
struct k_pipe pipe;
uint8_t buffer[12];
uint8_t input[8];
uint8_t res[16];
mkrandom(input, sizeof(input));
k_pipe_init(&pipe, buffer, sizeof(buffer));
zassert_true(k_pipe_write(&pipe, input, sizeof(input), K_NO_WAIT) == sizeof(input),
"Failed to write bytes to pipe");
zassert_true(k_pipe_read(&pipe, res, 5, K_NO_WAIT) == 5,
"Failed to read bytes from pipe");
zassert_true(memcmp(input, res, 5) == 0, "Unexpected data received from pipe");
zassert_true(k_pipe_write(&pipe, input, sizeof(input), K_NO_WAIT) == sizeof(input),
"Failed to write bytes to pipe");
zassert_true(k_pipe_read(&pipe, res, sizeof(input) * 2 - 5, K_NO_WAIT) ==
sizeof(input) * 2 - 5, "Failed to read remaining bytes from pipe");
zassert_true(memcmp(&input[5], res, sizeof(input) - 5) == 0,
"Unexpected data received from pipe");
zassert_true(memcmp(input, &res[sizeof(input) - 5], sizeof(input)) == 0,
"Unexpected data received from pipe");
}
ZTEST(k_pipe_basic, test_reset)
{
struct k_pipe pipe;
uint8_t buffer[10];
uint8_t data = 0x55;
uint8_t read_data;
k_pipe_init(&pipe, buffer, sizeof(buffer));
/* reset an empty pipe, & no waiting should not produce any side-effects*/
k_pipe_reset(&pipe);
zassert_true(k_pipe_write(&pipe, &data, 1, K_NO_WAIT) == 1,
"Failed to write to resetted pipe");
zassert_true(k_pipe_read(&pipe, &read_data, 1, K_NO_WAIT) == 1,
"Failed to read from resetted pipe");
zassert_true(read_data == data, "Unexpected data received from pipe");
}
ZTEST(k_pipe_basic, test_close)
{
struct k_pipe pipe;
uint8_t buffer[12];
uint8_t input[8];
uint8_t res[16];
mkrandom(input, sizeof(input));
k_pipe_init(&pipe, buffer, sizeof(buffer));
zassert_true(k_pipe_write(&pipe, input, sizeof(input), K_NO_WAIT) == sizeof(input),
"Failed to write bytes to pipe");
k_pipe_close(&pipe);
zassert_true(k_pipe_write(&pipe, input, sizeof(input), K_NO_WAIT) == -EPIPE,
"should not be able to write to closed pipe");
zassert_true(k_pipe_read(&pipe, res, 5, K_NO_WAIT) == 5,
"You should be able to read from closed pipe");
zassert_true(memcmp(input, res, 5) == 0, "Sequence should be equal");
zassert_true(k_pipe_read(&pipe, res, 5, K_NO_WAIT) == 3,
"you should be able to read remaining bytes from closed pipe");
zassert_true(memcmp(&input[5], res, 3) == 0, "Written and read bytes should be equal");
zassert_true(k_pipe_read(&pipe, res, 5, K_NO_WAIT) == -EPIPE,
"Closed and empty pipe should return -EPIPE");
}

View file

@ -0,0 +1,170 @@
/*
* Copyright (c) 2024 Måns Ansgariusson <mansgariusson@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <zephyr/kernel.h>
#include <zephyr/ztest.h>
#include <zephyr/random/random.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(k_pipe_concurrency, LOG_LEVEL_DBG);
ZTEST_SUITE(k_pipe_concurrency, NULL, NULL, NULL, NULL, NULL);
static const int partial_wait_time = 2000;
static const int dummy_data_size = 16;
static struct k_thread thread;
static K_THREAD_STACK_DEFINE(stack, 1024);
static void thread_close(void *arg1, void *arg2, void *arg3)
{
k_pipe_close((struct k_pipe *)arg1);
}
static void thread_reset(void *arg1, void *arg2, void *arg3)
{
k_pipe_reset((struct k_pipe *)arg1);
}
static void thread_write(void *arg1, void *arg2, void *arg3)
{
uint8_t garbage[dummy_data_size];
zassert_true(k_pipe_write((struct k_pipe *)arg1, garbage, sizeof(garbage),
K_MSEC(partial_wait_time)) == sizeof(garbage), "Failed to write to pipe");
}
static void thread_read(void *arg1, void *arg2, void *arg3)
{
uint8_t garbage[dummy_data_size];
zassert_true(k_pipe_read((struct k_pipe *)arg1, garbage, sizeof(garbage),
K_MSEC(partial_wait_time)) == sizeof(garbage), "Failed to read from pipe");
}
ZTEST(k_pipe_concurrency, test_close_on_read)
{
k_tid_t tid;
struct k_pipe pipe;
uint8_t buffer[dummy_data_size];
uint8_t res;
k_pipe_init(&pipe, buffer, sizeof(buffer));
tid = k_thread_create(&thread, stack, K_THREAD_STACK_SIZEOF(stack),
thread_close, &pipe, NULL, NULL, K_PRIO_COOP(0), 0, K_MSEC(100));
zassert_true(tid, "k_thread_create failed");
zassert_true(k_pipe_read(&pipe, &res, sizeof(res), K_MSEC(1000)) == -EPIPE,
"Read on closed pipe should return -EPIPE");
k_thread_join(tid, K_FOREVER);
zassert_true((pipe.flags & PIPE_FLAG_OPEN) == 0,
"Pipe should continue to be closed after all waiters have been released");
}
ZTEST(k_pipe_concurrency, test_close_on_write)
{
k_tid_t tid;
struct k_pipe pipe;
uint8_t buffer[dummy_data_size];
uint8_t garbage[dummy_data_size];
k_pipe_init(&pipe, buffer, sizeof(buffer));
zassert_true(sizeof(garbage) == k_pipe_write(&pipe, garbage, sizeof(garbage), K_MSEC(1000)),
"Failed to write to pipe");
tid = k_thread_create(&thread, stack, K_THREAD_STACK_SIZEOF(stack),
thread_close, &pipe, NULL, NULL, K_PRIO_COOP(0), 0, K_MSEC(100));
zassert_true(tid, "k_thread_create failed");
zassert_true(k_pipe_write(&pipe, garbage, sizeof(garbage), K_MSEC(1000)) == -EPIPE,
"write should return -EPIPE, when pipe is closed");
k_thread_join(tid, K_FOREVER);
zassert_true((pipe.flags & PIPE_FLAG_OPEN) == 0,
"pipe should continue to be closed after all waiters have been released");
}
ZTEST(k_pipe_concurrency, test_reset_on_read)
{
k_tid_t tid;
struct k_pipe pipe;
uint8_t buffer[dummy_data_size];
uint8_t res;
k_pipe_init(&pipe, buffer, sizeof(buffer));
tid = k_thread_create(&thread, stack, K_THREAD_STACK_SIZEOF(stack),
thread_reset, &pipe, NULL, NULL, K_PRIO_COOP(0), 0, K_MSEC(100));
zassert_true(tid, "k_thread_create failed");
zassert_true(k_pipe_read(&pipe, &res, sizeof(res), K_MSEC(1000)) == -ECANCELED,
"reset on read should return -ECANCELED");
k_thread_join(tid, K_FOREVER);
zassert_true((pipe.flags & PIPE_FLAG_RESET) == 0,
"pipe should not have reset flag after all waiters are done");
zassert_true((pipe.flags & PIPE_FLAG_OPEN) != 0,
"pipe should continue to be open after pipe is reseted");
}
ZTEST(k_pipe_concurrency, test_reset_on_write)
{
k_tid_t tid;
struct k_pipe pipe;
uint8_t buffer[dummy_data_size];
uint8_t garbage[dummy_data_size];
k_pipe_init(&pipe, buffer, sizeof(buffer));
zassert_true(sizeof(garbage) == k_pipe_write(&pipe, garbage, sizeof(garbage), K_MSEC(1000)),
"Failed to write to pipe");
tid = k_thread_create(&thread, stack, K_THREAD_STACK_SIZEOF(stack),
thread_reset, &pipe, NULL, NULL, K_PRIO_COOP(0), 0, K_MSEC(100));
zassert_true(tid, "k_thread_create failed");
zassert_true(k_pipe_write(&pipe, garbage, sizeof(garbage), K_MSEC(1000)) == -ECANCELED,
"reset on write should return -ECANCELED");
k_thread_join(tid, K_FOREVER);
zassert_true((pipe.flags & PIPE_FLAG_RESET) == 0,
"pipe should not have reset flag after all waiters are done");
zassert_true((pipe.flags & PIPE_FLAG_OPEN) != 0,
"pipe should continue to be open after pipe is reseted");
}
ZTEST(k_pipe_concurrency, test_partial_read)
{
k_tid_t tid;
struct k_pipe pipe;
uint8_t buffer[dummy_data_size];
uint8_t garbage[dummy_data_size];
size_t write_size = sizeof(garbage)/2;
k_pipe_init(&pipe, buffer, sizeof(buffer));
tid = k_thread_create(&thread, stack, K_THREAD_STACK_SIZEOF(stack),
thread_read, &pipe, NULL, NULL, K_PRIO_COOP(0), 0, K_NO_WAIT);
zassert_true(k_pipe_write(&pipe, garbage, write_size, K_NO_WAIT) == write_size,
"write to pipe failed");
k_msleep(partial_wait_time/4);
zassert_true(k_pipe_write(&pipe, garbage, write_size, K_NO_WAIT) == write_size,
"k_k_pipe_write should return number of bytes written");
k_thread_join(tid, K_FOREVER);
}
ZTEST(k_pipe_concurrency, test_partial_write)
{
k_tid_t tid;
struct k_pipe pipe;
uint8_t buffer[dummy_data_size];
uint8_t garbage[dummy_data_size];
size_t read_size = sizeof(garbage)/2;
k_pipe_init(&pipe, buffer, sizeof(buffer));
zassert_true(k_pipe_write(&pipe, garbage, sizeof(garbage), K_NO_WAIT) == sizeof(garbage),
"Failed to write to pipe");
tid = k_thread_create(&thread, stack, K_THREAD_STACK_SIZEOF(stack),
thread_write, &pipe, NULL, NULL, K_PRIO_COOP(0), 0, K_NO_WAIT);
zassert_true(k_pipe_read(&pipe, garbage, read_size, K_NO_WAIT) == read_size,
"Failed to read from pipe");
k_msleep(partial_wait_time/2);
zassert_true(k_pipe_read(&pipe, garbage, read_size, K_NO_WAIT) == read_size,
"failed t read from pipe");
k_thread_join(tid, K_FOREVER);
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2024 Måns Ansgariusson <mansgariusson@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/ztress.h>
#include <zephyr/logging/log.h>
#include <zephyr/random/random.h>
#include <zephyr/timing/timing.h>
LOG_MODULE_REGISTER(k_k_pipe_stress, LOG_LEVEL_INF);
ZTEST_SUITE(k_pipe_stress, NULL, NULL, NULL, NULL, NULL);
ZTEST(k_pipe_stress, test_write)
{
int rc;
struct k_pipe pipe;
size_t len = 512;
uint8_t buffer[len];
uint8_t buf[len];
size_t sent;
uint32_t start_cycles, end_cycles;
k_pipe_init(&pipe, buffer, sizeof(buffer));
start_cycles = k_uptime_get_32();
sent = 0;
while (sent < len) {
rc = k_pipe_write(&pipe, &buf[sent], len - sent, K_FOREVER);
zassert_true(rc > 0, "Failed to write to pipe");
sent += rc;
}
end_cycles = k_uptime_get_32();
LOG_INF("Elapsed cycles: %u\n", end_cycles - start_cycles);
}
ZTEST(k_pipe_stress, test_read)
{
int rc;
struct k_pipe pipe;
size_t len = 512;
uint8_t buffer[len];
uint8_t buf[len];
size_t sent, read;
uint32_t start_cycles, end_cycles;
k_pipe_init(&pipe, buffer, sizeof(buffer));
start_cycles = k_uptime_get_32();
for (int i = 0; i < 100; i++) {
sent = 0;
while (sent < len) {
rc = k_pipe_write(&pipe, &buf[sent], len - sent, K_FOREVER);
zassert_true(rc > 0, "Failed to write to pipe");
sent += rc;
}
read = 0;
while (read < len) {
rc = k_pipe_read(&pipe, &buf[read], len - read, K_FOREVER);
zassert_true(rc > 0, "Failed to read from pipe");
read += rc;
}
}
end_cycles = k_uptime_get_32();
LOG_INF("Elapsed cycles: %u\n", end_cycles - start_cycles);
}