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:
Seppo Takalo 2024-05-10 17:34:48 +03:00 committed by Alberto Escolar
commit a9e91af375
7 changed files with 257 additions and 7 deletions

View file

@ -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

View file

@ -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

View 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')

View 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);

View file

@ -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;
}