tests/subsys/modem: Added unit tests for modem modules

The tests test each module independently using the mock pipe,
which implements the modem_pipe API, making it compatible
with the modem modules.

The modem_ppp module is slightly different from the others
since it also interacts with the network stack. To acheive
this, a mock implementation of the PPP L2 net iface has
been implemented in the test suite for the modem_ppp.

Signed-off-by: Bjarki Arge Andreasen <baa@trackunit.com>
This commit is contained in:
Bjarki Arge Andreasen 2023-04-10 18:20:52 +02:00 committed by Carles Cufí
commit 10ac828776
18 changed files with 2185 additions and 0 deletions

View file

@ -0,0 +1,8 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(modem_backend_tty_test)
target_sources(app PRIVATE src/main.c)

View file

@ -0,0 +1,12 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
CONFIG_NO_OPTIMIZATIONS=y
CONFIG_MODEM_MODULES=y
CONFIG_MODEM_BACKEND_TTY=y
CONFIG_ZTEST=y
CONFIG_ZTEST_NEW_API=y
CONFIG_LOG=y

View file

@ -0,0 +1,188 @@
/*
* Copyright (c) 2022 Trackunit Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/*************************************************************************************************/
/* Dependencies */
/*************************************************************************************************/
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/modem/backend/tty.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#define TEST_MODEM_BACKEND_TTY_PIPE_EVENT_OPENED_BIT (0)
#define TEST_MODEM_BACKEND_TTY_PIPE_EVENT_RRDY_BIT (1)
#define TEST_MODEM_BACKEND_TTY_PIPE_EVENT_CLOSED_BIT (2)
#define TEST_MODEM_BACKEND_TTY_OP_DELAY (K_MSEC(1000))
/*************************************************************************************************/
/* Mock pipe */
/*************************************************************************************************/
static struct modem_backend_tty tty_backend;
static struct modem_pipe *tty_pipe;
/*************************************************************************************************/
/* Mock PTY */
/*************************************************************************************************/
static int primary_fd;
/*************************************************************************************************/
/* Buffers */
/*************************************************************************************************/
static uint8_t buffer1[1024];
K_KERNEL_STACK_DEFINE(tty_stack, 4096);
/*************************************************************************************************/
/* Helpers */
/*************************************************************************************************/
static void test_modem_backend_tty_cfmakeraw(struct termios *termios_p)
{
termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios_p->c_cflag &= ~(CSIZE | PARENB);
termios_p->c_cflag |= CS8;
}
/*************************************************************************************************/
/* Modem pipe callback */
/*************************************************************************************************/
static atomic_t tty_pipe_events;
static void modem_pipe_callback_handler(struct modem_pipe *pipe, enum modem_pipe_event event,
void *user_data)
{
switch (event) {
case MODEM_PIPE_EVENT_OPENED:
atomic_set_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_OPENED_BIT);
break;
case MODEM_PIPE_EVENT_RECEIVE_READY:
atomic_set_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_RRDY_BIT);
break;
case MODEM_PIPE_EVENT_CLOSED:
atomic_set_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_CLOSED_BIT);
break;
}
}
/*************************************************************************************************/
/* Test setup */
/*************************************************************************************************/
static void *test_modem_backend_tty_setup(void)
{
const char *secondary_name;
struct termios tio;
primary_fd = posix_openpt(O_RDWR | O_NOCTTY);
__ASSERT_NO_MSG(primary_fd > -1);
__ASSERT_NO_MSG(grantpt(primary_fd) > -1);
__ASSERT_NO_MSG(unlockpt(primary_fd) > -1);
__ASSERT_NO_MSG(tcgetattr(primary_fd, &tio) > -1);
test_modem_backend_tty_cfmakeraw(&tio);
__ASSERT_NO_MSG(tcsetattr(primary_fd, TCSAFLUSH, &tio) > -1);
secondary_name = ptsname(primary_fd);
struct modem_backend_tty_config config = {
.tty_path = secondary_name,
.stack = tty_stack,
.stack_size = K_KERNEL_STACK_SIZEOF(tty_stack),
};
tty_pipe = modem_backend_tty_init(&tty_backend, &config);
modem_pipe_attach(tty_pipe, modem_pipe_callback_handler, NULL);
__ASSERT_NO_MSG(modem_pipe_open(tty_pipe) == 0);
return NULL;
}
static void test_modem_backend_tty_before(void *f)
{
atomic_set(&tty_pipe_events, 0);
}
static void test_modem_backend_tty_teardown(void *f)
{
modem_pipe_close(tty_pipe);
}
/*************************************************************************************************/
/* Tests */
/*************************************************************************************************/
ZTEST(modem_backend_tty_suite, test_close_open)
{
zassert_true(modem_pipe_close(tty_pipe) == 0, "Failed to close pipe");
zassert_true(modem_pipe_close(tty_pipe) == -EALREADY, "Pipe should already be closed");
zassert_true(modem_pipe_open(tty_pipe) == 0, "Failed to open pipe");
zassert_true(modem_pipe_open(tty_pipe) == -EALREADY, "Pipe should already be open");
}
ZTEST(modem_backend_tty_suite, test_receive_ready_event_not_raised)
{
bool result;
k_sleep(TEST_MODEM_BACKEND_TTY_OP_DELAY);
result = atomic_test_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_RRDY_BIT);
zassert_false(result, "Receive ready event should not be set");
}
ZTEST(modem_backend_tty_suite, test_receive_ready_event_raised)
{
int ret;
bool result;
char *msg = "Test me buddy";
ret = write(primary_fd, msg, sizeof(msg));
zassert_true(ret == sizeof(msg), "Failed to write to primary FD");
k_sleep(TEST_MODEM_BACKEND_TTY_OP_DELAY);
result = atomic_test_bit(&tty_pipe_events, TEST_MODEM_BACKEND_TTY_PIPE_EVENT_RRDY_BIT);
zassert_true(result == true, "Receive ready evennt not set");
}
ZTEST(modem_backend_tty_suite, test_receive)
{
int ret;
char msg[] = "Test me buddy";
ret = write(primary_fd, msg, sizeof(msg));
zassert_true(ret == sizeof(msg), "Failed to write to primary FD");
k_sleep(TEST_MODEM_BACKEND_TTY_OP_DELAY);
ret = modem_pipe_receive(tty_pipe, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(msg), "Received incorrect number of bytes");
ret = memcmp(msg, buffer1, sizeof(msg));
zassert_true(ret == 0, "Received incorrect bytes");
}
ZTEST(modem_backend_tty_suite, test_transmit)
{
int ret;
char msg[] = "Test me buddy 2";
ret = modem_pipe_transmit(tty_pipe, msg, sizeof(msg));
zassert_true(ret == sizeof(msg), "Failed to transmit using pipe");
k_sleep(TEST_MODEM_BACKEND_TTY_OP_DELAY);
ret = read(primary_fd, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(msg), "Read incorrect number of bytes");
ret = memcmp(msg, buffer1, sizeof(msg));
zassert_true(ret == 0, "Read incorrect bytes");
}
ZTEST_SUITE(modem_backend_tty_suite, NULL, test_modem_backend_tty_setup,
test_modem_backend_tty_before, NULL, test_modem_backend_tty_teardown);

View file

