test: lwm2m: Add tests for Block-Wise transfers
Block-Wise GET, PUT and SEND Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
This commit is contained in:
parent
01b57b4999
commit
a9e91af375
7 changed files with 257 additions and 7 deletions
|
@ -21,8 +21,11 @@ CONFIG_LWM2M_SHELL=y
|
|||
CONFIG_LWM2M_TICKLESS=y
|
||||
CONFIG_NET_SOCKETPAIR=y
|
||||
|
||||
#Enable Portfolio object
|
||||
#Enable test objects
|
||||
CONFIG_LWM2M_PORTFOLIO_OBJ_SUPPORT=y
|
||||
CONFIG_LWM2M_BINARYAPPDATA_OBJ_SUPPORT=y
|
||||
CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT=y
|
||||
CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT=y
|
||||
|
||||
#LwM2M v1.1 configure
|
||||
CONFIG_LWM2M_VERSION_1_1=y
|
||||
|
@ -36,7 +39,7 @@ CONFIG_LWM2M_RW_SENML_JSON_SUPPORT=y
|
|||
|
||||
#Enable SenML CBOR content format
|
||||
CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT=y
|
||||
CONFIG_LWM2M_RW_SENML_CBOR_RECORDS=40
|
||||
CONFIG_LWM2M_RW_SENML_CBOR_RECORDS=60
|
||||
CONFIG_ZCBOR_CANONICAL=y
|
||||
|
||||
#Enable legacy content formats
|
||||
|
@ -90,11 +93,11 @@ CONFIG_MBEDTLS_SSL_DTLS_CONNECTION_ID=y
|
|||
# MTU - IPv6 header - UDP header - DTLS header - CoAP header room
|
||||
# 1280 - 40 - 8 - 21 - 48
|
||||
CONFIG_LWM2M_COAP_MAX_MSG_SIZE=1163
|
||||
CONFIG_LWM2M_COAP_BLOCK_SIZE=1024
|
||||
CONFIG_LWM2M_COAP_BLOCK_SIZE=512
|
||||
CONFIG_LWM2M_COAP_BLOCK_TRANSFER=y
|
||||
CONFIG_LWM2M_COAP_ENCODE_BUFFER_SIZE=4096
|
||||
CONFIG_LWM2M_NUM_OUTPUT_BLOCK_CONTEXT=1
|
||||
CONFIG_LWM2M_NUM_BLOCK1_CONTEXT=1
|
||||
CONFIG_LWM2M_COAP_ENCODE_BUFFER_SIZE=8192
|
||||
CONFIG_LWM2M_NUM_OUTPUT_BLOCK_CONTEXT=2
|
||||
CONFIG_LWM2M_NUM_BLOCK1_CONTEXT=2
|
||||
CONFIG_SYS_HASH_FUNC32=y
|
||||
CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE=0
|
||||
CONFIG_LWM2M_ENGINE_MAX_PENDING=2
|
||||
|
|
|
@ -156,6 +156,8 @@ class Leshan:
|
|||
return 'integer'
|
||||
if isinstance(value, datetime):
|
||||
return 'time'
|
||||
if isinstance(value, bytes):
|
||||
return 'opaque'
|
||||
return 'string'
|
||||
|
||||
@classmethod
|
||||
|
@ -163,6 +165,8 @@ class Leshan:
|
|||
"""Wrapper for special types that are not understood by Json"""
|
||||
if isinstance(value, datetime):
|
||||
return int(value.timestamp())
|
||||
elif isinstance(value, bytes):
|
||||
return binascii.b2a_hex(value).decode()
|
||||
else:
|
||||
return value
|
||||
|
||||
|
|
133
tests/net/lib/lwm2m/interop/pytest/test_blockwise.py
Normal file
133
tests/net/lib/lwm2m/interop/pytest/test_blockwise.py
Normal file
|
@ -0,0 +1,133 @@
|
|||
"""
|
||||
Tests for Block-Wise transfers in LwM2M
|
||||
#######################################
|
||||
|
||||
Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
||||
"""
|
||||
|
||||
import time
|
||||
import logging
|
||||
import zlib
|
||||
import re
|
||||
import random
|
||||
import string
|
||||
import binascii
|
||||
from leshan import Leshan
|
||||
|
||||
from twister_harness import Shell
|
||||
from twister_harness import DeviceAdapter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def test_blockwise_1(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str):
|
||||
"""Blockwise test 1: Block-Wise PUT using OPAQUE content format"""
|
||||
|
||||
fw = b'1234567890' * 500
|
||||
fmt = leshan.format
|
||||
to = leshan.timeout
|
||||
leshan.format = 'OPAQUE'
|
||||
leshan.timeout = 600
|
||||
leshan.write(endpoint, '5/0/0', fw)
|
||||
# Our Firmware object prints out the CRC of the received firmware
|
||||
# when Update is executed
|
||||
leshan.execute(endpoint, '5/0/2')
|
||||
lines = dut.readlines_until(regex='app_fw_update: UPDATE', timeout=5.0)
|
||||
assert len(lines) > 0
|
||||
line = lines[-1]
|
||||
crc = int(re.search('CRC ([0-9]+)', line).group(1))
|
||||
# Verify that CRC matches
|
||||
assert crc == zlib.crc32(fw)
|
||||
leshan.format = fmt
|
||||
leshan.timeout = to
|
||||
|
||||
def test_blockwise_2(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str):
|
||||
"""Blockwise test 2: Block-Wise PUT with retry"""
|
||||
|
||||
fw = b'1234567890' * 500
|
||||
fmt = leshan.format
|
||||
to = leshan.timeout
|
||||
leshan.format = 'OPAQUE'
|
||||
# Set timeout to 1 second to force Leshan to stop sending
|
||||
leshan.timeout = 1
|
||||
try:
|
||||
leshan.write(endpoint, '5/0/0', fw)
|
||||
except Exception as e:
|
||||
logger.debug(f'Caught exception: {e}')
|
||||
shell.exec_command('lwm2m update')
|
||||
time.sleep(1)
|
||||
# Now send the firmware again using longer timeout
|
||||
leshan.timeout = 600
|
||||
leshan.write(endpoint, '5/0/0', fw)
|
||||
# Our Firmware object prints out the CRC of the received firmware
|
||||
# when Update is executed
|
||||
leshan.execute(endpoint, '5/0/2')
|
||||
lines = dut.readlines_until(regex='app_fw_update: UPDATE', timeout=5.0)
|
||||
assert len(lines) > 0
|
||||
line = lines[-1]
|
||||
crc = int(re.search('CRC ([0-9]+)', line).group(1))
|
||||
# Verify that CRC matches
|
||||
assert crc == zlib.crc32(fw)
|
||||
leshan.format = fmt
|
||||
leshan.timeout = to
|
||||
|
||||
|
||||
def test_blockwise_3(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str):
|
||||
"""Blockwise test 3: Block-Wise Get using TLV and SenML-CBOR content formats"""
|
||||
|
||||
shell.exec_command('lwm2m create /19/0')
|
||||
|
||||
# Generate 4 kB of binary app-data
|
||||
# and write it into BinaryAppData object
|
||||
data = ''.join(random.choice(string.ascii_letters) for i in range(4096)).encode()
|
||||
fmt = leshan.format
|
||||
to = leshan.timeout
|
||||
leshan.format = 'OPAQUE'
|
||||
leshan.timeout = 600
|
||||
leshan.write(endpoint, '19/0/0/0', data)
|
||||
|
||||
# Verify data is correctly transferred
|
||||
lines = shell.get_filtered_output(shell.exec_command('lwm2m read /19/0/0/0 -crc32'))
|
||||
crc = int(lines[0])
|
||||
assert crc == zlib.crc32(data)
|
||||
|
||||
# Try reading the data using different content formats
|
||||
for fmt in ['TLV', 'SENML_CBOR']:
|
||||
leshan.format = fmt
|
||||
data = leshan.read(endpoint, '19/0/0')
|
||||
data = binascii.a2b_hex(data[0][0])
|
||||
assert crc == zlib.crc32(data)
|
||||
|
||||
leshan.format = fmt
|
||||
leshan.timeout = to
|
||||
shell.exec_command('lwm2m delete /19/0')
|
||||
|
||||
def test_blockwise_4(shell: Shell, dut: DeviceAdapter, leshan: Leshan, endpoint: str):
|
||||
"""Blockwise test 4: Block-Wise SEND using SenML-CBOR content format"""
|
||||
|
||||
shell.exec_command('lwm2m create /19/0')
|
||||
# Generate 4 kB of binary app-data
|
||||
data = ''.join(random.choice(string.ascii_letters) for i in range(4096)).encode()
|
||||
fmt = leshan.format
|
||||
to = leshan.timeout
|
||||
leshan.format = 'OPAQUE'
|
||||
leshan.timeout = 600
|
||||
leshan.write(endpoint, '19/0/0/0', data)
|
||||
leshan.format = 'SENML_CBOR'
|
||||
|
||||
# Execute SEND and verify that correct data is received
|
||||
with leshan.get_event_stream(endpoint) as events:
|
||||
shell.exec_command('lwm2m send /19/0')
|
||||
dut.readlines_until(regex=r'.*SEND status: 0', timeout=5.0)
|
||||
send = events.next_event('SEND')
|
||||
assert send is not None
|
||||
content = binascii.a2b_hex(send[19][0][0][0])
|
||||
assert zlib.crc32(content) == zlib.crc32(data)
|
||||
|
||||
leshan.format = fmt
|
||||
leshan.timeout = to
|
||||
|
||||
shell.exec_command('lwm2m delete /19/0')
|
80
tests/net/lib/lwm2m/interop/src/firmware_update.c
Normal file
80
tests/net/lib/lwm2m/interop/src/firmware_update.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_MODULE_NAME app_fw_update
|
||||
#define LOG_LEVEL LOG_LEVEL_DBG
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
||||
|
||||
#include <zephyr/net/lwm2m.h>
|
||||
#include <zephyr/sys/crc.h>
|
||||
#include "lwm2m_engine.h"
|
||||
|
||||
static uint8_t firmware_buf[64];
|
||||
static uint32_t crc;
|
||||
|
||||
/* Array with supported PULL firmware update protocols */
|
||||
static uint8_t supported_protocol[1];
|
||||
|
||||
static int firmware_update_cb(uint16_t obj_inst_id,
|
||||
uint8_t *args, uint16_t args_len)
|
||||
{
|
||||
LOG_INF("UPDATE, (CRC %u)", crc);
|
||||
|
||||
lwm2m_set_u8(&LWM2M_OBJ(5, 0, 3), STATE_IDLE);
|
||||
lwm2m_set_u8(&LWM2M_OBJ(5, 0, 5), RESULT_SUCCESS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *firmware_get_buf(uint16_t obj_inst_id, uint16_t res_id,
|
||||
uint16_t res_inst_id, size_t *data_len)
|
||||
{
|
||||
*data_len = sizeof(firmware_buf);
|
||||
return firmware_buf;
|
||||
}
|
||||
|
||||
static int firmware_block_received_cb(uint16_t obj_inst_id, uint16_t res_id,
|
||||
uint16_t res_inst_id, uint8_t *data,
|
||||
uint16_t data_len, bool last_block,
|
||||
size_t total_size, size_t offset)
|
||||
{
|
||||
if (offset == 0) {
|
||||
crc = crc32_ieee(data, data_len);
|
||||
} else {
|
||||
crc = crc32_ieee_update(crc, data, data_len);
|
||||
}
|
||||
LOG_INF("FIRMWARE: BLOCK RECEIVED: offset:%zd len:%u last_block:%d crc: %u",
|
||||
offset, data_len, last_block, crc);
|
||||
|
||||
/* Add extra delay so short block-wise may timeout */
|
||||
k_sleep(K_MSEC(100));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int firmware_cancel_cb(const uint16_t obj_inst_id)
|
||||
{
|
||||
LOG_INF("FIRMWARE: Update canceled");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_firmware_update(void)
|
||||
{
|
||||
/* setup data buffer for block-wise transfer */
|
||||
lwm2m_register_pre_write_callback(&LWM2M_OBJ(5, 0, 0), firmware_get_buf);
|
||||
lwm2m_firmware_set_write_cb(firmware_block_received_cb);
|
||||
|
||||
/* register cancel callback */
|
||||
lwm2m_firmware_set_cancel_cb(firmware_cancel_cb);
|
||||
lwm2m_firmware_set_update_cb(firmware_update_cb);
|
||||
|
||||
lwm2m_create_res_inst(&LWM2M_OBJ(5, 0, 8, 0));
|
||||
lwm2m_set_res_buf(&LWM2M_OBJ(5, 0, 8, 0), &supported_protocol[0],
|
||||
sizeof(supported_protocol[0]),
|
||||
sizeof(supported_protocol[0]), 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
LWM2M_APP_INIT(init_firmware_update);
|
|
@ -88,6 +88,18 @@ int set_socketoptions(struct lwm2m_ctx *ctx)
|
|||
return lwm2m_set_default_sockopt(ctx);
|
||||
}
|
||||
|
||||
static int create_appdata(uint16_t obj_inst_id)
|
||||
{
|
||||
/* Create BinaryAppData object */
|
||||
static uint8_t data[4096];
|
||||
static char description[16];
|
||||
|
||||
lwm2m_set_res_buf(&LWM2M_OBJ(19, 0, 0, 0), data, sizeof(data), 0, 0);
|
||||
lwm2m_set_res_buf(&LWM2M_OBJ(19, 0, 3), description, sizeof(description), 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lwm2m_setup(void)
|
||||
{
|
||||
/* setup DEVICE object */
|
||||
|
@ -118,6 +130,8 @@ static int lwm2m_setup(void)
|
|||
lwm2m_create_res_inst(&LWM2M_OBJ(3, 0, 8, 1));
|
||||
lwm2m_set_res_buf(&LWM2M_OBJ(3, 0, 8, 1), &usb_ma, sizeof(usb_ma), sizeof(usb_ma), 0);
|
||||
|
||||
lwm2m_register_create_callback(19, create_appdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue