| /* |
| * Copyright (c) 2023 Gardena GmbH |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include "lwm2m_engine.h" |
| #include "lwm2m_object.h" |
| |
| #include <zephyr/ztest.h> |
| |
| /* Declaration of 'private' function */ |
| int prepare_msg_for_send(struct lwm2m_message *msg); |
| int build_msg_block_for_send(struct lwm2m_message *msg, uint16_t block_num); |
| int request_output_block_ctx(struct coap_block_context **ctx); |
| void release_output_block_ctx(struct coap_block_context ** const ctx); |
| |
| BUILD_ASSERT(IS_ENABLED(CONFIG_LWM2M_COAP_BLOCK_TRANSFER), |
| "These tests expect to have block transfer enabled."); |
| |
| #define EXPECTED_LWM2M_COAP_FULL_BUFFER_SIZE 256 |
| BUILD_ASSERT(CONFIG_LWM2M_COAP_ENCODE_BUFFER_SIZE == EXPECTED_LWM2M_COAP_FULL_BUFFER_SIZE, |
| "The expected max message size is wrong."); |
| |
| #define EXPECTED_NUM_OUTPUT_BLOCK_CONTEXT 3 |
| BUILD_ASSERT(NUM_OUTPUT_BLOCK_CONTEXT == EXPECTED_NUM_OUTPUT_BLOCK_CONTEXT, |
| "The expected number of output block contexts is wrong."); |
| |
| #define EXPECTED_DEFAULT_HEADER_OFFSET 4 |
| |
| struct net_block_transfer_fixture { |
| uint8_t dummy_msg[CONFIG_LWM2M_COAP_ENCODE_BUFFER_SIZE]; |
| struct lwm2m_ctx ctx; |
| struct lwm2m_message msg; |
| }; |
| |
| static void *net_block_transfer_setup(void) |
| { |
| static struct net_block_transfer_fixture f; |
| |
| for (uint_fast16_t i = 0; i < ARRAY_SIZE(f.dummy_msg); ++i) { |
| f.dummy_msg[i] = (uint8_t)i; |
| } |
| |
| return &f; |
| } |
| |
| static void net_block_transfer_before(void *f) |
| { |
| struct net_block_transfer_fixture *fixture = (struct net_block_transfer_fixture *)f; |
| |
| memset(&fixture->ctx, 0, sizeof(struct lwm2m_ctx)); |
| memset(&fixture->msg, 0, sizeof(struct lwm2m_message)); |
| fixture->msg.ctx = &fixture->ctx; |
| } |
| |
| static void net_block_transfer_after(void *f) |
| { |
| struct net_block_transfer_fixture *fixture = (struct net_block_transfer_fixture *)f; |
| |
| lwm2m_reset_message(&fixture->msg, true); |
| } |
| |
| ZTEST_F(net_block_transfer, test_init_message_use_big_buffer) |
| { |
| int ret; |
| struct lwm2m_message *msg = &fixture->msg; |
| |
| |
| ret = lwm2m_init_message(msg); |
| zassert_ok(ret, "Failed to initialize lwm2m message"); |
| |
| zassert_not_equal(msg->msg_data, msg->cpkt.data, |
| "Default data buffer should not be used for writing body"); |
| zassert_equal(msg->cpkt.data, msg->body_encode_buffer.data, |
| "Full body buffer should be in use"); |
| |
| zassert_equal(EXPECTED_LWM2M_COAP_FULL_BUFFER_SIZE, msg->cpkt.max_len, |
| "Max length for the package is wrong"); |
| |
| zassert_equal(EXPECTED_DEFAULT_HEADER_OFFSET, msg->cpkt.offset); |
| |
| /* write to buffer in a similar way as the writers */ |
| msg->out.out_cpkt = &msg->cpkt; |
| ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), fixture->dummy_msg, |
| EXPECTED_LWM2M_COAP_FULL_BUFFER_SIZE - EXPECTED_DEFAULT_HEADER_OFFSET); |
| zassert_ok(ret, "Should be able to write to buffer"); |
| zassert_equal(msg->cpkt.max_len, msg->cpkt.offset, "Buffer should be full"); |
| |
| const uint8_t one_byte = 0xAB; |
| |
| ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), &one_byte, 1); |
| zassert_equal(ret, -ENOMEM, "Should not be able to write to full buffer"); |
| } |
| |
| #define EXPECTED_HEADERS_LEN 7 |
| ZTEST_F(net_block_transfer, test_one_block_with_big_buffer) |
| { |
| int ret; |
| struct lwm2m_message *msg = &fixture->msg; |
| |
| /* Arrange */ |
| ret = lwm2m_init_message(msg); |
| zassert_equal(0, ret, "Failed to initialize lwm2m message"); |
| |
| zassert_not_equal(msg->msg_data, msg->cpkt.data, |
| "Big body data buffer should be used for writing body"); |
| zassert_equal(msg->cpkt.data, msg->body_encode_buffer.data, |
| "Full body buffer should be in use"); |
| |
| ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_CONTENT_FORMAT, |
| COAP_CONTENT_FORMAT_APP_LINK_FORMAT); |
| zassert_equal(0, ret, "Not able to append option"); |
| |
| ret = coap_packet_append_payload_marker(&msg->cpkt); |
| zassert_equal(0, ret, "Not able to append payload marker"); |
| |
| ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), fixture->dummy_msg, |
| CONFIG_LWM2M_COAP_BLOCK_SIZE); |
| zassert_ok(ret, "Should be able to write to buffer"); |
| |
| uint16_t payload_len; |
| |
| coap_packet_get_payload(&msg->cpkt, &payload_len); |
| zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, |
| "Block was not filled as expected"); |
| |
| /* Act */ |
| ret = prepare_msg_for_send(msg); |
| zassert_equal(0, ret, "Preparing message for sending failed"); |
| |
| /* Assert */ |
| zassert_equal(msg->msg_data, msg->cpkt.data, |
| "Default data buffer should be used for sending the block"); |
| zassert_is_null(msg->body_encode_buffer.data, "Complete body buffer should not be set"); |
| |
| const uint8_t *payload = coap_packet_get_payload(&msg->cpkt, &payload_len); |
| |
| zassert_not_null(payload, "Payload expected"); |
| zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, |
| "Block was not filled as expected"); |
| |
| zassert_equal(EXPECTED_HEADERS_LEN, msg->cpkt.hdr_len + msg->cpkt.opt_len + 1, |
| "Headers length not as expected"); |
| /* payload should start after headers, options and payload marker (size: 1) */ |
| zassert_equal(payload, msg->cpkt.data + EXPECTED_HEADERS_LEN, |
| "Payload not starting at expected address"); |
| |
| const uint8_t expected_headers[EXPECTED_HEADERS_LEN] = {0x40, 0, 0, 0, 0xc1, 0x28, 0xff}; |
| |
| zassert_mem_equal(msg->cpkt.data, expected_headers, EXPECTED_HEADERS_LEN, |
| "Payload not starting at expected address"); |
| |
| /* check payload */ |
| for (uint_fast8_t i = 0; i < payload_len; ++i) { |
| zassert_equal(payload[i], i, "Byte %i in payload is wrong", i); |
| } |
| |
| /* check first byte after payload */ |
| zassert_equal(payload[payload_len], 0x00, "Byte after payload is wrong"); |
| } |
| |
| ZTEST_F(net_block_transfer, test_build_first_block_for_send) |
| { |
| int ret; |
| struct lwm2m_message *msg = &fixture->msg; |
| uint16_t payload_len; |
| |
| /* Arrange */ |
| msg->code = COAP_METHOD_GET; |
| ret = lwm2m_init_message(msg); |
| zassert_equal(0, ret, "Failed to initialize lwm2m message"); |
| |
| ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_CONTENT_FORMAT, |
| COAP_CONTENT_FORMAT_APP_LINK_FORMAT); |
| zassert_equal(0, ret, "Not able to append option"); |
| |
| ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_ACCEPT, COAP_CONTENT_FORMAT_APP_JSON); |
| zassert_equal(0, ret, "Not able to append option"); |
| |
| const uint8_t expected_header_len = 4; |
| |
| zassert_equal(msg->cpkt.hdr_len, expected_header_len, "Header length not as expected"); |
| |
| const uint8_t expected_options_len = 4; |
| |
| zassert_equal(msg->cpkt.opt_len, expected_options_len, "Options length not as expected"); |
| |
| ret = coap_packet_append_payload_marker(&msg->cpkt); |
| zassert_equal(0, ret, "Not able to append payload marker"); |
| |
| ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), fixture->dummy_msg, |
| 2 * CONFIG_LWM2M_COAP_BLOCK_SIZE); |
| zassert_ok(ret, "Should be able to write to buffer"); |
| |
| zassert_not_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not yet in use"); |
| |
| /* Act */ |
| ret = prepare_msg_for_send(msg); |
| zassert_equal(ret, 0, "Could not create first block"); |
| |
| /* Assert */ |
| zassert_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not in use"); |
| |
| ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1); |
| zassert(ret > 0, "block 1 option not set"); |
| |
| const uint8_t *payload = coap_packet_get_payload(&msg->cpkt, &payload_len); |
| |
| zassert_not_null(payload, "Payload expected"); |
| zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, "Wrong payload size"); |
| zassert_equal(0x00, payload[0], "First byte in payload wrong"); |
| zassert_equal(0x3f, payload[CONFIG_LWM2M_COAP_BLOCK_SIZE - 1], |
| "Last byte in payload wrong"); |
| } |
| |
| ZTEST_F(net_block_transfer, test_build_blocks_for_send_exactly_2_blocks) |
| { |
| int ret; |
| struct lwm2m_message *msg = &fixture->msg; |
| uint16_t payload_len; |
| const uint8_t *payload; |
| |
| /* Arrange */ |
| msg->code = COAP_METHOD_PUT; |
| ret = lwm2m_init_message(msg); |
| zassert_equal(0, ret, "Failed to initialize lwm2m message"); |
| |
| const uint8_t *query = "query"; |
| |
| ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_QUERY, query, strlen(query)); |
| zassert_equal(0, ret, "Not able to append option"); |
| |
| ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_ACCEPT, |
| COAP_CONTENT_FORMAT_TEXT_PLAIN); |
| zassert_equal(0, ret, "Not able to append option"); |
| |
| const uint8_t expected_header_len = 4; |
| |
| zassert_equal(msg->cpkt.hdr_len, expected_header_len, "Header length not as expected"); |
| |
| const uint8_t expected_options_len = 8; |
| |
| zassert_equal(msg->cpkt.opt_len, expected_options_len, "Options length not as expected"); |
| |
| ret = coap_packet_append_payload_marker(&msg->cpkt); |
| zassert_equal(0, ret, "Not able to append payload marker"); |
| |
| ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), fixture->dummy_msg, |
| 2 * CONFIG_LWM2M_COAP_BLOCK_SIZE); |
| zassert_ok(ret, "Should be able to write to buffer"); |
| |
| zassert_not_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not yet in use"); |
| |
| /* block 0 */ |
| ret = prepare_msg_for_send(msg); |
| zassert_equal(ret, 0, "Could not create first block"); |
| |
| zassert_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not in use"); |
| |
| ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1); |
| zassert(ret > 0, "block 1 option not set"); |
| |
| payload = coap_packet_get_payload(&msg->cpkt, &payload_len); |
| |
| zassert_not_null(payload, "Payload expected"); |
| zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, "Wrong payload size"); |
| zassert_equal(0x00, payload[0], "First byte in payload wrong"); |
| zassert_equal(0x3f, payload[CONFIG_LWM2M_COAP_BLOCK_SIZE - 1], |
| "Last byte in payload wrong"); |
| |
| /* block 1 */ |
| ret = build_msg_block_for_send(msg, 1); |
| zassert_equal(ret, 0, "Could not create second block"); |
| |
| ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1); |
| zassert(ret > 0, "block 1 option not set"); |
| |
| payload = coap_packet_get_payload(&msg->cpkt, &payload_len); |
| |
| zassert_not_null(payload, "Payload expected"); |
| zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, "Wrong payload size"); |
| zassert_equal(0x40, payload[0], "First byte in payload wrong"); |
| zassert_equal(0x7f, payload[CONFIG_LWM2M_COAP_BLOCK_SIZE - 1], |
| "Last byte in payload wrong"); |
| |
| /* block 2 doesn't exist */ |
| ret = build_msg_block_for_send(msg, 2); |
| zassert_equal(ret, -EINVAL, "Could not create second block"); |
| } |
| |
| ZTEST_F(net_block_transfer, test_build_blocks_for_send_more_than_2_blocks) |
| { |
| int ret; |
| struct lwm2m_message *msg = &fixture->msg; |
| uint16_t payload_len; |
| const uint8_t *payload; |
| |
| /* Arrange */ |
| msg->code = COAP_METHOD_DELETE; |
| ret = lwm2m_init_message(msg); |
| zassert_equal(0, ret, "Failed to initialize lwm2m message"); |
| |
| const uint8_t *proxy_scheme = "coap"; |
| |
| ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_PROXY_SCHEME, proxy_scheme, |
| strlen(proxy_scheme)); |
| zassert_equal(0, ret, "Not able to append option"); |
| |
| ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_CONTENT_FORMAT, |
| COAP_CONTENT_FORMAT_APP_JSON); |
| zassert_equal(0, ret, "Not able to append option"); |
| |
| const uint8_t expected_header_len = 4; |
| |
| zassert_equal(msg->cpkt.hdr_len, expected_header_len, "Header length not as expected"); |
| |
| const uint8_t expected_options_len = 8; |
| |
| zassert_equal(msg->cpkt.opt_len, expected_options_len, "Options length not as expected"); |
| |
| ret = coap_packet_append_payload_marker(&msg->cpkt); |
| zassert_equal(0, ret, "Not able to append payload marker"); |
| |
| ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), fixture->dummy_msg, |
| 2 * CONFIG_LWM2M_COAP_BLOCK_SIZE + 1); |
| zassert_ok(ret, "Should be able to write to buffer"); |
| |
| zassert_not_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not yet in use"); |
| |
| /* block 0 */ |
| ret = prepare_msg_for_send(msg); |
| zassert_equal(ret, 0, "Could not create first block"); |
| |
| zassert_equal(msg->msg_data, msg->cpkt.data, "Buffer for block data is not in use"); |
| |
| ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1); |
| zassert(ret > 0, "block 1 option not set"); |
| |
| payload = coap_packet_get_payload(&msg->cpkt, &payload_len); |
| |
| zassert_not_null(payload, "Payload expected"); |
| zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, "Wrong payload size"); |
| zassert_equal(0x00, payload[0], "First byte in payload wrong"); |
| zassert_equal(0x3f, payload[CONFIG_LWM2M_COAP_BLOCK_SIZE - 1], |
| "Last byte in payload wrong"); |
| |
| /* block 1 */ |
| ret = build_msg_block_for_send(msg, 1); |
| zassert_equal(ret, 0, "Could not create second block"); |
| |
| ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1); |
| zassert(ret > 0, "block 1 option not set"); |
| |
| payload = coap_packet_get_payload(&msg->cpkt, &payload_len); |
| |
| zassert_not_null(payload, "Payload expected"); |
| zassert_equal(payload_len, CONFIG_LWM2M_COAP_BLOCK_SIZE, "Wrong payload size"); |
| zassert_equal(0x40, payload[0], "First byte in payload wrong"); |
| zassert_equal(0x7f, payload[CONFIG_LWM2M_COAP_BLOCK_SIZE - 1], |
| "Last byte in payload wrong"); |
| |
| /* block 2 */ |
| ret = build_msg_block_for_send(msg, 2); |
| zassert_equal(ret, 0, "Could not create second block"); |
| |
| ret = coap_get_option_int(&msg->cpkt, COAP_OPTION_BLOCK1); |
| zassert(ret > 0, "block 1 option not set"); |
| |
| payload = coap_packet_get_payload(&msg->cpkt, &payload_len); |
| |
| zassert_not_null(payload, "Payload expected"); |
| zassert_equal(payload_len, 1, "Wrong payload size"); |
| zassert_equal(0x80, payload[0], "First (and only) byte in payload wrong"); |
| |
| /* block 3 doesn't exist */ |
| ret = build_msg_block_for_send(msg, 3); |
| zassert_equal(ret, -EINVAL, "Could not create second block"); |
| } |
| |
| ZTEST_F(net_block_transfer, test_block_context) |
| { |
| struct coap_block_context *ctx0, *ctx1, *ctx2, *ctx3, *ctx4; |
| int ret; |
| |
| zassert_equal(NUM_OUTPUT_BLOCK_CONTEXT, 3); |
| |
| /* block context 0 */ |
| ret = request_output_block_ctx(&ctx0); |
| zassert_ok(ret); |
| zassert_not_null(ctx0); |
| /* block context 1 */ |
| ret = request_output_block_ctx(&ctx1); |
| zassert_ok(ret); |
| zassert_not_null(ctx1); |
| /* block context 2 */ |
| ret = request_output_block_ctx(&ctx2); |
| zassert_ok(ret); |
| zassert_not_null(ctx2); |
| |
| /* Get one context more than available */ |
| ret = request_output_block_ctx(&ctx3); |
| zassert_equal(ret, -ENOMEM); |
| zassert_is_null(ctx3); |
| |
| /* release one block context */ |
| release_output_block_ctx(&ctx2); |
| zassert_is_null(ctx2); |
| |
| /* get another block context */ |
| ret = request_output_block_ctx(&ctx4); |
| zassert_ok(ret); |
| zassert_not_null(ctx4); |
| |
| /* release all block contexts */ |
| release_output_block_ctx(&ctx0); |
| zassert_is_null(ctx0); |
| release_output_block_ctx(&ctx1); |
| zassert_is_null(ctx1); |
| release_output_block_ctx(&ctx2); |
| zassert_is_null(ctx2); |
| release_output_block_ctx(&ctx3); |
| zassert_is_null(ctx3); |
| release_output_block_ctx(&ctx4); |
| zassert_is_null(ctx4); |
| } |
| |
| ZTEST_SUITE(net_block_transfer, NULL, net_block_transfer_setup, net_block_transfer_before, |
| net_block_transfer_after, NULL); |