@ -0,0 +1,8 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
tests:
modem.backends.tty:
tags: modem_backend_tty
harness: ztest
platform_allow: native_posix

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2022 Trackunit Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "modem_backend_mock.h"
#include <string.h>
static int modem_backend_mock_open(void *data)
{
struct modem_backend_mock *mock = (struct modem_backend_mock *)data;
modem_pipe_notify_opened(&mock->pipe);
return 0;
}
static bool modem_backend_mock_update(struct modem_backend_mock *mock, const uint8_t *buf,
size_t size)
{
if (mock->transaction == NULL) {
return false;
}
for (size_t i = 0; i < size; i++) {
__ASSERT(buf[i] == mock->transaction->get[mock->transaction_match_cnt],
"Unexpected transmit data");
mock->transaction_match_cnt++;
if (mock->transaction_match_cnt == mock->transaction->get_size) {
return true;
}
}
return false;
}
static int modem_backend_mock_transmit(void *data, const uint8_t *buf, size_t size)
{
struct modem_backend_mock *mock = (struct modem_backend_mock *)data;
int ret;
size = (mock->limit < size) ? mock->limit : size;
ret = ring_buf_put(&mock->tx_rb, buf, size);
if (modem_backend_mock_update(mock, buf, size)) {
modem_backend_mock_put(mock, mock->transaction->put,
mock->transaction->put_size);
mock->transaction = NULL;
}
return ret;
}
static int modem_backend_mock_receive(void *data, uint8_t *buf, size_t size)
{
struct modem_backend_mock *mock = (struct modem_backend_mock *)data;
size = (mock->limit < size) ? mock->limit : size;
return ring_buf_get(&mock->rx_rb, buf, size);
}
static int modem_backend_mock_close(void *data)
{
struct modem_backend_mock *mock = (struct modem_backend_mock *)data;
modem_pipe_notify_closed(&mock->pipe);
return 0;
}
struct modem_pipe_api modem_backend_mock_api = {
.open = modem_backend_mock_open,
.transmit = modem_backend_mock_transmit,
.receive = modem_backend_mock_receive,
.close = modem_backend_mock_close,
};
static void modem_backend_mock_received_handler(struct k_work *item)
{
struct modem_backend_mock_work *mock_work_item = (struct modem_backend_mock_work *)item;
struct modem_backend_mock *mock = mock_work_item->mock;
modem_pipe_notify_receive_ready(&mock->pipe);
}
struct modem_pipe *modem_backend_mock_init(struct modem_backend_mock *mock,
const struct modem_backend_mock_config *config)
{
memset(mock, 0, sizeof(*mock));
ring_buf_init(&mock->rx_rb, config->rx_buf_size, config->rx_buf);
ring_buf_init(&mock->tx_rb, config->tx_buf_size, config->tx_buf);
mock->received_work_item.mock = mock;
k_work_init(&mock->received_work_item.work, modem_backend_mock_received_handler);
mock->limit = config->limit;
modem_pipe_init(&mock->pipe, mock, &modem_backend_mock_api);
return &mock->pipe;
}
struct modem_pipe *modem_backend_mock_get_pipe(struct modem_backend_mock *mock)
{
return &mock->pipe;
}
void modem_backend_mock_reset(struct modem_backend_mock *mock)
{
ring_buf_reset(&mock->rx_rb);
ring_buf_reset(&mock->tx_rb);
mock->transaction = NULL;
mock->transaction_match_cnt = 0;
}
int modem_backend_mock_get(struct modem_backend_mock *mock, uint8_t *buf, size_t size)
{
return ring_buf_get(&mock->tx_rb, buf, size);
}
void modem_backend_mock_put(struct modem_backend_mock *mock, const uint8_t *buf, size_t size)
{
__ASSERT(ring_buf_put(&mock->rx_rb, buf, size) == size,
"Mock buffer capacity exceeded");
k_work_submit(&mock->received_work_item.work);
}
void modem_backend_mock_prime(struct modem_backend_mock *mock,
const struct modem_backend_mock_transaction *transaction)
{
mock->transaction = transaction;
mock->transaction_match_cnt = 0;
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2022 Trackunit Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/modem/pipe.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/ring_buffer.h>
#ifndef ZEPHYR_DRIVERS_MODEM_MODEM_PIPE_MOCK
#define ZEPHYR_DRIVERS_MODEM_MODEM_PIPE_MOCK
struct modem_backend_mock;
struct modem_backend_mock_work {
struct k_work work;
struct modem_backend_mock *mock;
};
struct modem_backend_mock_transaction {
/* Get data which will trigger put */
const uint8_t *get;
size_t get_size;
/* Data which will be put in response to get data */
const uint8_t *put;
size_t put_size;
};
struct modem_backend_mock {
struct modem_pipe pipe;
struct ring_buf rx_rb;
struct ring_buf tx_rb;
struct modem_backend_mock_work received_work_item;
const struct modem_backend_mock_transaction *transaction;
size_t transaction_match_cnt;
/* Max allowed read/write size */
size_t limit;
};
struct modem_backend_mock_config {
uint8_t *rx_buf;
size_t rx_buf_size;
uint8_t *tx_buf;
size_t tx_buf_size;
size_t limit;
};
struct modem_pipe *modem_backend_mock_init(struct modem_backend_mock *mock,
const struct modem_backend_mock_config *config);
void modem_backend_mock_reset(struct modem_backend_mock *mock);
int modem_backend_mock_get(struct modem_backend_mock *mock, uint8_t *buf, size_t size);
void modem_backend_mock_put(struct modem_backend_mock *mock, const uint8_t *buf, size_t size);
void modem_backend_mock_prime(struct modem_backend_mock *mock,
const struct modem_backend_mock_transaction *transaction);
#endif /* ZEPHYR_DRIVERS_MODEM_MODEM_PIPE_MOCK */

View file

@ -0,0 +1,9 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(modem_cmux_test)
target_sources(app PRIVATE src/main.c ../mock/modem_backend_mock.c)
target_include_directories(app PRIVATE ../mock)

View file

@ -0,0 +1,10 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
CONFIG_NO_OPTIMIZATIONS=y
CONFIG_MODEM_MODULES=y
CONFIG_MODEM_CHAT=y
CONFIG_ZTEST=y
CONFIG_ZTEST_NEW_API=y

View file

@ -0,0 +1,423 @@
/*
* Copyright (c) 2022 Trackunit Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/*************************************************************************************************/
/* Dependencies */
/*************************************************************************************************/
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/atomic.h>
#include <string.h>
#include <zephyr/modem/chat.h>
#include <modem_backend_mock.h>
/*************************************************************************************************/
/* Instances */
/*************************************************************************************************/
static struct modem_chat cmd;
static uint8_t cmd_delimiter[] = {'\r', '\n'};
static uint8_t cmd_receive_buf[128];
static uint8_t *cmd_argv[32];
static uint32_t cmd_user_data = 0x145212;
static struct modem_backend_mock mock;
static uint8_t mock_rx_buf[128];
static uint8_t mock_tx_buf[128];
static struct modem_pipe *mock_pipe;
/*************************************************************************************************/
/* Track callbacks */
/*************************************************************************************************/
#define MODEM_CHAT_UTEST_ON_IMEI_CALLED_BIT (0)
#define MODEM_CHAT_UTEST_ON_CREG_CALLED_BIT (1)
#define MODEM_CHAT_UTEST_ON_CGREG_CALLED_BIT (2)
#define MODEM_CHAT_UTEST_ON_QENG_SERVINGCELL_CALLED_BIT (3)
#define MODEM_CHAT_UTEST_ON_NO_CARRIER_CALLED_BIT (4)
#define MODEM_CHAT_UTEST_ON_ERROR_CALLED_BIT (5)
#define MODEM_CHAT_UTEST_ON_RDY_CALLED_BIT (6)
#define MODEM_CHAT_UTEST_ON_APP_RDY_CALLED_BIT (7)
#define MODEM_CHAT_UTEST_ON_NORMAL_POWER_DOWN_CALLED_BIT (8)
#define MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT (9)
static atomic_t callback_called;
/*************************************************************************************************/
/* Script callbacks args copy */
/*************************************************************************************************/
static uint8_t argv_buffers[32][128];
static uint16_t argc_buffers;
static void clone_args(char **argv, uint16_t argc)
{
argc_buffers = argc;
for (uint16_t i = 0; i < argc; i++) {
memcpy(argv_buffers[i], argv[i], strlen(argv[i]) + 1);
}
}
/*************************************************************************************************/
/* Script match callbacks */
/*************************************************************************************************/
static void on_imei(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
{
atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_IMEI_CALLED_BIT);
clone_args(argv, argc);
}
static void on_creg(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
{
atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_CREG_CALLED_BIT);
clone_args(argv, argc);
}
static void on_cgreg(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
{
atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_CGREG_CALLED_BIT);
clone_args(argv, argc);
}
static void on_qeng_serving_cell(struct modem_chat *cmd, char **argv, uint16_t argc,
void *user_data)
{
atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_QENG_SERVINGCELL_CALLED_BIT);
clone_args(argv, argc);
}
static void on_no_carrier(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
{
atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_NO_CARRIER_CALLED_BIT);
clone_args(argv, argc);
}
static void on_error(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
{
atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_ERROR_CALLED_BIT);
clone_args(argv, argc);
}
static void on_rdy(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
{
atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_RDY_CALLED_BIT);
clone_args(argv, argc);
}
static void on_app_rdy(struct modem_chat *cmd, char **argv, uint16_t argc, void *user_data)
{
atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_APP_RDY_CALLED_BIT);
clone_args(argv, argc);
}
static void on_normal_power_down(struct modem_chat *cmd, char **argv, uint16_t argc,
void *user_data)
{
atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_NORMAL_POWER_DOWN_CALLED_BIT);
clone_args(argv, argc);
}
/*************************************************************************************************/
/* Script callback */
/*************************************************************************************************/
static enum modem_chat_script_result script_result;
static void *script_result_user_data;
static void on_script_result(struct modem_chat *cmd, enum modem_chat_script_result result,
void *user_data)
{
atomic_set_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
script_result = result;
script_result_user_data = user_data;
}
/*************************************************************************************************/
/* Script */
/*************************************************************************************************/
MODEM_CHAT_MATCH_DEFINE(ok_match, "OK", "", NULL);
MODEM_CHAT_MATCH_DEFINE(imei_match, "", "", on_imei);
MODEM_CHAT_MATCH_DEFINE(creg_match, "CREG: ", ",", on_creg);
MODEM_CHAT_MATCH_DEFINE(cgreg_match, "CGREG: ", ",", on_cgreg);
MODEM_CHAT_MATCH_DEFINE(qeng_servinc_cell_match, "+QENG: \"servingcell\",", ",",
on_qeng_serving_cell);
MODEM_CHAT_MATCHES_DEFINE(unsol_matches, MODEM_CHAT_MATCH("RDY", "", on_rdy),
MODEM_CHAT_MATCH("APP RDY", "", on_app_rdy),
MODEM_CHAT_MATCH("NORMAL POWER DOWN", "", on_normal_power_down));
MODEM_CHAT_SCRIPT_CMDS_DEFINE(
script_cmds, MODEM_CHAT_SCRIPT_CMD_RESP("AT", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("ATE0", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("IMEI?", imei_match), MODEM_CHAT_SCRIPT_CMD_RESP("", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+CREG?;+CGREG?", creg_match),
MODEM_CHAT_SCRIPT_CMD_RESP("", cgreg_match), MODEM_CHAT_SCRIPT_CMD_RESP("", ok_match),
MODEM_CHAT_SCRIPT_CMD_RESP("AT+QENG=\"servingcell\"", qeng_servinc_cell_match),
MODEM_CHAT_SCRIPT_CMD_RESP("", ok_match));
MODEM_CHAT_MATCHES_DEFINE(abort_matches, MODEM_CHAT_MATCH("NO CARRIER", "", on_no_carrier),
MODEM_CHAT_MATCH("ERROR ", ",:", on_error));
MODEM_CHAT_SCRIPT_DEFINE(script, script_cmds, abort_matches, on_script_result, 4);
/*************************************************************************************************/
/* Script responses */
/*************************************************************************************************/
static const char at_response[] = "AT\r\n";
static const char ok_response[] = "OK\r\n";
static const char imei_response[] = "23412354123123\r\n";
static const char creg_response[] = "CREG: 1,2\r\n";
static const char cgreg_response[] = "CGREG: 10,43\r\n";
static const char qeng_servinc_cell_response[] = "+QENG: \"servingcell\",\"NOCONN\",\"GSM\",260"
",03,E182,AEAD,52,32,2,-68,255,255,0,38,38,1,,"
",,,,,,,,\r\n";
/*************************************************************************************************/
/* Test setup */
/*************************************************************************************************/
static void *test_modem_chat_setup(void)
{
const struct modem_chat_config cmd_config = {
.user_data = &cmd_user_data,
.receive_buf = cmd_receive_buf,
.receive_buf_size = ARRAY_SIZE(cmd_receive_buf),
.delimiter = cmd_delimiter,
.delimiter_size = ARRAY_SIZE(cmd_delimiter),
.filter = NULL,
.filter_size = 0,
.argv = cmd_argv,
.argv_size = ARRAY_SIZE(cmd_argv),
.unsol_matches = unsol_matches,
.unsol_matches_size = ARRAY_SIZE(unsol_matches),
.process_timeout = K_MSEC(2),
};
zassert(modem_chat_init(&cmd, &cmd_config) == 0, "Failed to init modem CMD");
const struct modem_backend_mock_config mock_config = {
.rx_buf = mock_rx_buf,
.rx_buf_size = ARRAY_SIZE(mock_rx_buf),
.tx_buf = mock_tx_buf,
.tx_buf_size = ARRAY_SIZE(mock_tx_buf),
.limit = 8,
};
mock_pipe = modem_backend_mock_init(&mock, &mock_config);
zassert(modem_pipe_open(mock_pipe) == 0, "Failed to open mock pipe");
zassert(modem_chat_attach(&cmd, mock_pipe) == 0, "Failed to attach pipe mock to modem CMD");
return NULL;
}
static void test_modem_chat_before(void *f)
{
/* Reset callback called */
atomic_set(&callback_called, 0);
/* Reset mock pipe */
modem_backend_mock_reset(&mock);
}
static void test_modem_chat_after(void *f)
{
/* Abort script */
modem_chat_script_abort(&cmd);
k_msleep(100);
}
/*************************************************************************************************/
/* Buffers */
/*************************************************************************************************/
static uint8_t buffer[4096];
/*************************************************************************************************/
/* Tests */
/*************************************************************************************************/
ZTEST(modem_chat, test_script_no_error)
{
bool called;
zassert_true(modem_chat_script_run(&cmd, &script) == 0, "Failed to start script");
k_msleep(100);
/*
* Script sends "AT\r\n"
* Modem responds "AT\r\n"
* Modem responds "OK\r\n"
*/
modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
zassert_true(memcmp(buffer, "AT\r", sizeof("AT\r") - 1) == 0,
"Request not sent as expected");
modem_backend_mock_put(&mock, at_response, sizeof(at_response) - 1);
modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
k_msleep(100);
/*
* Script sends "ATE0\r\n"
* Modem responds "OK\r\n"
*/
modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
zassert_true(memcmp(buffer, "ATE0\r\n", sizeof("ATE0\r\n") - 1) == 0,
"Request not sent as expected");
modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
k_msleep(100);
/*
* Script sends "IMEI?\r\n"
* Modem responds "23412354123123\r\n"
* Modem responds "OK\r\n"
*/
modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
zassert_true(memcmp(buffer, "IMEI?\r\n", sizeof("IMEI?\r\n") - 1) == 0,
"Request not sent as expected");
modem_backend_mock_put(&mock, imei_response, sizeof(imei_response) - 1);
modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
k_msleep(100);
zassert_true(atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_IMEI_CALLED_BIT) == true,
"Expected IMEI callback called");
zassert_true(argv_buffers[0][0] == '\0', "Unexpected argv");
zassert_true(memcmp(argv_buffers[1], "23412354123123", sizeof("23412354123123")) == 0,
"Unexpected argv");
zassert_true(argc_buffers == 2, "Unexpected argc");
/*
* Script sends "AT+CREG?;+CGREG?\r\n"
* Modem responds "CREG: 1,2\r\n"
* Modem responds "CGREG: 1,2\r\n"
* Modem responds "OK\r\n"
*/
modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
zassert_true(memcmp(buffer, "AT+CREG?;+CGREG?\r\n", sizeof("AT+CREG?;+CGREG?\r\n") - 1) ==
0,
"Request not sent as expected");
modem_backend_mock_put(&mock, creg_response, sizeof(creg_response) - 1);
k_msleep(100);
zassert_true(memcmp(argv_buffers[0], "CREG: ", sizeof("CREG: ")) == 0, "Unexpected argv");
zassert_true(memcmp(argv_buffers[1], "1", sizeof("1")) == 0, "Unexpected argv");
zassert_true(memcmp(argv_buffers[2], "2", sizeof("2")) == 0, "Unexpected argv");
zassert_true(argc_buffers == 3, "Unexpected argc");
modem_backend_mock_put(&mock, cgreg_response, sizeof(cgreg_response) - 1);
k_msleep(100);
zassert_true(memcmp(argv_buffers[0], "CGREG: ", sizeof("CGREG: ")) == 0, "Unexpected argv");
zassert_true(memcmp(argv_buffers[1], "10", sizeof("10")) == 0, "Unexpected argv");
zassert_true(memcmp(argv_buffers[2], "43", sizeof("43")) == 0, "Unexpected argv");
zassert_true(argc_buffers == 3, "Unexpected argc");
modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
k_msleep(100);
/*
* Script sends "AT+QENG=\"servingcell\"\r\n"
* Modem responds qeng_servinc_cell_response (long string)
* Modem responds "OK\r\n"
*/
modem_backend_mock_get(&mock, buffer, ARRAY_SIZE(buffer));
zassert_true(memcmp(buffer, "AT+QENG=\"servingcell\"\r\n",
sizeof("AT+QENG=\"servingcell\"\r\n") - 1) == 0,
"Request not sent as expected");
modem_backend_mock_put(&mock, qeng_servinc_cell_response,
sizeof(qeng_servinc_cell_response) - 1);
k_msleep(100);
zassert_true(memcmp(argv_buffers[0], "+QENG: \"servingcell\",",
sizeof("+QENG: \"servingcell\",")) == 0,
"Unexpected argv");
zassert_true(memcmp(argv_buffers[1], "\"NOCONN\"", sizeof("\"NOCONN\"")) == 0,
"Unexpected argv");
zassert_true(memcmp(argv_buffers[10], "-68", sizeof("-68")) == 0, "Unexpected argv");
zassert_true(argv_buffers[25][0] == '\0', "Unexpected argv");
zassert_true(argc_buffers == 26, "Unexpected argc");
/*
* Script ends after modem responds OK
*/
called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
zassert_true(called == false, "Script callback should not have been called yet");
modem_backend_mock_put(&mock, ok_response, sizeof(ok_response) - 1);
k_msleep(100);
called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
zassert_true(called == true, "Script callback should have been called");
zassert_true(script_result == MODEM_CHAT_SCRIPT_RESULT_SUCCESS,
"Script result should be SUCCESS");
zassert_true(script_result_user_data == &cmd_user_data,
"Script result callback user data is incorrect");
}
ZTEST(modem_chat, test_start_script_twice_then_abort)
{
bool called;
zassert_true(modem_chat_script_run(&cmd, &script) == 0, "Failed to start script");
k_msleep(100);
zassert_true(modem_chat_script_run(&cmd, &script) == -EBUSY,
"Started new script while script is running");
called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
zassert_true(called == false, "Script callback should not have been called yet");
modem_chat_script_abort(&cmd);
k_msleep(100);
called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
zassert_true(called == true, "Script callback should have been called");
zassert_true(script_result == MODEM_CHAT_SCRIPT_RESULT_ABORT,
"Script result should be ABORT");
zassert_true(script_result_user_data == &cmd_user_data,
"Script result callback user data is incorrect");
}
ZTEST(modem_chat, test_start_script_then_time_out)
{
bool called;
zassert_true(modem_chat_script_run(&cmd, &script) == 0, "Failed to start script");
k_msleep(100);
called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
zassert_true(called == false, "Script callback should not have been called yet");
k_msleep(5900);
called = atomic_test_bit(&callback_called, MODEM_CHAT_UTEST_ON_SCRIPT_CALLBACK_BIT);
zassert_true(called == true, "Script callback should have been called");
zassert_true(script_result == MODEM_CHAT_SCRIPT_RESULT_TIMEOUT,
"Script result should be TIMEOUT");
zassert_true(script_result_user_data == &cmd_user_data,
"Script result callback user data is incorrect");
}
/*************************************************************************************************/
/* Test suite */
/*************************************************************************************************/
ZTEST_SUITE(modem_chat, NULL, test_modem_chat_setup, test_modem_chat_before, test_modem_chat_after,
NULL);

View file

@ -0,0 +1,8 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
tests:
modem.modem_chat:
tags: modem_chat
harness: ztest
platform_allow: native_posix

View file

@ -0,0 +1,9 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(modem_cmux_test)
target_sources(app PRIVATE src/main.c ../mock/modem_backend_mock.c)
target_include_directories(app PRIVATE ../mock)

View file

@ -0,0 +1,10 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
CONFIG_NO_OPTIMIZATIONS=y
CONFIG_MODEM_MODULES=y
CONFIG_MODEM_CMUX=y
CONFIG_ZTEST=y
CONFIG_ZTEST_NEW_API=y

View file

@ -0,0 +1,739 @@
/*
* Copyright (c) 2022 Trackunit Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/*************************************************************************************************/
/* Dependencies */
/*************************************************************************************************/
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <string.h>
#include <zephyr/modem/cmux.h>
#include <modem_backend_mock.h>
/*************************************************************************************************/
/* Definitions */
/*************************************************************************************************/
#define EVENT_CMUX_CONNECTED BIT(0)
#define EVENT_CMUX_DLCI1_OPEN BIT(1)
#define EVENT_CMUX_DLCI2_OPEN BIT(2)
#define EVENT_CMUX_DLCI1_CLOSED BIT(3)
#define EVENT_CMUX_DLCI2_CLOSED BIT(4)
#define EVENT_CMUX_DISCONNECTED BIT(5)
/*************************************************************************************************/
/* Instances */
/*************************************************************************************************/
static struct modem_cmux cmux;
static uint8_t cmux_receive_buf[127];
static uint8_t cmux_transmit_buf[149];
static struct modem_cmux_dlci dlci1;
static struct modem_cmux_dlci dlci2;
static struct modem_pipe *dlci1_pipe;
static struct modem_pipe *dlci2_pipe;
static struct k_event cmux_event;
static struct modem_backend_mock bus_mock;
static uint8_t bus_mock_rx_buf[4096];
static uint8_t bus_mock_tx_buf[4096];
static struct modem_pipe *bus_mock_pipe;
static uint8_t dlci1_receive_buf[127];
static uint8_t dlci2_receive_buf[127];
static uint8_t buffer1[4096];
static uint8_t buffer2[4096];
/*************************************************************************************************/
/* Callbacks */
/*************************************************************************************************/
static void test_modem_dlci1_pipe_callback(struct modem_pipe *pipe, enum modem_pipe_event event,
void *user_data)
{
switch (event) {
case MODEM_PIPE_EVENT_OPENED:
k_event_post(&cmux_event, EVENT_CMUX_DLCI1_OPEN);
break;
case MODEM_PIPE_EVENT_CLOSED:
k_event_post(&cmux_event, EVENT_CMUX_DLCI1_CLOSED);
break;
default:
break;
}
}
static void test_modem_dlci2_pipe_callback(struct modem_pipe *pipe, enum modem_pipe_event event,
void *user_data)
{
switch (event) {
case MODEM_PIPE_EVENT_OPENED:
k_event_post(&cmux_event, EVENT_CMUX_DLCI2_OPEN);
break;
case MODEM_PIPE_EVENT_CLOSED:
k_event_post(&cmux_event, EVENT_CMUX_DLCI2_CLOSED);
break;
default:
break;
}
}
/*************************************************************************************************/
/* CMUX frames */
/*************************************************************************************************/
static uint8_t cmux_frame_control_sabm_cmd[] = {0xF9, 0x03, 0x3F, 0x01, 0x1C, 0xF9};
static uint8_t cmux_frame_control_sabm_ack[] = {0xF9, 0x03, 0x73, 0x01, 0xD7, 0xF9};
static uint8_t cmux_frame_control_cld_cmd[] = {0xF9, 0x03, 0xEF, 0x05, 0xC3, 0x01, 0xF2, 0xF9};
static uint8_t cmux_frame_control_cld_ack[] = {0xF9, 0x03, 0xEF, 0x05, 0xC1, 0x01, 0xF2, 0xF9};
static uint8_t cmux_frame_dlci1_sabm_cmd[] = {0xF9, 0x07, 0x3F, 0x01, 0xDE, 0xF9};
static uint8_t cmux_frame_dlci1_sabm_ack[] = {0xF9, 0x07, 0x73, 0x01, 0x15, 0xF9};
static uint8_t cmux_frame_dlci1_disc_cmd[] = {0xF9, 0x07, 0x53, 0x01, 0x3F, 0xF9};
static uint8_t cmux_frame_dlci1_ua_ack[] = {0xF9, 0x07, 0x73, 0x01, 0x15, 0xF9};
static uint8_t cmux_frame_dlci2_sabm_cmd[] = {0xF9, 0x0B, 0x3F, 0x01, 0x59, 0xF9};
static uint8_t cmux_frame_dlci2_sabm_ack[] = {0xF9, 0x0B, 0x73, 0x01, 0x92, 0xF9};
static uint8_t cmux_frame_dlci2_disc_cmd[] = {0xF9, 0x0B, 0x53, 0x01, 0xB8, 0xF9};
static uint8_t cmux_frame_dlci2_ua_ack[] = {0xF9, 0x0B, 0x73, 0x01, 0x92, 0xF9};
static uint8_t cmux_frame_control_msc_cmd[] = {0xF9, 0x01, 0xFF, 0x09, 0xE3,
0x05, 0x0B, 0x09, 0x8F, 0xF9};
static uint8_t cmux_frame_control_msc_ack[] = {0xF9, 0x01, 0xFF, 0x09, 0xE1,
0x05, 0x0B, 0x09, 0x8F, 0xF9};
static uint8_t cmux_frame_control_fcon_cmd[] = {0xF9, 0x01, 0xFF, 0x05, 0xA3, 0x01, 0x86, 0xF9};
static uint8_t cmux_frame_control_fcon_ack[] = {0xF9, 0x01, 0xFF, 0x05, 0xA1, 0x01, 0x86, 0xF9};
static uint8_t cmux_frame_control_fcoff_cmd[] = {0xF9, 0x01, 0xFF, 0x05, 0x63, 0x01, 0x86, 0xF9};
static uint8_t cmux_frame_control_fcoff_ack[] = {0xF9, 0x01, 0xFF, 0x05, 0x61, 0x01, 0x86, 0xF9};
/*************************************************************************************************/
/* DLCI2 AT CMUX frames */
/*************************************************************************************************/
static uint8_t cmux_frame_dlci2_at_cgdcont[] = {
0xF9, 0x0B, 0xEF, 0x43, 0x41, 0x54, 0x2B, 0x43, 0x47, 0x44, 0x43, 0x4F, 0x4E,
0x54, 0x3D, 0x31, 0x2C, 0x22, 0x49, 0x50, 0x22, 0x2C, 0x22, 0x74, 0x72, 0x61,
0x63, 0x6B, 0x75, 0x6E, 0x69, 0x74, 0x2E, 0x6D, 0x32, 0x6D, 0x22, 0x23, 0xF9};
static uint8_t cmux_frame_data_dlci2_at_cgdcont[] = {
0x41, 0x54, 0x2B, 0x43, 0x47, 0x44, 0x43, 0x4F, 0x4E, 0x54, 0x3D,
0x31, 0x2C, 0x22, 0x49, 0x50, 0x22, 0x2C, 0x22, 0x74, 0x72, 0x61,
0x63, 0x6B, 0x75, 0x6E, 0x69, 0x74, 0x2E, 0x6D, 0x32, 0x6D, 0x22};
static uint8_t cmux_frame_dlci2_at_newline[] = {0xF9, 0x0B, 0xEF, 0x05, 0x0D, 0x0A, 0xB7, 0xF9};
static uint8_t cmux_frame_data_dlci2_at_newline[] = {0x0D, 0x0A};
/*************************************************************************************************/
/* DLCI1 AT CMUX frames */
/*************************************************************************************************/
static uint8_t cmux_frame_dlci1_at_at[] = {0xF9, 0x07, 0xEF, 0x05, 0x41, 0x54, 0x30, 0xF9};
static uint8_t cmux_frame_data_dlci1_at_at[] = {0x41, 0x54};
static uint8_t cmux_frame_dlci1_at_newline[] = {0xF9, 0x07, 0xEF, 0x05, 0x0D, 0x0A, 0x30, 0xF9};
static uint8_t cmux_frame_data_dlci1_at_newline[] = {0x0D, 0x0A};
/*************************************************************************************************/
/* DLCI1 AT CMUX Desync frames */
/*************************************************************************************************/
static uint8_t cmux_frame_dlci1_at_at_desync[] = {0x41, 0x54, 0x30, 0xF9};
static uint8_t cmux_frame_resync[] = {0xF9, 0xF9, 0xF9};
/*************************************************************************************************/
/* DLCI2 PPP CMUX frames */
/*************************************************************************************************/
static uint8_t cmux_frame_dlci2_ppp_52[] = {
0xF9, 0x09, 0xEF, 0x69, 0x7E, 0xFF, 0x7D, 0x23, 0xC0, 0x21, 0x7D, 0x21, 0x7D, 0x20, 0x7D,
0x20, 0x7D, 0x38, 0x7D, 0x22, 0x7D, 0x26, 0x7D, 0x20, 0x7D, 0x20, 0x7D, 0x20, 0x7D, 0x20,
0x7D, 0x23, 0x7D, 0x24, 0xC0, 0x23, 0x7D, 0x25, 0x7D, 0x26, 0x53, 0x96, 0x7D, 0x38, 0xAA,
0x7D, 0x27, 0x7D, 0x22, 0x7D, 0x28, 0x7D, 0x22, 0xD5, 0xA8, 0x7E, 0x97, 0xF9};
static uint8_t cmux_frame_data_dlci2_ppp_52[] = {
0x7E, 0xFF, 0x7D, 0x23, 0xC0, 0x21, 0x7D, 0x21, 0x7D, 0x20, 0x7D, 0x20, 0x7D,
0x38, 0x7D, 0x22, 0x7D, 0x26, 0x7D, 0x20, 0x7D, 0x20, 0x7D, 0x20, 0x7D, 0x20,
0x7D, 0x23, 0x7D, 0x24, 0xC0, 0x23, 0x7D, 0x25, 0x7D, 0x26, 0x53, 0x96, 0x7D,
0x38, 0xAA, 0x7D, 0x27, 0x7D, 0x22, 0x7D, 0x28, 0x7D, 0x22, 0xD5, 0xA8, 0x7E};
static uint8_t cmux_frame_dlci2_ppp_18[] = {0xF9, 0x09, 0xEF, 0x25, 0x7E, 0xFF, 0x7D, 0x23,
0xC0, 0x21, 0x7D, 0x22, 0x7D, 0x21, 0x7D, 0x20,
0x7D, 0x24, 0x7D, 0x3C, 0x90, 0x7E, 0xEE, 0xF9};
static uint8_t cmux_frame_data_dlci2_ppp_18[] = {0x7E, 0xFF, 0x7D, 0x23, 0xC0, 0x21,
0x7D, 0x22, 0x7D, 0x21, 0x7D, 0x20,
0x7D, 0x24, 0x7D, 0x3C, 0x90, 0x7E};
const static struct modem_backend_mock_transaction transaction_control_cld = {
.get = cmux_frame_control_cld_cmd,
.get_size = sizeof(cmux_frame_control_cld_cmd),
.put = cmux_frame_control_cld_ack,
.put_size = sizeof(cmux_frame_control_cld_ack)
};
const static struct modem_backend_mock_transaction transaction_control_sabm = {
.get = cmux_frame_control_sabm_cmd,
.get_size = sizeof(cmux_frame_control_sabm_cmd),
.put = cmux_frame_control_sabm_ack,
.put_size = sizeof(cmux_frame_control_sabm_ack)
};
const static struct modem_backend_mock_transaction transaction_dlci1_disc = {
.get = cmux_frame_dlci1_disc_cmd,
.get_size = sizeof(cmux_frame_dlci1_disc_cmd),
.put = cmux_frame_dlci1_ua_ack,
.put_size = sizeof(cmux_frame_dlci1_ua_ack)
};
const static struct modem_backend_mock_transaction transaction_dlci2_disc = {
.get = cmux_frame_dlci2_disc_cmd,
.get_size = sizeof(cmux_frame_dlci2_disc_cmd),
.put = cmux_frame_dlci2_ua_ack,
.put_size = sizeof(cmux_frame_dlci2_ua_ack)
};
const static struct modem_backend_mock_transaction transaction_dlci1_sabm = {
.get = cmux_frame_dlci1_sabm_cmd,
.get_size = sizeof(cmux_frame_dlci1_sabm_cmd),
.put = cmux_frame_dlci1_ua_ack,
.put_size = sizeof(cmux_frame_dlci1_ua_ack)
};
const static struct modem_backend_mock_transaction transaction_dlci2_sabm = {
.get = cmux_frame_dlci2_sabm_cmd,
.get_size = sizeof(cmux_frame_dlci2_sabm_cmd),
.put = cmux_frame_dlci2_ua_ack,
.put_size = sizeof(cmux_frame_dlci2_ua_ack)
};
static void test_modem_cmux_callback(struct modem_cmux *cmux, enum modem_cmux_event event,
void *user_data)
{
if (event == MODEM_CMUX_EVENT_CONNECTED) {
k_event_post(&cmux_event, EVENT_CMUX_CONNECTED);
return;
}
if (event == MODEM_CMUX_EVENT_DISCONNECTED) {
k_event_post(&cmux_event, EVENT_CMUX_DISCONNECTED);
return;
}
}
static void *test_modem_cmux_setup(void)
{
uint32_t events;
struct modem_cmux_dlci_config dlci1_config = {
.dlci_address = 1,
.receive_buf = dlci1_receive_buf,
.receive_buf_size = sizeof(dlci1_receive_buf),
};
struct modem_cmux_dlci_config dlci2_config = {
.dlci_address = 2,
.receive_buf = dlci2_receive_buf,
.receive_buf_size = sizeof(dlci2_receive_buf),
};
k_event_init(&cmux_event);
struct modem_cmux_config cmux_config = {
.callback = test_modem_cmux_callback,
.user_data = NULL,
.receive_buf = cmux_receive_buf,
.receive_buf_size = sizeof(cmux_receive_buf),
.transmit_buf = cmux_transmit_buf,
.transmit_buf_size = ARRAY_SIZE(cmux_transmit_buf),
};
modem_cmux_init(&cmux, &cmux_config);
dlci1_pipe = modem_cmux_dlci_init(&cmux, &dlci1, &dlci1_config);
dlci2_pipe = modem_cmux_dlci_init(&cmux, &dlci2, &dlci2_config);
const struct modem_backend_mock_config bus_mock_config = {
.rx_buf = bus_mock_rx_buf,
.rx_buf_size = sizeof(bus_mock_rx_buf),
.tx_buf = bus_mock_tx_buf,
.tx_buf_size = sizeof(bus_mock_tx_buf),
.limit = 32,
};
bus_mock_pipe = modem_backend_mock_init(&bus_mock, &bus_mock_config);
__ASSERT_NO_MSG(modem_pipe_open_async(bus_mock_pipe) == 0);
/* Connect CMUX */
__ASSERT_NO_MSG(modem_cmux_attach(&cmux, bus_mock_pipe) == 0);
__ASSERT_NO_MSG(modem_cmux_connect_async(&cmux) == 0);
modem_backend_mock_put(&bus_mock, cmux_frame_control_sabm_ack,
sizeof(cmux_frame_control_sabm_ack));
events = k_event_wait(&cmux_event, EVENT_CMUX_CONNECTED, false, K_MSEC(100));
__ASSERT_NO_MSG(events == EVENT_CMUX_CONNECTED);
/* Open DLCI channels */
modem_pipe_attach(dlci1_pipe, test_modem_dlci1_pipe_callback, NULL);
modem_pipe_attach(dlci2_pipe, test_modem_dlci2_pipe_callback, NULL);
__ASSERT_NO_MSG(modem_pipe_open_async(dlci1_pipe) == 0);
__ASSERT_NO_MSG(modem_pipe_open_async(dlci2_pipe) == 0);
modem_backend_mock_put(&bus_mock, cmux_frame_dlci1_sabm_ack,
sizeof(cmux_frame_dlci1_sabm_ack));
modem_backend_mock_put(&bus_mock, cmux_frame_dlci2_sabm_ack,
sizeof(cmux_frame_dlci2_sabm_ack));
events = k_event_wait_all(&cmux_event, (EVENT_CMUX_DLCI1_OPEN | EVENT_CMUX_DLCI2_OPEN),
false, K_MSEC(100));
__ASSERT_NO_MSG((events & EVENT_CMUX_DLCI1_OPEN));
__ASSERT_NO_MSG((events & EVENT_CMUX_DLCI2_OPEN));
return NULL;
}
static void test_modem_cmux_before(void *f)
{
/* Reset events */
k_event_clear(&cmux_event, UINT32_MAX);
/* Reset mock pipes */
modem_backend_mock_reset(&bus_mock);
}
ZTEST(modem_cmux, test_modem_cmux_receive_dlci2_at)
{
int ret;
modem_backend_mock_put(&bus_mock, cmux_frame_dlci2_at_cgdcont,
sizeof(cmux_frame_dlci2_at_cgdcont));
modem_backend_mock_put(&bus_mock, cmux_frame_dlci2_at_newline,
sizeof(cmux_frame_dlci2_at_newline));
k_msleep(100);
ret = modem_pipe_receive(dlci2_pipe, buffer2, sizeof(buffer2));
zassert_true(ret == (sizeof(cmux_frame_data_dlci2_at_cgdcont) +
sizeof(cmux_frame_data_dlci2_at_newline)),
"Incorrect number of bytes received");
zassert_true(memcmp(buffer2, cmux_frame_data_dlci2_at_cgdcont,
sizeof(cmux_frame_data_dlci2_at_cgdcont)) == 0,
"Incorrect data received");
zassert_true(memcmp(&buffer2[sizeof(cmux_frame_data_dlci2_at_cgdcont)],
cmux_frame_data_dlci2_at_newline,
sizeof(cmux_frame_data_dlci2_at_newline)) == 0,
"Incorrect data received");
}
ZTEST(modem_cmux, test_modem_cmux_receive_dlci1_at)
{
int ret;
modem_backend_mock_put(&bus_mock, cmux_frame_dlci1_at_at, sizeof(cmux_frame_dlci1_at_at));
modem_backend_mock_put(&bus_mock, cmux_frame_dlci1_at_newline,
sizeof(cmux_frame_dlci1_at_newline));
k_msleep(100);
ret = modem_pipe_receive(dlci1_pipe, buffer1, sizeof(buffer1));
zassert_true(ret == (sizeof(cmux_frame_data_dlci1_at_at) +
sizeof(cmux_frame_data_dlci1_at_newline)),
"Incorrect number of bytes received");
zassert_true(memcmp(buffer1, cmux_frame_data_dlci1_at_at,
sizeof(cmux_frame_data_dlci1_at_at)) == 0,
"Incorrect data received");
zassert_true(memcmp(&buffer1[sizeof(cmux_frame_data_dlci1_at_at)],
cmux_frame_data_dlci1_at_newline,
sizeof(cmux_frame_data_dlci1_at_newline)) == 0,
"Incorrect data received");
}
ZTEST(modem_cmux, test_modem_cmux_receive_dlci2_ppp)
{
int ret;
modem_backend_mock_put(&bus_mock, cmux_frame_dlci2_ppp_52, sizeof(cmux_frame_dlci2_ppp_52));
modem_backend_mock_put(&bus_mock, cmux_frame_dlci2_ppp_18, sizeof(cmux_frame_dlci2_ppp_18));
k_msleep(100);
ret = modem_pipe_receive(dlci2_pipe, buffer2, sizeof(buffer2));
zassert_true(ret == (sizeof(cmux_frame_data_dlci2_ppp_52) +
sizeof(cmux_frame_data_dlci2_ppp_18)),
"Incorrect number of bytes received");
zassert_true(memcmp(buffer2, cmux_frame_data_dlci2_ppp_52,
sizeof(cmux_frame_data_dlci2_ppp_52)) == 0,
"Incorrect data received");
zassert_true(memcmp(&buffer2[sizeof(cmux_frame_data_dlci2_ppp_52)],
cmux_frame_data_dlci2_ppp_18,
sizeof(cmux_frame_data_dlci2_ppp_18)) == 0,
"Incorrect data received");
}
ZTEST(modem_cmux, test_modem_cmux_transmit_dlci2_ppp)
{
int ret;
ret = modem_pipe_transmit(dlci2_pipe, cmux_frame_data_dlci2_ppp_52,
sizeof(cmux_frame_data_dlci2_ppp_52));
zassert_true(ret == sizeof(cmux_frame_data_dlci2_ppp_52), "Failed to send DLCI2 PPP 52");
ret = modem_pipe_transmit(dlci2_pipe, cmux_frame_data_dlci2_ppp_18,
sizeof(cmux_frame_data_dlci2_ppp_18));
zassert_true(ret == sizeof(cmux_frame_data_dlci2_ppp_18), "Failed to send DLCI2 PPP 18");
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer2, sizeof(buffer2));
zassert_true(ret == (sizeof(cmux_frame_dlci2_ppp_52) + sizeof(cmux_frame_dlci2_ppp_18)),
"Incorrect number of bytes transmitted");
}
ZTEST(modem_cmux, test_modem_cmux_resync)
{
int ret;
modem_backend_mock_put(&bus_mock, cmux_frame_dlci1_at_at_desync,
sizeof(cmux_frame_dlci1_at_at_desync));
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(cmux_frame_resync), "Expected resync flags to be sent to bus");
zassert_true(memcmp(buffer1, cmux_frame_resync, sizeof(cmux_frame_resync)) == 0,
"Expected resync flags to be sent to bus");
modem_backend_mock_put(&bus_mock, cmux_frame_resync, sizeof(cmux_frame_resync));
modem_backend_mock_put(&bus_mock, cmux_frame_dlci1_at_at, sizeof(cmux_frame_dlci1_at_at));
modem_backend_mock_put(&bus_mock, cmux_frame_dlci1_at_newline,
sizeof(cmux_frame_dlci1_at_newline));
k_msleep(100);
ret = modem_pipe_receive(dlci1_pipe, buffer1, sizeof(buffer1));
zassert_true(ret == (sizeof(cmux_frame_data_dlci1_at_at) +
sizeof(cmux_frame_data_dlci1_at_newline)),
"Incorrect number of bytes received");
zassert_true(memcmp(buffer1, cmux_frame_data_dlci1_at_at,
sizeof(cmux_frame_data_dlci1_at_at)) == 0,
"Incorrect data received");
zassert_true(memcmp(&buffer1[sizeof(cmux_frame_data_dlci1_at_at)],
cmux_frame_data_dlci1_at_newline,
sizeof(cmux_frame_data_dlci1_at_newline)) == 0,
"Incorrect data received");
}
ZTEST(modem_cmux, test_modem_cmux_flow_control_dlci2)
{
int ret;
modem_backend_mock_put(&bus_mock, cmux_frame_control_fcoff_cmd,
sizeof(cmux_frame_control_fcoff_cmd));
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(cmux_frame_control_fcoff_ack),
"Incorrect number of bytes received");
zassert_true(memcmp(buffer1, cmux_frame_control_fcoff_ack,
sizeof(cmux_frame_control_fcoff_ack)) == 0,
"Incorrect data received");
ret = modem_pipe_transmit(dlci2_pipe, cmux_frame_data_dlci2_ppp_52,
sizeof(cmux_frame_data_dlci2_ppp_52));
zassert_true(ret == 0, "Failed to block transmit while flow control is off");
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == 0, "FCOFF failed to prevent transmission of data");
modem_backend_mock_put(&bus_mock, cmux_frame_control_fcon_cmd,
sizeof(cmux_frame_control_fcon_cmd));
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(cmux_frame_control_fcon_ack),
"Incorrect number of bytes received");
zassert_true(memcmp(buffer1, cmux_frame_control_fcon_ack,
sizeof(cmux_frame_control_fcon_ack)) == 0,
"Incorrect data received");
ret = modem_pipe_transmit(dlci2_pipe, cmux_frame_data_dlci2_ppp_52,
sizeof(cmux_frame_data_dlci2_ppp_52));
zassert_true(ret == sizeof(cmux_frame_data_dlci2_ppp_52),
"Transmit failed after flow control is enabled");
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(cmux_frame_dlci2_ppp_52),
"Transmit failed after flow control is enabled");
zassert_true(memcmp(buffer1, cmux_frame_dlci2_ppp_52,
sizeof(cmux_frame_dlci2_ppp_52)) == 0,
"Incorrect data received");
}
ZTEST(modem_cmux, test_modem_cmux_msc_cmd_ack)
{
int ret;
modem_backend_mock_put(&bus_mock, cmux_frame_control_msc_cmd,
sizeof(cmux_frame_control_msc_cmd));
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(cmux_frame_control_msc_ack),
"Incorrect number of bytes received");
zassert_true(memcmp(buffer1, cmux_frame_control_msc_ack,
sizeof(cmux_frame_control_msc_ack)) == 0,
"Incorrect MSC ACK received");
}
ZTEST(modem_cmux, test_modem_cmux_dlci1_close_open)
{
int ret;
uint32_t events;
/* Close DLCI1 */
zassert_true(modem_pipe_close_async(dlci1_pipe) == 0, "Failed to close DLCI1 pipe");
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(cmux_frame_dlci1_disc_cmd),
"Incorrect number of bytes received for DLCI1 close cmd");
zassert_true(memcmp(buffer1, cmux_frame_dlci1_disc_cmd,
sizeof(cmux_frame_dlci1_disc_cmd)) == 0,
"Incorrect DLCI1 close cmd received");
modem_backend_mock_put(&bus_mock, cmux_frame_dlci1_ua_ack,
sizeof(cmux_frame_dlci1_ua_ack));
events = k_event_wait_all(&cmux_event, (EVENT_CMUX_DLCI1_CLOSED),
false, K_MSEC(100));
zassert_true((events & EVENT_CMUX_DLCI1_CLOSED),
"DLCI1 not closed as expected");
/* Wait for potential T1 timeout */
k_msleep(500);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == 0, "Received unexpected data");
/* Open DLCI1 */
zassert_true(modem_pipe_open_async(dlci1_pipe) == 0, "Failed to open DLCI1 pipe");
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(cmux_frame_dlci1_sabm_cmd),
"Incorrect number of bytes received for DLCI1 open cmd");
zassert_true(memcmp(buffer1, cmux_frame_dlci1_sabm_cmd,
sizeof(cmux_frame_dlci1_sabm_cmd)) == 0,
"Incorrect DLCI1 open cmd received");
modem_backend_mock_put(&bus_mock, cmux_frame_dlci1_sabm_ack,
sizeof(cmux_frame_dlci1_sabm_ack));
events = k_event_wait_all(&cmux_event, (EVENT_CMUX_DLCI1_OPEN),
false, K_MSEC(100));
zassert_true((events & EVENT_CMUX_DLCI1_OPEN),
"DLCI1 not opened as expected");
/* Wait for potential T1 timeout */
k_msleep(500);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == 0, "Received unexpected data");
}
ZTEST(modem_cmux, test_modem_cmux_disconnect_connect)
{
uint32_t events;
int ret;
/* Disconnect CMUX */
zassert_true(modem_pipe_close_async(dlci1_pipe) == 0, "Failed to close DLCI1");
zassert_true(modem_pipe_close_async(dlci2_pipe) == 0, "Failed to close DLCI2");
modem_backend_mock_put(&bus_mock, cmux_frame_dlci1_ua_ack,
sizeof(cmux_frame_dlci1_ua_ack));
modem_backend_mock_put(&bus_mock, cmux_frame_dlci2_ua_ack,
sizeof(cmux_frame_dlci2_ua_ack));
events = k_event_wait_all(&cmux_event, (EVENT_CMUX_DLCI1_CLOSED | EVENT_CMUX_DLCI2_CLOSED),
false, K_MSEC(100));
zassert_true((events & EVENT_CMUX_DLCI1_CLOSED), "Failed to close DLCI1");
zassert_true((events & EVENT_CMUX_DLCI2_CLOSED), "Failed to close DLCI2");
/* Discard CMUX DLCI DISC commands */
modem_backend_mock_reset(&bus_mock);
zassert_true(modem_cmux_disconnect_async(&cmux) == 0, "Failed to disconnect CMUX");
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(cmux_frame_control_cld_cmd),
"Incorrect number of bytes received for CLD cmd");
zassert_true(memcmp(buffer1, cmux_frame_control_cld_cmd,
sizeof(cmux_frame_control_cld_cmd)) == 0,
"Incorrect DLC cmd received");
modem_backend_mock_put(&bus_mock, cmux_frame_control_cld_ack,
sizeof(cmux_frame_control_cld_ack));
events = k_event_wait_all(&cmux_event, (EVENT_CMUX_DISCONNECTED), false, K_MSEC(100));
zassert_true((events & EVENT_CMUX_DISCONNECTED), "Failed to disconnect CMUX");
/* Wait for potential T1 timeout */
k_msleep(500);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == 0, "Received unexpected data");
/* Reconnect CMUX */
zassert_true(modem_cmux_connect_async(&cmux) == 0, "Failed to connect CMUX");
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(cmux_frame_control_sabm_cmd),
"Incorrect number of bytes received for SABM cmd");
zassert_true(memcmp(buffer1, cmux_frame_control_sabm_cmd,
sizeof(cmux_frame_control_sabm_cmd)) == 0,
"Incorrect SABM cmd received");
modem_backend_mock_put(&bus_mock, cmux_frame_control_sabm_ack,
sizeof(cmux_frame_control_sabm_ack));
events = k_event_wait_all(&cmux_event, (EVENT_CMUX_CONNECTED), false, K_MSEC(100));
zassert_true((events & EVENT_CMUX_CONNECTED), "Failed to connect CMUX");
/* Wait for potential T1 timeout */
k_msleep(500);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == 0, "Received unexpected data");
/* Open DLCI1 */
zassert_true(modem_pipe_open_async(dlci1_pipe) == 0, "Failed to open DLCI1 pipe");
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(cmux_frame_dlci1_sabm_cmd),
"Incorrect number of bytes received for DLCI1 open cmd");
zassert_true(memcmp(buffer1, cmux_frame_dlci1_sabm_cmd,
sizeof(cmux_frame_dlci1_sabm_cmd)) == 0,
"Incorrect DLCI1 open cmd received");
modem_backend_mock_put(&bus_mock, cmux_frame_dlci1_sabm_ack,
sizeof(cmux_frame_dlci1_sabm_ack));
events = k_event_wait_all(&cmux_event, (EVENT_CMUX_DLCI1_OPEN),
false, K_MSEC(100));
zassert_true((events & EVENT_CMUX_DLCI1_OPEN),
"DLCI1 not opened as expected");
/* Wait for potential T1 timeout */
k_msleep(500);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == 0, "Received unexpected data");
/* Open DLCI2 */
zassert_true(modem_pipe_open_async(dlci2_pipe) == 0, "Failed to open DLCI2 pipe");
k_msleep(100);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == sizeof(cmux_frame_dlci2_sabm_cmd),
"Incorrect number of bytes received for DLCI1 open cmd");
zassert_true(memcmp(buffer1, cmux_frame_dlci2_sabm_cmd,
sizeof(cmux_frame_dlci2_sabm_cmd)) == 0,
"Incorrect DLCI1 open cmd received");
modem_backend_mock_put(&bus_mock, cmux_frame_dlci2_sabm_ack,
sizeof(cmux_frame_dlci2_sabm_ack));
events = k_event_wait_all(&cmux_event, (EVENT_CMUX_DLCI2_OPEN),
false, K_MSEC(100));
zassert_true((events & EVENT_CMUX_DLCI2_OPEN),
"DLCI1 not opened as expected");
/* Wait for potential T1 timeout */
k_msleep(500);
ret = modem_backend_mock_get(&bus_mock, buffer1, sizeof(buffer1));
zassert_true(ret == 0, "Received unexpected data");
}
ZTEST(modem_cmux, test_modem_cmux_disconnect_connect_sync)
{
modem_backend_mock_prime(&bus_mock, &transaction_dlci1_disc);
zassert_true(modem_pipe_close(dlci1_pipe) == 0, "Failed to close DLCI1");
modem_backend_mock_prime(&bus_mock, &transaction_dlci2_disc);
zassert_true(modem_pipe_close(dlci2_pipe) == 0, "Failed to close DLCI2");
modem_backend_mock_prime(&bus_mock, &transaction_control_cld);
zassert_true(modem_cmux_disconnect(&cmux) == 0, "Failed to disconnect CMUX");
zassert_true(modem_cmux_disconnect(&cmux) == -EALREADY,
"Should already be disconnected");
modem_backend_mock_prime(&bus_mock, &transaction_control_sabm);
zassert_true(modem_cmux_connect(&cmux) == 0, "Failed to connect CMUX");
zassert_true(modem_cmux_connect(&cmux) == -EALREADY,
"Should already be connected");
modem_backend_mock_prime(&bus_mock, &transaction_dlci1_sabm);
zassert_true(modem_pipe_open(dlci1_pipe) == 0, "Failed to open DLCI1 pipe");
modem_backend_mock_prime(&bus_mock, &transaction_dlci2_sabm);
zassert_true(modem_pipe_open(dlci2_pipe) == 0, "Failed to open DLCI2 pipe");
}
ZTEST(modem_cmux, test_modem_cmux_dlci_close_open_sync)
{
modem_backend_mock_prime(&bus_mock, &transaction_dlci1_disc);
zassert_true(modem_pipe_close(dlci1_pipe) == 0, "Failed to close DLCI1");
modem_backend_mock_prime(&bus_mock, &transaction_dlci2_disc);
zassert_true(modem_pipe_close(dlci2_pipe) == 0, "Failed to close DLCI2");
modem_backend_mock_prime(&bus_mock, &transaction_dlci1_sabm);
zassert_true(modem_pipe_open(dlci1_pipe) == 0, "Failed to open DLCI1 pipe");
modem_backend_mock_prime(&bus_mock, &transaction_dlci2_sabm);
zassert_true(modem_pipe_open(dlci2_pipe) == 0, "Failed to open DLCI2 pipe");
}
ZTEST_SUITE(modem_cmux, NULL, test_modem_cmux_setup, test_modem_cmux_before, NULL, NULL);

