obex: add stream-based obex parser and tests

This commit is contained in:
Matthias Ringwald 2021-11-25 17:16:29 +01:00
parent eea7024143
commit ac9a0d84d5
5 changed files with 575 additions and 3 deletions

244
src/classic/obex_parser.c Normal file
View File

@ -0,0 +1,244 @@
/*
* Copyright (C) 2021 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#define BTSTACK_FILE__ "obex_parser.c"
#include "btstack_config.h"
#include "classic/obex.h"
#include "classic/obex_parser.h"
#include <string.h>
#include "btstack_debug.h"
#include "btstack_util.h"
static uint16_t obex_parser_param_size_for_request(uint8_t opcode){
switch (opcode) {
case OBEX_OPCODE_SETPATH:
return 4;
case OBEX_OPCODE_CONNECT:
return 6;
default:
return 2;
}
}
static uint16_t obex_parser_param_size_for_response(uint8_t opcode){
switch (opcode) {
case OBEX_OPCODE_CONNECT:
return 6;
default:
return 2;
}
}
static void obex_parser_init(obex_parser_t * obex_parser, obex_parser_callback_t obex_parser_callback, void * user_data){
memset(obex_parser, 0, sizeof(obex_parser_t));
obex_parser->packet_size = 3;
obex_parser->user_data = user_data;
obex_parser->callback = obex_parser_callback;
}
void obex_parser_init_for_request(obex_parser_t * obex_parser, obex_parser_callback_t obex_parser_callback, void * user_data){
obex_parser_init(obex_parser, obex_parser_callback, user_data);
obex_parser->state = OBEX_PARSER_STATE_W4_OPCODE;
}
void obex_parser_init_for_response(obex_parser_t * obex_parser, uint8_t opcode, obex_parser_callback_t obex_parser_callback, void * user_data){
obex_parser_init(obex_parser, obex_parser_callback, user_data);
obex_parser->state = OBEX_PARSER_STATE_W4_RESPONSE_CODE;
obex_parser->opcode = opcode;
}
obex_parser_object_state_t obex_parser_process_data(obex_parser_t *obex_parser, const uint8_t *data_buffer, uint16_t data_len) {
while (data_len){
uint16_t bytes_to_consume = 1;
uint8_t header_type;
switch (obex_parser->state){
case OBEX_PARSER_STATE_W4_OPCODE:
obex_parser->opcode = *data_buffer;
obex_parser->state = OBEX_PARSER_STATE_W4_PACKET_LEN;
obex_parser->item_len = obex_parser_param_size_for_request(obex_parser->opcode);
obex_parser->packet_size = 1 + obex_parser->item_len;
break;
case OBEX_PARSER_STATE_W4_RESPONSE_CODE:
obex_parser->response_code = *data_buffer;
obex_parser->state = OBEX_PARSER_STATE_W4_PACKET_LEN;
obex_parser->item_len = obex_parser_param_size_for_response(obex_parser->opcode);
obex_parser->packet_size = 1 + obex_parser->item_len;
break;
case OBEX_PARSER_STATE_W4_PACKET_LEN:
bytes_to_consume = btstack_min(2 - obex_parser->item_pos, data_len);
memcpy(&obex_parser->params[obex_parser->item_pos], data_buffer, bytes_to_consume);
obex_parser->item_pos += bytes_to_consume;
if (obex_parser->item_pos == 2){
// validate packet large enough for header
obex_parser->packet_size = big_endian_read_16(obex_parser->params, 0);
if (obex_parser->packet_size < (obex_parser->item_len + 1)){
// packet size smaller than opcode + params
obex_parser->state = OBEX_PARSER_STATE_INVALID;
break;
}
// params already complete?
if (obex_parser->item_len == 2){
obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
} else {
obex_parser->state = OBEX_PARSER_STATE_W4_PARAMS;
}
}
break;
case OBEX_PARSER_STATE_W4_PARAMS:
bytes_to_consume = btstack_min(obex_parser->item_len - obex_parser->item_pos, data_len);
memcpy(&obex_parser->params[obex_parser->item_pos], data_buffer, bytes_to_consume);
obex_parser->item_pos += bytes_to_consume;
if (obex_parser->item_pos == obex_parser->item_len){
obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
}
break;
case OBEX_PARSER_STATE_W4_HEADER_ID:
obex_parser->header_id = *data_buffer; // constants in obex.h encode type as well, so just use these as well
header_type = *data_buffer >> 6;
obex_parser->item_pos = 0;
switch (header_type){
case 0:
case 1:
// 16-bit length info prefixed
obex_parser->item_len = 2;
obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_LEN_FIRST;
break;
case 2:
// 8-bit value
obex_parser->item_len = 1;
obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_VALUE;
break;
case 3:
// 32-bit value
obex_parser->item_len = 4;
obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_VALUE;
break;
default:
// avoid compiler warning about unused cases (encoding in [0..3])
break;
}
break;
case OBEX_PARSER_STATE_W4_HEADER_LEN_FIRST:
obex_parser->item_len = *data_buffer << 8;
obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_LEN_SECOND;
break;
case OBEX_PARSER_STATE_W4_HEADER_LEN_SECOND:
obex_parser->item_len = obex_parser->item_len + *data_buffer - 3;
if ( obex_parser->item_len > 0){
obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_VALUE;
} else {
obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
}
break;
case OBEX_PARSER_STATE_W4_HEADER_VALUE:
bytes_to_consume = btstack_min(obex_parser->item_len - obex_parser->item_pos, data_len);
if (*obex_parser->callback != NULL){
(*obex_parser->callback)(obex_parser->user_data, obex_parser->header_id, obex_parser->item_len, obex_parser->item_pos, data_buffer, bytes_to_consume);
}
obex_parser->item_pos += bytes_to_consume;
if (obex_parser->item_pos == obex_parser->item_len){
obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
}
break;
case OBEX_PARSER_STATE_COMPLETE:
obex_parser->state = OBEX_PARSER_STATE_OVERRUN;
break;
case OBEX_PARSER_STATE_INVALID:
break;
default:
btstack_unreachable();
break;
}
data_buffer += bytes_to_consume;
data_len -= bytes_to_consume;
obex_parser->packet_pos += bytes_to_consume;
// all bytes read? then check state
if (obex_parser->packet_pos == obex_parser->packet_size){
if (obex_parser->state == OBEX_PARSER_STATE_W4_HEADER_ID){
obex_parser->state = OBEX_PARSER_STATE_COMPLETE;
} else {
obex_parser->state = OBEX_PARSER_STATE_INVALID;
}
}
}
switch (obex_parser->state){
case OBEX_PARSER_STATE_COMPLETE:
return OBEX_PARSER_OBJECT_STATE_COMPLETE;
case OBEX_PARSER_STATE_OVERRUN:
return OBEX_PARSER_OBJECT_STATE_OVERRUN;
case OBEX_PARSER_STATE_INVALID:
return OBEX_PARSER_OBJECT_STATE_INVALID;
default:
return OBEX_PARSER_OBJECT_STATE_INCOMPLETE;
}
}
void obex_parser_get_operation_info(obex_parser_t * obex_parser, obex_parser_operation_info_t * obex_operation_info){
memset(obex_operation_info, 0, sizeof(obex_parser_operation_info_t));
obex_operation_info->opcode = obex_parser->opcode;
obex_operation_info->response_code = obex_parser->response_code;
switch (obex_parser->opcode){
case OBEX_OPCODE_CONNECT:
obex_operation_info->obex_version_number = obex_parser->params[2];
obex_operation_info->flags = obex_parser->params[3];
obex_operation_info->max_packet_length = big_endian_read_16(obex_parser->params, 4);
break;
case OBEX_OPCODE_SETPATH:
obex_operation_info->flags = obex_parser->params[2];
break;
default:
break;
}
}
obex_parser_header_state_t obex_parser_header_store(uint8_t * header_buffer, uint16_t buffer_size, uint16_t total_len,
uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len){
uint16_t bytes_to_store = btstack_min(buffer_size - data_offset, data_len);
memcpy(&header_buffer[data_offset], data_buffer, bytes_to_store);
uint16_t new_offset = data_offset + bytes_to_store;
if (new_offset > buffer_size){
return OBEX_PARSER_HEADER_OVERRUN;
} else if (new_offset == total_len) {
return OBEX_PARSER_HEADER_COMPLETE;
} else {
return OBEX_PARSER_HEADER_INCOMPLETE;
}
}

168
src/classic/obex_parser.h Normal file
View File

@ -0,0 +1,168 @@
/*
* Copyright (C) 2021 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
/**
* OBEX Parser
* Parser incoming arbitrarily chunked OBEX object
*/
#ifndef OBEX_PARSER_H
#define OBEX_PARSER_H
#if defined __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef enum {
OBEX_PARSER_STATE_W4_OPCODE,
OBEX_PARSER_STATE_W4_RESPONSE_CODE,
OBEX_PARSER_STATE_W4_PACKET_LEN,
OBEX_PARSER_STATE_W4_PARAMS,
OBEX_PARSER_STATE_W4_HEADER_ID,
OBEX_PARSER_STATE_W4_HEADER_LEN_FIRST,
OBEX_PARSER_STATE_W4_HEADER_LEN_SECOND,
OBEX_PARSER_STATE_W4_HEADER_VALUE,
OBEX_PARSER_STATE_COMPLETE,
OBEX_PARSER_STATE_OVERRUN,
OBEX_PARSER_STATE_INVALID,
} obex_parser_state_t;
/* API_START */
/**
* Callback to process chunked data
* @param user_data provided in obex_parser_init
* @param header_id current OBEX header ID
* @param total_len of header
* @param data_offset
* @param data_len
* @param data_buffer
*/
typedef void (*obex_parser_callback_t)(void * user_data, uint8_t header_id, uint16_t total_len, uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len);
typedef enum {
OBEX_PARSER_OBJECT_STATE_INCOMPLETE,
OBEX_PARSER_OBJECT_STATE_COMPLETE,
OBEX_PARSER_OBJECT_STATE_OVERRUN,
OBEX_PARSER_OBJECT_STATE_INVALID,
} obex_parser_object_state_t;
typedef enum {
OBEX_PARSER_HEADER_INCOMPLETE,
OBEX_PARSER_HEADER_COMPLETE,
OBEX_PARSER_HEADER_OVERRUN,
} obex_parser_header_state_t;
typedef struct {
uint8_t opcode;
uint8_t response_code;
// Connect only
uint8_t obex_version_number;
uint16_t max_packet_length;
// Connect + Set Path only
uint8_t flags;
} obex_parser_operation_info_t;
typedef struct {
obex_parser_callback_t callback;
void * user_data;
uint16_t packet_size;
uint16_t packet_pos;
obex_parser_state_t state;
uint8_t opcode;
uint8_t response_code;
uint16_t item_len;
uint16_t item_pos;
uint8_t params[6]; // for connect and set path
uint8_t header_id;
} obex_parser_t;
/**
* Initialize OBEX Parser for next OBEX request
* @param obex_parser
* @param function to call for fields that are not registered
* @param user_data provided to callback function
*/
void obex_parser_init_for_request(obex_parser_t * obex_parser, obex_parser_callback_t obex_parser_callback, void * user_data);
/**
* Initialize OBEX Parser for next OBEX response
* @param obex_parser
* @param opcode of request - needed as responses with additional fields like connect and set path
* @param function to call for fields that are not registered
* @param user_data provided to callback function
*/
void obex_parser_init_for_response(obex_parser_t * obex_parser, uint8_t opcode, obex_parser_callback_t obex_parser_callback, void * user_data);
/**
* Process OBEX data
* @param obex_parser
* @param data_len
* @param data_buffer
* @return OBEX_PARSER_OBJECT_STATE_COMPLETE if packet has been completely parsed
*/
obex_parser_object_state_t obex_parser_process_data(obex_parser_t *obex_parser, const uint8_t *data_buffer, uint16_t data_len);
/**
* Get operation info for request/response packets
* @param obex_parser
* @return
*/
void obex_parser_get_operation_info(obex_parser_t * obex_parser, obex_parser_operation_info_t * obex_operation_info);
/**
* Helper to collect header chunks in fixed-size header buffer
* @param header_buffer
* @param buffer_size of header_buffer
* @param total_len of header value
* @param data_offset of chunkc to store
* @param data_buffer
* @param data_len chunk length
* @return OBEX_PARSER_HEADER_COMPLETE when header value complete
*/
obex_parser_header_state_t obex_parser_header_store(uint8_t * header_buffer, uint16_t buffer_size, uint16_t total_len,
uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len);
/* API_END */
#if defined __cplusplus
}
#endif
#endif

