mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-09 20:41:14 +00:00
feat(rt/posix): Added FreeRTOS-Plus-POSIX message queues implementation
Note: The current mq_open() implementation is changed to match the POSIX standard.
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||
|
||||
components/rt/test_apps/posix_rt_test:
|
||||
enable:
|
||||
- if: IDF_TARGET in ["esp32", "esp32s2", "esp32c3", "esp32p4"]
|
||||
reason: covers all major arch types, xtensa vs riscv, single vs dual-core
|
||||
depends_components:
|
||||
- rt
|
||||
- freertos
|
7
components/rt/test_apps/posix_rt_test/CMakeLists.txt
Normal file
7
components/rt/test_apps/posix_rt_test/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(COMPONENTS main)
|
||||
project(posix_test)
|
2
components/rt/test_apps/posix_rt_test/README.md
Normal file
2
components/rt/test_apps/posix_rt_test/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32 | ESP32-C3 | ESP32-P4 | ESP32-S2 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "main.c" "mqueue_test.cpp"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES rt unity
|
||||
WHOLE_ARCHIVE)
|
36
components/rt/test_apps/posix_rt_test/main/main.c
Normal file
36
components/rt/test_apps/posix_rt_test/main/main.c
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "Linux_sim";
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_set_leak_level(0);
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks();
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// The following code avoids memory leaks from initializing libc structures in clock_gettime() calls.
|
||||
struct timespec tp;
|
||||
clock_gettime(CLOCK_REALTIME, &tp);
|
||||
(void) tp;
|
||||
|
||||
ESP_LOGI(TAG, "Running FreeRTOS Linux test app");
|
||||
unity_run_menu();
|
||||
}
|
288
components/rt/test_apps/posix_rt_test/main/mqueue_test.cpp
Normal file
288
components/rt/test_apps/posix_rt_test/main/mqueue_test.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <thread>
|
||||
#include "unity.h"
|
||||
#include "mqueue.h"
|
||||
|
||||
// Note: implementation does not support mode argument
|
||||
// Note: implementation does not support message priorities (msg_prio arguments)
|
||||
|
||||
struct MessageQueueFixture {
|
||||
MessageQueueFixture(const char *name = "/test_queue",
|
||||
long int maxmsg = 10,
|
||||
long int msgsize = sizeof(int),
|
||||
int oflag = O_CREAT | O_WRONLY)
|
||||
: name(name) {
|
||||
struct mq_attr conf = {
|
||||
.mq_flags = 0, // ignored by mq_open
|
||||
.mq_maxmsg = maxmsg,
|
||||
.mq_msgsize = msgsize,
|
||||
.mq_curmsgs = 0 // ignored by mq_open
|
||||
};
|
||||
mq = mq_open(name, oflag, S_IRUSR, &conf);
|
||||
TEST_ASSERT_NOT_EQUAL((mqd_t) -1, mq);
|
||||
}
|
||||
~MessageQueueFixture() {
|
||||
TEST_ASSERT_EQUAL(0, mq_close(mq));
|
||||
TEST_ASSERT_EQUAL(0, mq_unlink(name));
|
||||
}
|
||||
const char *name;
|
||||
mqd_t mq;
|
||||
};
|
||||
|
||||
TEST_CASE("open with invalid message queue name fails", "[mqueue]")
|
||||
{
|
||||
struct mq_attr conf = {
|
||||
.mq_flags = 0, // ignored by mq_open
|
||||
.mq_maxmsg = 10,
|
||||
.mq_msgsize = sizeof(int),
|
||||
.mq_curmsgs = 0 // ignored by mq_open
|
||||
};
|
||||
|
||||
// 255 + 2 characters, excl. '\0'
|
||||
#define TOO_LONG_NAME "/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
|
||||
|
||||
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("my_queue", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open(TOO_LONG_NAME, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("name / is valid", "[mqueue]")
|
||||
{
|
||||
mqd_t mq = mq_open("/", O_CREAT | O_WRONLY, S_IRUSR, NULL);
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL((mqd_t) -1, mq);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, mq_close(mq));
|
||||
TEST_ASSERT_EQUAL(0, mq_unlink("/"));
|
||||
}
|
||||
|
||||
TEST_CASE("open without create flag fails", "[mqueue]")
|
||||
{
|
||||
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("/my_queue", O_WRONLY));
|
||||
TEST_ASSERT_EQUAL(ENOENT, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("open mq_maxmsg <= 0", "[mqueue]")
|
||||
{
|
||||
struct mq_attr conf = {
|
||||
.mq_flags = 0, // ignored by mq_open
|
||||
.mq_maxmsg = 0,
|
||||
.mq_msgsize = sizeof(int),
|
||||
.mq_curmsgs = 0 // ignored by mq_open
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("/my_queue", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
|
||||
conf.mq_maxmsg = -1;
|
||||
|
||||
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("/my_queue", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("open mq_msgsize <= 0", "[mqueue]")
|
||||
{
|
||||
struct mq_attr conf = {
|
||||
.mq_flags = 0, // ignored by mq_open
|
||||
.mq_maxmsg = 10,
|
||||
.mq_msgsize = 0,
|
||||
.mq_curmsgs = 0 // ignored by mq_open
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("/my_queue", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
|
||||
conf.mq_msgsize = -1;
|
||||
|
||||
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open("/my_queue", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR , &conf));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("creating already existing message queue fails", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix;
|
||||
|
||||
TEST_ASSERT_EQUAL((mqd_t) -1, mq_open(mq_fix.name, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR , NULL));
|
||||
TEST_ASSERT_EQUAL(EEXIST, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("open with default flags", "[mqueue]")
|
||||
{
|
||||
mqd_t mq = mq_open("/my_queue", O_CREAT | O_WRONLY, S_IRUSR, NULL);
|
||||
TEST_ASSERT_NOT_EQUAL((mqd_t) -1, mq);
|
||||
|
||||
struct mq_attr default_conf;
|
||||
TEST_ASSERT_EQUAL(0, mq_getattr(mq, &default_conf));
|
||||
TEST_ASSERT_EQUAL(10, default_conf.mq_maxmsg);
|
||||
TEST_ASSERT_EQUAL(128, default_conf.mq_msgsize);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, mq_close(mq));
|
||||
TEST_ASSERT_EQUAL(0, mq_unlink("/my_queue"));
|
||||
}
|
||||
|
||||
TEST_CASE("open propagates attributes correctly", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix("/test", 4, 8);
|
||||
|
||||
struct mq_attr conf;
|
||||
TEST_ASSERT_EQUAL(0, mq_getattr(mq_fix.mq, &conf));
|
||||
TEST_ASSERT_EQUAL(4, conf.mq_maxmsg);
|
||||
TEST_ASSERT_EQUAL(8, conf.mq_msgsize);
|
||||
}
|
||||
|
||||
TEST_CASE("message queue does not exceed queue size", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
|
||||
int to_send = 47;
|
||||
TEST_ASSERT_EQUAL(0, mq_send(mq_fix.mq, (const char*) &to_send, sizeof(to_send), 0));
|
||||
int to_send_2 = 48;
|
||||
int received = 0;
|
||||
|
||||
const struct timespec zero_time = {0, 0};
|
||||
TEST_ASSERT_EQUAL(-1, mq_timedsend(mq_fix.mq, (const char*) &to_send_2, sizeof(to_send_2), 0, &zero_time));
|
||||
TEST_ASSERT_EQUAL(ETIMEDOUT, errno);
|
||||
|
||||
TEST_ASSERT_EQUAL(4, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
|
||||
TEST_ASSERT_EQUAL(47, received);
|
||||
}
|
||||
|
||||
TEST_CASE("sending on invalid mq descriptor fails", "[mqueue]")
|
||||
{
|
||||
int to_send = 47;
|
||||
TEST_ASSERT_EQUAL(-1, mq_send(nullptr, (char*) &to_send, sizeof(to_send), 0));
|
||||
TEST_ASSERT_EQUAL(EBADF, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("sending with invalid timeout fails", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
|
||||
int to_send = 47;
|
||||
struct timespec invalid_time = {0, 1'000'000'000};
|
||||
TEST_ASSERT_EQUAL(-1, mq_timedsend(mq_fix.mq, (char*) &to_send, sizeof(to_send), 0, &invalid_time));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
invalid_time.tv_nsec = -1;
|
||||
TEST_ASSERT_EQUAL(-1, mq_timedsend(mq_fix.mq, (char*) &to_send, sizeof(to_send), 0, &invalid_time));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("sending with invalid message size fails", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
|
||||
int to_send = 47;
|
||||
TEST_ASSERT_EQUAL(-1, mq_send(mq_fix.mq, (char*) &to_send, sizeof(to_send) + 1, 0));
|
||||
TEST_ASSERT_EQUAL(EMSGSIZE, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("sending does not exceed queue size", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
|
||||
int to_send = 47;
|
||||
TEST_ASSERT_EQUAL(0, mq_send(mq_fix.mq, (const char*) &to_send, sizeof(to_send), 0));
|
||||
int to_send_2 = 48;
|
||||
int received = 0;
|
||||
|
||||
const struct timespec zero_time = {0, 0};
|
||||
TEST_ASSERT_EQUAL(-1, mq_timedsend(mq_fix.mq, (const char*) &to_send_2, sizeof(to_send_2), 0, &zero_time));
|
||||
TEST_ASSERT_EQUAL(ETIMEDOUT, errno);
|
||||
|
||||
TEST_ASSERT_EQUAL(4, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
|
||||
TEST_ASSERT_EQUAL(47, received);
|
||||
}
|
||||
|
||||
TEST_CASE("nonblocking send works", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix("/test", 1, sizeof(int), O_CREAT | O_WRONLY | O_NONBLOCK);
|
||||
int to_send = 47;
|
||||
int received = 0;
|
||||
TEST_ASSERT_EQUAL(0, mq_send(mq_fix.mq, (char*) &to_send, sizeof(to_send), 0));
|
||||
int to_send_2 = 48;
|
||||
const struct timespec zero_time = {0, 0};
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, mq_timedsend(mq_fix.mq, (char*) &to_send_2, sizeof(to_send_2), 0, &zero_time));
|
||||
TEST_ASSERT_EQUAL(EAGAIN, errno);
|
||||
TEST_ASSERT_EQUAL(-1, mq_send(mq_fix.mq, (char*) &to_send_2, sizeof(to_send_2), 0));
|
||||
TEST_ASSERT_EQUAL(EAGAIN, errno);
|
||||
|
||||
TEST_ASSERT_EQUAL(4, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
|
||||
TEST_ASSERT_EQUAL(47, received);
|
||||
}
|
||||
|
||||
TEST_CASE("receiving on invalid mq descriptor fails", "[mqueue]")
|
||||
{
|
||||
int received;
|
||||
TEST_ASSERT_EQUAL(-1, mq_receive(nullptr, (char*) &received, sizeof(received), nullptr));
|
||||
TEST_ASSERT_EQUAL(EBADF, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("receiving with invalid timeout fails", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
|
||||
int received;
|
||||
struct timespec invalid_time = {0, 1'000'000'000};
|
||||
TEST_ASSERT_EQUAL(-1, mq_timedreceive(mq_fix.mq, (char*) &received, sizeof(received), 0, &invalid_time));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
invalid_time.tv_nsec = -1;
|
||||
TEST_ASSERT_EQUAL(-1, mq_timedreceive(mq_fix.mq, (char*) &received, sizeof(received), 0, &invalid_time));
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("receiving with invalid message size fails", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
|
||||
int received;
|
||||
TEST_ASSERT_EQUAL(-1, mq_send(mq_fix.mq, (char*) &received, sizeof(received) + 1, 0));
|
||||
TEST_ASSERT_EQUAL(EMSGSIZE, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("receive on empty message queue times out", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix("/test", 1, sizeof(int));
|
||||
int received;
|
||||
const struct timespec zero_time = {0, 0};
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, mq_timedreceive(mq_fix.mq, (char*) &received, sizeof(received), 0, &zero_time));
|
||||
TEST_ASSERT_EQUAL(ETIMEDOUT, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("nonblocking receive works", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix("/test", 1, sizeof(int), O_CREAT | O_WRONLY | O_NONBLOCK);
|
||||
int received;
|
||||
const struct timespec zero_time = {0, 0};
|
||||
|
||||
TEST_ASSERT_EQUAL(-1, mq_timedreceive(mq_fix.mq, (char*) &received, sizeof(received), 0, &zero_time));
|
||||
TEST_ASSERT_EQUAL(EAGAIN, errno);
|
||||
TEST_ASSERT_EQUAL(-1, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
|
||||
TEST_ASSERT_EQUAL(EAGAIN, errno);
|
||||
}
|
||||
|
||||
TEST_CASE("queue sends and receives multiple messages", "[mqueue]")
|
||||
{
|
||||
MessageQueueFixture mq_fix("/test", 2, sizeof(int), O_CREAT | O_WRONLY | O_NONBLOCK);
|
||||
int received;
|
||||
int to_send = 47;
|
||||
|
||||
TEST_ASSERT_EQUAL(0, mq_send(mq_fix.mq, (char*) &to_send, sizeof(to_send), 0));
|
||||
to_send = 48;
|
||||
TEST_ASSERT_EQUAL(0, mq_send(mq_fix.mq, (char*) &to_send, sizeof(to_send), 0));
|
||||
|
||||
TEST_ASSERT_EQUAL(4, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
|
||||
TEST_ASSERT_EQUAL(47, received);
|
||||
TEST_ASSERT_EQUAL(4, mq_receive(mq_fix.mq, (char*) &received, sizeof(received), nullptr));
|
||||
TEST_ASSERT_EQUAL(48, received);
|
||||
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.esp32p4
|
||||
@pytest.mark.esp32s2
|
||||
def test_rt_mqueue(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
2
components/rt/test_apps/posix_rt_test/sdkconfig.defaults
Normal file
2
components/rt/test_apps/posix_rt_test/sdkconfig.defaults
Normal file
@@ -0,0 +1,2 @@
|
||||
# This "default" configuration is appended to all other configurations
|
||||
CONFIG_ESP_TASK_WDT_INIT=n
|
Reference in New Issue
Block a user