View file

@ -0,0 +1,8 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
tests:
modem.modem_cmux:
tags: modem_cmux
harness: ztest
platform_allow: native_posix

View file

@ -0,0 +1,9 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(modem_ppp_test)
target_sources(app PRIVATE src/main.c ../mock/modem_backend_mock.c)
include_directories(../mock)

View file

@ -0,0 +1,16 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
CONFIG_NO_OPTIMIZATIONS=y
CONFIG_NETWORKING=y
CONFIG_MODEM_MODULES=y
CONFIG_MODEM_PPP=y
CONFIG_NET_L2_PPP=y
CONFIG_ETH_NATIVE_POSIX=n
CONFIG_ZTEST=y
CONFIG_ZTEST_NEW_API=y
CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=4

View file

@ -0,0 +1,522 @@
/*
* Copyright (c) 2022 Trackunit Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/*************************************************************************************************/
/* Dependencies */
/*************************************************************************************************/
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_pkt.h>
#include "zephyr/net/net_l2.h"
#include "zephyr/net/ppp.h"
#include <zephyr/sys/crc.h>
#include <string.h>
#include <zephyr/modem/ppp.h>
#include <modem_backend_mock.h>
#define TEST_MODEM_PPP_BUF_SIZE (16)
#define TEST_MODEM_PPP_TX_PKT_BUF_SIZE (5)
#define TEST_MODEM_PPP_MOCK_PIPE_RX_BUF_SIZE (4096)
#define TEST_MODEM_PPP_MOCK_PIPE_TX_BUF_SIZE (4096)
#define TEST_MODEM_PPP_IP_FRAME_SEND_MULT_N (5)
#define TEST_MODEM_PPP_IP_FRAME_SEND_LARGE_N (2048)
#define TEST_MODEM_PPP_IP_FRAME_RECEIVE_LARGE_N (2048)
/*************************************************************************************************/
/* Mock pipe */
/*************************************************************************************************/
static struct modem_backend_mock mock;
static uint8_t mock_rx_buf[TEST_MODEM_PPP_MOCK_PIPE_RX_BUF_SIZE];
static uint8_t mock_tx_buf[TEST_MODEM_PPP_MOCK_PIPE_TX_BUF_SIZE];
static struct modem_pipe *mock_pipe;
/*************************************************************************************************/
/* PPP frames */
/*************************************************************************************************/
static uint8_t ppp_frame_wrapped[] = {0x7E, 0xFF, 0x7D, 0x23, 0xC0, 0x21, 0x7D, 0x21, 0x7D,
0x21, 0x7D, 0x20, 0x7D, 0x24, 0xD1, 0xB5, 0x7E};
static uint8_t ppp_frame_unwrapped[] = {0xC0, 0x21, 0x01, 0x01, 0x00, 0x04};
static uint8_t ip_frame_wrapped[] = {
0x7E, 0xFF, 0x7D, 0x23, 0x7D, 0x20, 0x21, 0x45, 0x7D, 0x20, 0x7D, 0x20, 0x29, 0x87, 0x6E,
0x40, 0x7D, 0x20, 0xE8, 0x7D, 0x31, 0xC1, 0xE9, 0x7D, 0x23, 0xFB, 0x7D, 0x25, 0x20, 0x7D,
0x2A, 0x2B, 0x36, 0x26, 0x25, 0x7D, 0x32, 0x8C, 0x3E, 0x7D, 0x20, 0x7D, 0x35, 0xBD, 0xF3,
0x2D, 0x7D, 0x20, 0x7D, 0x2B, 0x7D, 0x20, 0x7D, 0x27, 0x7D, 0x20, 0x7D, 0x24, 0x7D, 0x20,
0x7D, 0x24, 0x7D, 0x2A, 0x7D, 0x20, 0x7D, 0x2A, 0x7D, 0x20, 0xD4, 0x31, 0x7E};
static uint8_t ip_frame_unwrapped[] = {
0x45, 0x00, 0x00, 0x29, 0x87, 0x6E, 0x40, 0x00, 0xE8, 0x11, 0xC1, 0xE9, 0x03, 0xFB,
0x05, 0x20, 0x0A, 0x2B, 0x36, 0x26, 0x25, 0x12, 0x8C, 0x3E, 0x00, 0x15, 0xBD, 0xF3,
0x2D, 0x00, 0x0B, 0x00, 0x07, 0x00, 0x04, 0x00, 0x04, 0x0A, 0x00, 0x0A, 0x00};
static uint8_t ip_frame_unwrapped_with_protocol[] = {
0x00, 0x21, 0x45, 0x00, 0x00, 0x29, 0x87, 0x6E, 0x40, 0x00, 0xE8, 0x11, 0xC1, 0xE9, 0x03,
0xFB, 0x05, 0x20, 0x0A, 0x2B, 0x36, 0x26, 0x25, 0x12, 0x8C, 0x3E, 0x00, 0x15, 0xBD, 0xF3,
0x2D, 0x00, 0x0B, 0x00, 0x07, 0x00, 0x04, 0x00, 0x04, 0x0A, 0x00, 0x0A, 0x00};
static uint8_t corrupt_start_end_ppp_frame_wrapped[] = {0x2A, 0x46, 0x7E, 0x7E, 0xFF, 0x7D, 0x23,
0xC0, 0x21, 0x7D, 0x21, 0x7D, 0x21, 0x7D,
0x20, 0x7D, 0x24, 0xD1, 0xB5, 0x7E};
/*************************************************************************************************/
/* Buffers */
/*************************************************************************************************/
static struct net_pkt *received_packets[12];
static size_t received_packets_len;
static uint8_t buffer[4096];
static uint8_t unwrapped_buffer[4096];
static uint8_t wrapped_buffer[4096];
/*************************************************************************************************/
/* Mock network interface */
/*************************************************************************************************/
static uint8_t test_net_link_addr[] = {0x00, 0x00, 0x5E, 0x00, 0x53, 0x01};
static enum net_verdict test_net_l2_recv(struct net_if *iface, struct net_pkt *pkt)
{
/* Validate buffer not overflowing */
zassert_true(received_packets_len < ARRAY_SIZE(received_packets),
"Mock network interface receive buffer limit reached");
/* Store pointer to received packet */
received_packets[received_packets_len] = pkt;
received_packets_len++;
return NET_OK;
}
/* This emulates the layer two API */
static struct net_l2 test_net_l2 = {
.recv = test_net_l2_recv,
};
/* This emulates the network interface device which will receive unwrapped network packets */
static struct net_if_dev test_net_if_dev = {
.l2 = &test_net_l2,
.link_addr.addr = test_net_link_addr,
.link_addr.len = sizeof(test_net_link_addr),
.link_addr.type = NET_LINK_DUMMY,
.mtu = 1500,
.oper_state = NET_IF_OPER_UP,
};
/* This emulates the network interface which contains the network interface device */
static struct net_if test_iface = {
.if_dev = &test_net_if_dev,
};
/*************************************************************************************************/
/* Modem PPP */
/*************************************************************************************************/
/*
* The following initialization happens automatically when MODEM_PPP_DEFINE is used. However,
* since we are emulating the network interface, we can't use that macro, and have to initialize
* it manually here.
*/
static uint8_t ppp_receive_buf[TEST_MODEM_PPP_BUF_SIZE];
static uint8_t ppp_transmit_buf[TEST_MODEM_PPP_BUF_SIZE];
static struct modem_ppp ppp = {
.iface = &test_iface,
.receive_buf = ppp_receive_buf,
.transmit_buf = ppp_transmit_buf,
.buf_size = TEST_MODEM_PPP_BUF_SIZE,
};
/*************************************************************************************************/
/* Modem PPP net device */
/*************************************************************************************************/
extern const struct ppp_api modem_ppp_ppp_api;
static const struct device ppp_net_dev = {.data = &ppp};
static int test_net_send(struct net_pkt *pkt)
{
return modem_ppp_ppp_api.send(&ppp_net_dev, pkt);
}
/*************************************************************************************************/
/* Helpers */
/*************************************************************************************************/
static uint8_t test_modem_ppp_prng_random(bool reset)
{
static uint32_t prng_state = 1234;
if (reset == true) {
prng_state = 1234;
}
prng_state = (1103515245 * prng_state + 12345) % (1 << 31);
return (uint8_t)(prng_state & 0xFF);
}
static size_t test_modem_ppp_fill_net_pkt(struct net_pkt *pkt, size_t size)
{
test_modem_ppp_prng_random(true);
for (size_t i = 0; i < size; i++) {
if (net_pkt_write_u8(pkt, test_modem_ppp_prng_random(false)) < 0) {
return i;
}
}
return size;
}
static size_t test_modem_ppp_unwrap(uint8_t *unwrapped, const uint8_t *wrapped, size_t wrapped_size)
{
size_t wrapped_pos = 4;
size_t unwrapped_pos = 0;
while (wrapped_pos < (wrapped_size - 1)) {
/* Escape byte */
if (wrapped[wrapped_pos] == 0x7D) {
unwrapped[unwrapped_pos] = wrapped[wrapped_pos + 1] ^ 0x20;
wrapped_pos += 2;
unwrapped_pos += 1;
continue;
}
/* Normal byte */
unwrapped[unwrapped_pos] = wrapped[wrapped_pos];
wrapped_pos += 1;
unwrapped_pos += 1;
}
/* Remove FCS */
unwrapped_pos -= 2;
return unwrapped_pos;
}
static bool test_modem_ppp_validate_fill(const uint8_t *data, size_t size)
{
test_modem_ppp_prng_random(true);
for (size_t i = 0; i < size; i++) {
if (data[i] != test_modem_ppp_prng_random(false)) {
return false;
}
}
return true;
}
static void test_modem_ppp_generate_ppp_frame(uint8_t *frame, size_t size)
{
uint8_t byte;
uint16_t fcs;
test_modem_ppp_prng_random(true);
byte = 0x03;
fcs = crc16_ccitt(0xFFFF, &byte, 0x01);
frame[0] = 0x00;
frame[1] = 0x21;
for (size_t i = 2; i < (size - 2); i++) {
byte = test_modem_ppp_prng_random(false);
frame[i] = byte;
}
fcs = crc16_ccitt(fcs, frame, size) ^ 0xFFFF;
frame[size - 2] = fcs >> 8;
frame[size - 1] = fcs;
}
static size_t test_modem_ppp_wrap_ppp_frame(uint8_t *wrapped, const uint8_t *frame, size_t size)
{
size_t wrapped_pos = 4;
wrapped[0] = 0x7E;
wrapped[1] = 0xFF;
wrapped[2] = 0x7D;
wrapped[3] = 0x23;
for (size_t i = 0; i < size; i++) {
if ((frame[i] == 0x7E) || (frame[i] == 0x7D) || (frame[i] < 0x20)) {
wrapped[wrapped_pos] = 0x7D;
wrapped[wrapped_pos + 1] = frame[i] ^ 0x20;
wrapped_pos += 2;
continue;
}
wrapped[wrapped_pos] = frame[i];
wrapped_pos += 1;
}
wrapped[wrapped_pos] = 0x7E;
wrapped_pos += 1;
return wrapped_pos;
}
/*************************************************************************************************/
/* Test setup */
/*************************************************************************************************/
static void *test_modem_ppp_setup(void)
{
/*
* Manually run internal init function which would normally be performed by kernel as
* result of using the macro MODEM_PPP_DEFINE()
*/
zassert_true(modem_ppp_init_internal(&ppp_net_dev) == 0, "Failed to run internal init");
net_if_flag_set(modem_ppp_get_iface(&ppp), NET_IF_UP);
const struct modem_backend_mock_config mock_config = {
.rx_buf = mock_rx_buf,
.rx_buf_size = sizeof(mock_rx_buf),
.tx_buf = mock_tx_buf,
.tx_buf_size = sizeof(mock_tx_buf),
.limit = 8,
};
mock_pipe = modem_backend_mock_init(&mock, &mock_config);
zassert_true(modem_pipe_open(mock_pipe) == 0, "Failed to open mock pipe");
modem_ppp_attach(&ppp, mock_pipe);
return NULL;
}
static void test_modem_ppp_before(void *f)
{
/* Unreference packets */
for (size_t i = 0; i < received_packets_len; i++) {
net_pkt_unref(received_packets[i]);
}
/* Reset packets received buffer */
received_packets_len = 0;
/* Reset mock pipe */
modem_backend_mock_reset(&mock);
}
/*************************************************************************************************/
/* Tests */
/*************************************************************************************************/
ZTEST(modem_ppp, test_ppp_frame_receive)
{
struct net_pkt *pkt;
size_t pkt_len;
/* Put wrapped frame */
modem_backend_mock_put(&mock, ppp_frame_wrapped, sizeof(ppp_frame_wrapped));
/* Give modem ppp time to process received frame */
k_msleep(1000);
/* Validate frame received on mock network interface */
zassert_true(received_packets_len == 1, "Expected to receive one network packet");
pkt = received_packets[0];
pkt_len = net_pkt_get_len(pkt);
/* Validate length of received frame */
zassert_true(pkt_len == sizeof(ppp_frame_unwrapped), "Received net pkt data len incorrect");
/* Validate data of received frame */
net_pkt_cursor_init(pkt);
net_pkt_read(pkt, buffer, pkt_len);
zassert_true(memcmp(buffer, ppp_frame_unwrapped, pkt_len) == 0,
"Recevied net pkt data incorrect");
}
ZTEST(modem_ppp, test_corrupt_start_end_ppp_frame_receive)
{
struct net_pkt *pkt;
size_t pkt_len;
/* Put wrapped frame */
modem_backend_mock_put(&mock, corrupt_start_end_ppp_frame_wrapped,
sizeof(corrupt_start_end_ppp_frame_wrapped));
/* Give modem ppp time to process received frame */
k_msleep(1000);
/* Validate frame received on mock network interface */
zassert_true(received_packets_len == 1, "Expected to receive one network packet");
/* Validate length of received frame */
pkt = received_packets[0];
pkt_len = net_pkt_get_len(pkt);
zassert_true(pkt_len == sizeof(ppp_frame_unwrapped), "Received net pkt data len incorrect");
/* Validate data of received frame */
net_pkt_cursor_init(pkt);
net_pkt_read(pkt, buffer, pkt_len);
zassert_true(memcmp(buffer, ppp_frame_unwrapped, pkt_len) == 0,
"Recevied net pkt data incorrect");
}
ZTEST(modem_ppp, test_ppp_frame_send)
{
struct net_pkt *pkt;
int ret;
/* Allocate net pkt */
pkt = net_pkt_alloc_with_buffer(&test_iface, 256, AF_UNSPEC, 0, K_NO_WAIT);
zassert_true(pkt != NULL, "Failed to allocate network packet");
/* Set network packet data */
net_pkt_cursor_init(pkt);
ret = net_pkt_write(pkt, ppp_frame_unwrapped, sizeof(ppp_frame_unwrapped));
zassert_true(ret == 0, "Failed to write data to allocated network packet");
net_pkt_set_ppp(pkt, true);
/* Send network packet */
zassert_true(test_net_send(pkt) == 0, "Failed to send PPP pkt");
/* Give modem ppp time to wrap and send frame */
k_msleep(1000);
/* Get any sent data */
ret = modem_backend_mock_get(&mock, buffer, sizeof(buffer));
zassert_true(ret == sizeof(ppp_frame_wrapped), "Wrapped frame length incorrect");
zassert_true(memcmp(buffer, ppp_frame_wrapped, ret) == 0,
"Wrapped frame content is incorrect");
}
ZTEST(modem_ppp, test_ip_frame_receive)
{
struct net_pkt *pkt;
size_t pkt_len;
/* Put wrapped frame */
modem_backend_mock_put(&mock, ip_frame_wrapped, sizeof(ip_frame_wrapped));
/* Give modem ppp time to process received frame */
k_msleep(1000);
/* Validate frame received on mock network interface */
zassert_true(received_packets_len == 1, "Expected to receive one network packet");
pkt = received_packets[0];
pkt_len = net_pkt_get_len(pkt);
/* Validate length of received frame */
zassert_true(pkt_len == sizeof(ip_frame_unwrapped_with_protocol),
"Received net pkt data len incorrect");
/* Validate data of received frame */
net_pkt_cursor_init(pkt);
net_pkt_read(pkt, buffer, pkt_len);
zassert_true(memcmp(buffer, ip_frame_unwrapped_with_protocol, pkt_len) == 0,
"Recevied net pkt data incorrect");
}
ZTEST(modem_ppp, test_ip_frame_send)
{
struct net_pkt *pkt;
int ret;
/* Allocate net pkt */
pkt = net_pkt_alloc_with_buffer(&test_iface, 256, AF_UNSPEC, 0, K_NO_WAIT);
zassert_true(pkt != NULL, "Failed to allocate network packet");
/* Set network packet data */
net_pkt_cursor_init(pkt);
ret = net_pkt_write(pkt, ip_frame_unwrapped, sizeof(ip_frame_unwrapped));
zassert_true(ret == 0, "Failed to write data to allocated network packet");
net_pkt_set_family(pkt, AF_INET);
/* Send network packet */
test_net_send(pkt);
/* Give modem ppp time to wrap and send frame */
k_msleep(100);
/* Get any sent data */
ret = modem_backend_mock_get(&mock, buffer, sizeof(buffer));
zassert_true(ret == sizeof(ip_frame_wrapped), "Wrapped frame length incorrect");
zassert_true(memcmp(buffer, ip_frame_wrapped, ret) == 0,
"Wrapped frame content is incorrect");
}
ZTEST(modem_ppp, test_ip_frame_send_multiple)
{
struct net_pkt *pkts[TEST_MODEM_PPP_IP_FRAME_SEND_MULT_N];
int ret;
/* Allocate net pkts */
for (uint8_t i = 0; i < TEST_MODEM_PPP_IP_FRAME_SEND_MULT_N; i++) {
pkts[i] = net_pkt_alloc_with_buffer(&test_iface, 256, AF_UNSPEC, 0, K_NO_WAIT);
zassert_true(pkts[i] != NULL, "Failed to allocate network packet");
net_pkt_cursor_init(pkts[i]);
ret = net_pkt_write(pkts[i], ip_frame_unwrapped, sizeof(ip_frame_unwrapped));
zassert_true(ret == 0, "Failed to write data to allocated network packet");
net_pkt_set_family(pkts[i], AF_INET);
}
/* Send net pkts */
for (uint8_t i = 0; i < TEST_MODEM_PPP_IP_FRAME_SEND_MULT_N; i++) {
test_net_send(pkts[i]);
}
k_msleep(100);
ret = modem_backend_mock_get(&mock, buffer, TEST_MODEM_PPP_MOCK_PIPE_RX_BUF_SIZE);
zassert_true(ret == (sizeof(ip_frame_wrapped) * TEST_MODEM_PPP_IP_FRAME_SEND_MULT_N),
"Incorrect data amount received");
}
ZTEST(modem_ppp, test_ip_frame_send_large)
{
struct net_pkt *pkt;
size_t size;
int ret;
pkt = net_pkt_alloc_with_buffer(&test_iface, TEST_MODEM_PPP_IP_FRAME_SEND_LARGE_N,
AF_UNSPEC, 0, K_NO_WAIT);
net_pkt_cursor_init(pkt);
net_pkt_set_family(pkt, AF_INET);
size = test_modem_ppp_fill_net_pkt(pkt, TEST_MODEM_PPP_IP_FRAME_SEND_LARGE_N);
zassert_true(size == TEST_MODEM_PPP_IP_FRAME_SEND_LARGE_N, "Failed to fill net pkt");
test_net_send(pkt);
k_msleep(TEST_MODEM_PPP_IP_FRAME_SEND_LARGE_N * 2);
/* Data + protocol */
ret = modem_backend_mock_get(&mock, buffer, TEST_MODEM_PPP_MOCK_PIPE_RX_BUF_SIZE);
size = test_modem_ppp_unwrap(unwrapped_buffer, buffer, ret);
zassert_true(size == (TEST_MODEM_PPP_IP_FRAME_SEND_LARGE_N + 2),
"Incorrect data amount received");
/* Validate protocol */
zassert_true(unwrapped_buffer[0] == 0x00, "Incorrect protocol");
zassert_true(unwrapped_buffer[1] == 0x21, "Incorrect protocol");
/* Validate data */
zassert_true(test_modem_ppp_validate_fill(&unwrapped_buffer[2], (size - 2)) == true,
"Incorrect data received");
}
ZTEST(modem_ppp, test_ip_frame_receive_large)
{
struct net_pkt *pkt;
size_t size;
size_t pkt_len;
test_modem_ppp_generate_ppp_frame(buffer, TEST_MODEM_PPP_IP_FRAME_RECEIVE_LARGE_N);
size = test_modem_ppp_wrap_ppp_frame(wrapped_buffer, buffer,
TEST_MODEM_PPP_IP_FRAME_RECEIVE_LARGE_N);
zassert_true(size > TEST_MODEM_PPP_IP_FRAME_RECEIVE_LARGE_N, "Failed to wrap data");
modem_backend_mock_put(&mock, wrapped_buffer, size);
k_msleep(TEST_MODEM_PPP_IP_FRAME_RECEIVE_LARGE_N * 2);
zassert_true(received_packets_len == 1, "Expected to receive one network packet");
pkt = received_packets[0];
pkt_len = net_pkt_get_len(pkt);
/* FCS is removed from packet data */
zassert_true(pkt_len == (TEST_MODEM_PPP_IP_FRAME_RECEIVE_LARGE_N - 2),
"Incorrect length of net packet received");
}
ZTEST_SUITE(modem_ppp, NULL, test_modem_ppp_setup, test_modem_ppp_before, NULL, NULL);

View file

@ -0,0 +1,8 @@
# Copyright (c) 2023 Trackunit Corporation
# SPDX-License-Identifier: Apache-2.0
tests:
modem.modem_ppp:
tags: modem_ppp
harness: ztest
platform_allow: native_posix