View File

@ -18,8 +18,8 @@ VPATH += ${BTSTACK_ROOT}/platform/posix
COMMON = \
btstack_util.c \
hci_dump.c \
obex_message_builder.c \
obex_parser.c
CFLAGS_COVERAGE = ${CFLAGS} -fprofile-arcs -ftest-coverage
CFLAGS_ASAN = ${CFLAGS} -fsanitize=address -DHAVE_ASSERT
@ -31,7 +31,8 @@ LDFLAGS_ASAN = ${LDFLAGS} -fsanitize=address
COMMON_OBJ_COVERAGE = $(addprefix build-coverage/,$(COMMON:.c=.o))
COMMON_OBJ_ASAN = $(addprefix build-asan/, $(COMMON:.c=.o))
all: build-coverage/obex_message_builder_test build-asan/obex_message_builder_test
all: build-coverage/obex_message_builder_test build-asan/obex_message_builder_test \
build-coverage/obex_parser_test build-asan/obex_parser_test
build-%:
mkdir -p $@
@ -42,6 +43,12 @@ build-coverage/%.o: %.c | build-coverage
build-asan/%.o: %.c | build-asan
${CC} -c $(CFLAGS_ASAN) $< -o $@
build-coverage/%.cpp.o: %.cpp | build-coverage
${CC} -c $(CFLAGS_COVERAGE) $< -o $@
build-asan/%.cpp.o: %.cpp | build-asan
${CC} -c $(CFLAGS_ASAN) $< -o $@
build-coverage/obex_message_builder_test: ${COMMON_OBJ_COVERAGE} build-coverage/obex_message_builder_test.o | build-coverage
${CC} $^ ${LDFLAGS_COVERAGE} -o $@
@ -49,12 +56,20 @@ build-coverage/obex_message_builder_test: ${COMMON_OBJ_COVERAGE} build-coverage/
build-asan/obex_message_builder_test: ${COMMON_OBJ_ASAN} build-asan/obex_message_builder_test.o | build-asan
${CC} $^ ${LDFLAGS_ASAN} -o $@
build-coverage/obex_parser_test: ${COMMON_OBJ_COVERAGE} build-coverage/obex_parser_test.cpp.o | build-coverage
${CC} $^ ${LDFLAGS_COVERAGE} -o $@
build-asan/obex_parser_test: ${COMMON_OBJ_ASAN} build-asan/obex_parser_test.cpp.o | build-asan
${CC} $^ ${LDFLAGS_ASAN} -o $@
test: all
build-asan/obex_message_builder_test
build-asan/obex_parser_test
coverage: all
rm -f build-coverage/*.gcda
build-coverage/obex_message_builder_test
build-coverage/obex_parser_test
clean:
rm -rf build-coverage build-asan

View File

@ -6,6 +6,9 @@
#include "classic/obex.h"
#include "classic/obex_message_builder.h"
// mock hci_dump.c
extern "C" void hci_dump_log(int log_level, const char * format, ...){}
static const uint8_t service_uuid[] = {0xbb, 0x58, 0x2b, 0x40, 0x42, 0xc, 0x11, 0xdb, 0xb0, 0xde, 0x8, 0x0, 0x20, 0xc, 0x9a, 0x66};
static const uint8_t application_parameters[] = {0x29, 4, 0, 0, 0xFF, 0xFF};
static const char path_element[] = "test";

View File

@ -0,0 +1,142 @@
#include "CppUTest/TestHarness.h"
#include "CppUTest/CommandLineTestRunner.h"
#include "CppUTestExt/MockSupport.h"
#include "classic/obex.h"
#include "classic/obex_parser.h"
#include "classic/obex_message_builder.h"
#include "btstack_util.h"
static const uint8_t flags = 1 << 1;
static const uint16_t maximum_obex_packet_length = 0xFFFF;
static const uint8_t obex_version_number = OBEX_VERSION;
static const uint8_t target[] = { 1, 2, 3, 4};
// from parser_callback
static uint8_t test_header_id;
static uint8_t test_header_buffer[100];
static uint16_t test_header_len;
// mock hci_dump.c
extern "C" void hci_dump_log(int log_level, const char * format, ...){}
static void parser_callback(void * user_data, uint8_t header_id, uint16_t total_len, uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len){
if (obex_parser_header_store(test_header_buffer, sizeof(test_header_buffer), total_len, data_offset, data_buffer, data_len) == OBEX_PARSER_HEADER_COMPLETE){
test_header_len = total_len;
test_header_id = header_id;
}
}
TEST_GROUP(OBEX_PARSER){
obex_parser_t parser;
uint8_t message[300];
void setup(void){
test_header_id = 0;
test_header_len = 0;
}
void teardown(void){
}
void parse_request(void){
obex_parser_init_for_request(&parser, &parser_callback, NULL);
uint16_t message_len = big_endian_read_16(message, 1);
for (uint16_t i = 0; i < message_len - 1;i++){
obex_parser_object_state_t parser_state = obex_parser_process_data(&parser, &message[i], 1);
CHECK_EQUAL(OBEX_PARSER_OBJECT_STATE_INCOMPLETE, parser_state);
}
obex_parser_object_state_t parser_state = obex_parser_process_data(&parser, &message[message_len-1], 1);
CHECK_EQUAL(OBEX_PARSER_OBJECT_STATE_COMPLETE, parser_state);
}
void parse_response(uint8_t opcode){
obex_parser_init_for_response(&parser, opcode, &parser_callback, NULL);
uint16_t message_len = big_endian_read_16(message, 1);
for (uint16_t i = 0; i < message_len - 1;i++){
obex_parser_object_state_t parser_state = obex_parser_process_data(&parser, &message[i], 1);
CHECK_EQUAL(OBEX_PARSER_OBJECT_STATE_INCOMPLETE, parser_state);
}
obex_parser_object_state_t parser_state = obex_parser_process_data(&parser, &message[message_len-1], 1);
CHECK_EQUAL(OBEX_PARSER_OBJECT_STATE_COMPLETE, parser_state);
}
};
TEST(OBEX_PARSER, RequestOverrun){
(void) obex_message_builder_request_create_connect(message, sizeof(message), obex_version_number, flags, maximum_obex_packet_length);
uint16_t message_len = big_endian_read_16(message, 1);
for (uint16_t i = 0; i < message_len - 1;i++){
obex_parser_object_state_t parser_state = obex_parser_process_data(&parser, &message[i], 1);
CHECK_EQUAL(OBEX_PARSER_OBJECT_STATE_INCOMPLETE, parser_state);
}
obex_parser_object_state_t parser_state = obex_parser_process_data(&parser, &message[message_len-1], 1);
CHECK_EQUAL(OBEX_PARSER_OBJECT_STATE_COMPLETE, parser_state);
parser_state = obex_parser_process_data(&parser, &message[message_len], 1);
CHECK_EQUAL(OBEX_PARSER_OBJECT_STATE_OVERRUN, parser_state);
}
TEST(OBEX_PARSER, RequestInvalid){
(void) obex_message_builder_request_create_connect(message, sizeof(message), obex_version_number, flags, maximum_obex_packet_length);
// decrease packet len
uint16_t message_len = big_endian_read_16(message, 1) - 1;
big_endian_store_16(message, 1, message_len);
for (uint16_t i = 0; i < message_len;i++){
obex_parser_object_state_t parser_state = obex_parser_process_data(&parser, &message[i], 1);
if (i < 2){
CHECK_EQUAL(OBEX_PARSER_OBJECT_STATE_INCOMPLETE, parser_state);
} else {
CHECK_EQUAL(OBEX_PARSER_OBJECT_STATE_INVALID, parser_state);
}
}
}
TEST(OBEX_PARSER, ConnectRequest){
(void) obex_message_builder_request_create_connect(message, sizeof(message), obex_version_number, flags, maximum_obex_packet_length);
parse_request();
obex_parser_operation_info_t op_info;
obex_parser_get_operation_info(&parser, &op_info);
CHECK_EQUAL(OBEX_OPCODE_CONNECT, op_info.opcode);
CHECK_EQUAL(obex_version_number, op_info.obex_version_number);
CHECK_EQUAL(flags, op_info.flags);
CHECK_EQUAL(maximum_obex_packet_length, op_info.max_packet_length);
}
TEST(OBEX_PARSER, ConnectRequestWithTarget){
(void) obex_message_builder_request_create_connect(message, sizeof(message), obex_version_number, flags, maximum_obex_packet_length);
(void) obex_message_builder_header_add_target(message, sizeof(message), target, sizeof(target));
parse_request();
obex_parser_operation_info_t op_info;
obex_parser_get_operation_info(&parser, &op_info);
CHECK_EQUAL(OBEX_HEADER_TARGET, test_header_id);
CHECK_EQUAL(sizeof(target), test_header_len);
MEMCMP_EQUAL(target, test_header_buffer, sizeof(target));
}
TEST(OBEX_PARSER, ConnectResponse){
// no create response yet, fake it
(void) obex_message_builder_request_create_connect(message, sizeof(message), obex_version_number, flags, maximum_obex_packet_length);
message[0] = OBEX_RESP_SUCCESS;
parse_response(OBEX_OPCODE_CONNECT);
obex_parser_operation_info_t op_info;
obex_parser_get_operation_info(&parser, &op_info);
CHECK_EQUAL(OBEX_RESP_SUCCESS, op_info.response_code);
CHECK_EQUAL(obex_version_number, op_info.obex_version_number);
CHECK_EQUAL(flags, op_info.flags);
CHECK_EQUAL(maximum_obex_packet_length, op_info.max_packet_length);
}
TEST(OBEX_PARSER, GetResponseWithSRM){
// no get response yet, fake it
(void) obex_message_builder_request_create_get(message, sizeof(message), 0x1234);
obex_message_builder_header_add_srm_enable(message, sizeof(message));
parse_request();
obex_parser_operation_info_t op_info;
obex_parser_get_operation_info(&parser, &op_info);
}
TEST(OBEX_PARSER, SetPathResponse){
const uint8_t set_path_response_success[] = { 0xa0, 0x00, 0x03};
memcpy(message, set_path_response_success, sizeof(set_path_response_success));
parse_response(OBEX_OPCODE_SETPATH);
}
int main (int argc, const char * argv[]){
return CommandLineTestRunner::RunAllTests(argc, argv);
}