From ad95d4d87699f95c1f2618f1536448f2cba94c9f Mon Sep 17 00:00:00 2001 From: Fabian Nawratil Date: Wed, 9 Aug 2023 14:59:26 +0200 Subject: [PATCH] net: lib: aws_fota: Improve error handling for FOTA jobs Improves error handling by providing more fine grained errors on job parsing. Jobs are now marked as failed if document is invalid but the job id could be parsed. A new error state is introduced to enable retries for marking jobs as failed. Additionally, the job update accepted/rejected topics are now longer subscribed to, as this is not necessary to receive the respective messages and thus adds unnecessary overhead. Signed-off-by: Fabian Nawratil --- .../releases/release-notes-changelog.rst | 1 + .../net/lib/aws_fota/include/aws_fota_json.h | 14 +- subsys/net/lib/aws_fota/src/aws_fota.c | 194 +++++++++--------- subsys/net/lib/aws_fota/src/aws_fota_json.c | 83 ++++---- .../net/lib/aws_fota/aws_fota_json/src/main.c | 16 +- 5 files changed, 165 insertions(+), 143 deletions(-) diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index df19265ec32..09d58f8ee8c 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -654,6 +654,7 @@ Libraries for networking * The :kconfig:option:`CONFIG_AWS_FOTA_HOSTNAME_MAX_LEN` Kconfig option has been replaced by the :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_MAX_HOSTNAME_SIZE` Kconfig option. * The :kconfig:option:`CONFIG_AWS_FOTA_FILE_PATH_MAX_LEN` Kconfig option has been replaced by the :kconfig:option:`CONFIG_DOWNLOAD_CLIENT_MAX_FILENAME_SIZE` Kconfig option. + * AWS FOTA jobs are now marked as failed if the job document for the update is invalid. * :ref:`lib_azure_fota` library: diff --git a/subsys/net/lib/aws_fota/include/aws_fota_json.h b/subsys/net/lib/aws_fota/include/aws_fota_json.h index 3e44cb3cf12..9cc3a9b195a 100644 --- a/subsys/net/lib/aws_fota/include/aws_fota_json.h +++ b/subsys/net/lib/aws_fota/include/aws_fota_json.h @@ -37,6 +37,16 @@ extern "C" { */ #define EXECUTION_OBJ_DECODED_BIT 2 +/** @brief AWS FOTA JSON parse result. */ +enum aws_fota_json_result { + AWS_FOTA_JSON_RES_SKIPPED = 1, /*!< Not a FOTA job document, skipped */ + AWS_FOTA_JSON_RES_SUCCESS = 0, /*!< Job document parsed successfully */ + AWS_FOTA_JSON_RES_INVALID_PARAMS = -1, /*!< Input parameters invalid */ + AWS_FOTA_JSON_RES_INVALID_JOB = -2, /*!< Job document invalid, could not get job id */ + AWS_FOTA_JSON_RES_INVALID_DOCUMENT = -3,/*!< FOTA update data invalid */ + AWS_FOTA_JSON_RES_URL_TOO_LONG = -4, /*!< Parts of URL too large for buffer */ +}; + /** * @brief Parse a given AWS IoT DescribeJobExecution response JSON object. * More information on this object can be found at https://docs.aws.amazon.com/iot/latest/developerguide/jobs-api.html#mqtt-describejobexecution @@ -54,9 +64,7 @@ extern "C" { * @param[in] file_path_buf_size Size of the output buffer for the "file" field * @param[out] version_number Version number from the Job Execution data type. * - * @return 0 if the Job Execution object is empty, 1 if Job Execution object was - * correctly decoded, otherwise a negative error code is returned - * identicating reason of failure. + * @return aws_fota_json_result specifying the result of the parse operation. **/ int aws_fota_parse_DescribeJobExecution_rsp(const char *job_document, uint32_t payload_len, diff --git a/subsys/net/lib/aws_fota/src/aws_fota.c b/subsys/net/lib/aws_fota/src/aws_fota.c index 21d37142c8f..b6dad7743e3 100644 --- a/subsys/net/lib/aws_fota/src/aws_fota.c +++ b/subsys/net/lib/aws_fota/src/aws_fota.c @@ -23,6 +23,7 @@ static enum internal_state { STATE_INIT, STATE_DOWNLOADING, STATE_DOWNLOAD_COMPLETE, + STATE_ERROR, } internal_state = STATE_UNINIT; /* Enum used when parsing AWS jobs topics messages are received on. */ @@ -78,6 +79,8 @@ static char *state2str(enum internal_state state) return "STATE_DOWNLOADING"; case STATE_DOWNLOAD_COMPLETE: return "STATE_DOWNLOAD_COMPLETE"; + case STATE_ERROR: + return "STATE_ERROR"; default: return "Unknown"; } @@ -95,13 +98,18 @@ static void internal_state_set(enum internal_state new_state) internal_state = new_state; } +static void set_current_job_id(uint8_t *job_id) +{ + strncpy(job_id_handling, job_id, sizeof(job_id_handling)); + job_id_handling[sizeof(job_id_handling) - 1] = '\0'; +} + static void reset_library(void) { internal_state_set(STATE_INIT); execution_status = AWS_JOBS_QUEUED; download_progress = 0; - strncpy(job_id_handling, AWS_JOB_ID_DEFAULT, sizeof(job_id_handling)); - job_id_handling[sizeof(job_id_handling) - 1] = '\0'; + set_current_job_id(AWS_JOB_ID_DEFAULT); LOG_DBG("Library reset"); } @@ -142,8 +150,7 @@ static enum jobs_topic topic_type_get(const char *incoming_topic, size_t topic_l * * @return 0 If successful otherwise a negative error code is returned. */ -static int get_published_payload(struct mqtt_client *client, uint8_t *write_buf, - size_t length) +static int get_published_payload(struct mqtt_client *client, uint8_t *write_buf, size_t length) { uint8_t *buf = write_buf; uint8_t *end = buf + length; @@ -208,6 +215,28 @@ static int update_job_execution(struct mqtt_client *const client, return 0; } +/** + * @brief Update the job document of the current job to AWS_JOBS_FAILED and move to ERROR state. + * + * @param client Connected MQTT client instance + * + * @return 0 If successful otherwise a negative error code is returned. + */ +static int set_current_job_failed(struct mqtt_client *const client) +{ + struct aws_fota_event aws_fota_evt = { + .id = AWS_FOTA_EVT_ERROR + }; + + callback(&aws_fota_evt); + internal_state_set(STATE_ERROR); + + return update_job_execution(client, + job_id_handling, + sizeof(job_id_handling), + AWS_JOBS_FAILED, ""); +} + /** * @brief Parsing an AWS IoT Job Execution response received on $next/get MQTT * topic or notify-next. If it is a valid response the program state is @@ -220,8 +249,7 @@ static int update_job_execution(struct mqtt_client *const client, * * @return 0 If successful otherwise a negative error code is returned. */ -static int get_job_execution(struct mqtt_client *const client, - uint32_t payload_len) +static int parse_job_execution(struct mqtt_client *const client, uint32_t payload_len) { int err; int execution_version_number_prev = execution_version_number; @@ -248,13 +276,16 @@ static int get_job_execution(struct mqtt_client *const client, file_path, sizeof(file_path), &execution_version_number); - if (err < 0) { - LOG_ERR("Error when parsing the json: %d", err); - goto cleanup; - } else if (err == 0) { + if (err == AWS_FOTA_JSON_RES_SKIPPED) { LOG_DBG("Got only one field"); LOG_DBG("No queued jobs for this device"); return 0; + } else if ((err < 0) && + (err != AWS_FOTA_JSON_RES_INVALID_DOCUMENT) && + (err != AWS_FOTA_JSON_RES_URL_TOO_LONG)) { + LOG_ERR("Error when parsing the json: %d", err); + err = -ENODATA; + goto cleanup; } /* Check if the incoming job is already being handled. */ @@ -262,26 +293,35 @@ static int get_job_execution(struct mqtt_client *const client, LOG_WRN("Job already being handled, ignore message"); err = 0; goto cleanup; - } else { - strncpy(job_id_handling, job_id_incoming, sizeof(job_id_handling)); - job_id_handling[sizeof(job_id_handling) - 1] = '\0'; } + set_current_job_id(job_id_incoming); + + /* Check if the update data is valid */ + if (err == AWS_FOTA_JSON_RES_INVALID_DOCUMENT) { + LOG_ERR("Invalid FOTA update document: %d", err); + return set_current_job_failed(client); + } else if (err == AWS_FOTA_JSON_RES_URL_TOO_LONG) { + LOG_ERR("URL elements too long for buffer: %d", err); + return set_current_job_failed(client); + } + + /* Valid update */ LOG_DBG("Job ID: %s", (char *)job_id_handling); LOG_DBG("hostname: %s", (char *)hostname); LOG_DBG("file_path %s", (char *)file_path); LOG_DBG("execution_version_number: %d ", execution_version_number); - /* Subscribe to update topic to receive feedback on whether an - * update is accepted or not. - */ - err = aws_jobs_subscribe_topic_update(client, job_id_handling, update_topic); + err = update_job_execution(client, + job_id_handling, + sizeof(job_id_handling), + AWS_JOBS_IN_PROGRESS, + ""); if (err) { - LOG_ERR("Error when subscribing job_id_update: %d", err); - goto cleanup; + LOG_ERR("update_job_execution failed, error: %d", err); + return err; } - LOG_DBG("Subscribed to FOTA update topic %s", (char *)update_topic); return 0; cleanup: @@ -298,8 +338,7 @@ static int get_job_execution(struct mqtt_client *const client, * * @return 0 If successful otherwise a negative error code is returned. */ -static int job_update_accepted(struct mqtt_client *const client, - uint32_t payload_len) +static int job_update_accepted(struct mqtt_client *const client, uint32_t payload_len) { int err; int sec_tag = -1; @@ -334,12 +373,7 @@ static int job_update_accepted(struct mqtt_client *const client, err = fota_download_start(hostname, file_path, sec_tag, 0, 0); if (err) { LOG_ERR("Error (%d) when trying to start firmware download", err); - aws_fota_evt.id = AWS_FOTA_EVT_ERROR; - callback(&aws_fota_evt); - return update_job_execution(client, - job_id_handling, - sizeof(job_id_handling), - AWS_JOBS_FAILED, ""); + return set_current_job_failed(client); } internal_state_set(STATE_DOWNLOADING); @@ -361,6 +395,13 @@ static int job_update_accepted(struct mqtt_client *const client, reset_library(); } break; + case AWS_JOBS_FAILED: + LOG_DBG("Job document was updated with status FAILED"); + reset_library(); + + /* Ignore return value since error does not need to be handled. */ + (void)aws_jobs_get_job_execution(client_internal, "$next", get_topic); + break; default: LOG_ERR("Invalid execution status"); return -EINVAL; @@ -378,8 +419,7 @@ static int job_update_accepted(struct mqtt_client *const client, * * @return A negative error code is returned. */ -static int job_update_rejected(struct mqtt_client *const client, - uint32_t payload_len) +static int job_update_rejected(struct mqtt_client *const client, uint32_t payload_len) { struct aws_fota_event aws_fota_evt = { .id = AWS_FOTA_EVT_ERROR }; LOG_ERR("Job document update was rejected"); @@ -449,10 +489,11 @@ static int on_publish_evt(struct mqtt_client *const client, } LOG_DBG("Checking for an available job"); - return get_job_execution(client, payload_len); + return parse_job_execution(client, payload_len); case TOPIC_UPDATE_ACCEPTED: if (internal_state != STATE_INIT && - internal_state != STATE_DOWNLOAD_COMPLETE) { + internal_state != STATE_DOWNLOAD_COMPLETE && + internal_state != STATE_ERROR) { goto read_payload; } @@ -464,7 +505,8 @@ static int on_publish_evt(struct mqtt_client *const client, return job_update_accepted(client, payload_len); case TOPIC_UPDATE_REJECTED: if (internal_state != STATE_INIT && - internal_state != STATE_DOWNLOAD_COMPLETE) { + internal_state != STATE_DOWNLOAD_COMPLETE && + internal_state != STATE_ERROR) { goto read_payload; } @@ -500,6 +542,7 @@ static int on_publish_evt(struct mqtt_client *const client, static int on_connack_evt(struct mqtt_client *const client) { int err; + enum execution_status status; switch (internal_state) { case STATE_INIT: @@ -514,24 +557,30 @@ static int on_connack_evt(struct mqtt_client *const client) LOG_ERR("Unable to subscribe to jobs/$next/get"); return err; } + + status = AWS_JOBS_IN_PROGRESS; break; - case STATE_DOWNLOADING: - /* Fall through */ case STATE_DOWNLOAD_COMPLETE: - if (strncmp(job_id_handling, AWS_JOB_ID_DEFAULT, sizeof(job_id_handling)) == 0) { - return -ECANCELED; - } - - err = aws_jobs_subscribe_topic_update(client, job_id_handling, update_topic); - if (err) { - LOG_ERR("Error when subscribing job_id_update: %d", err); - return err; - } - - LOG_DBG("Subscribed to FOTA update topic %s", (char *)update_topic); + status = AWS_JOBS_SUCCEEDED; break; - default: + case STATE_ERROR: + status = AWS_JOBS_FAILED; break; + default: + return 0; + } + + /* If we have just reconnected, we might already be handling a job. + * Check if this is the case and update the job status accordingly. + */ + if (strncmp(job_id_handling, AWS_JOB_ID_DEFAULT, sizeof(job_id_handling)) == 0) { + return 0; + } + + err = update_job_execution(client, job_id_handling, sizeof(job_id_handling), status, ""); + if (err) { + LOG_ERR("update_job_execution failed, error: %d", err); + return err; } return 0; @@ -560,34 +609,7 @@ static int on_suback_evt(struct mqtt_client *const client, uint16_t message_id) break; case SUBSCRIBE_JOB_ID_UPDATE: LOG_DBG("Subscribed to job ID update accepted/rejected topics"); - - enum execution_status status; - - switch (internal_state) { - case STATE_INIT: - status = AWS_JOBS_IN_PROGRESS; break; - case STATE_DOWNLOAD_COMPLETE: - status = AWS_JOBS_SUCCEEDED; - break; - case STATE_DOWNLOADING: - return 0; - default: - LOG_WRN("Invalid state"); - return -ECANCELED; - } - - err = update_job_execution(client, - job_id_handling, - sizeof(job_id_handling), - status, - ""); - if (err) { - LOG_ERR("update_job_execution failed, error: %d", err); - return err; - } - - break; default: /* Message ID not related to AWS FOTA. */ break; @@ -596,8 +618,7 @@ static int on_suback_evt(struct mqtt_client *const client, uint16_t message_id) return 0; } -int aws_fota_mqtt_evt_handler(struct mqtt_client *const client, - const struct mqtt_evt *evt) +int aws_fota_mqtt_evt_handler(struct mqtt_client *const client, const struct mqtt_evt *evt) { int err; @@ -739,21 +760,11 @@ static void http_fota_handler(const struct fota_download_evt *evt) case FOTA_DOWNLOAD_EVT_ERROR: LOG_ERR("FOTA_DOWNLOAD_EVT_ERROR"); - (void)update_job_execution(client_internal, - job_id_handling, - sizeof(job_id_handling), - AWS_JOBS_FAILED, - ""); - aws_fota_evt.id = AWS_FOTA_EVT_ERROR; - - callback(&aws_fota_evt); - reset_library(); - - /* If the FOTA download fails it might be due to the image being deleted. - * Try to get the next job if any exist. - */ - (void)aws_jobs_get_job_execution(client_internal, "$next", get_topic); + err = set_current_job_failed(client_internal); + if (err < 0) { + reset_library(); + } break; case FOTA_DOWNLOAD_EVT_PROGRESS: @@ -772,8 +783,7 @@ static void http_fota_handler(const struct fota_download_evt *evt) } } -int aws_fota_init(struct mqtt_client *const client, - aws_fota_callback_t evt_handler) +int aws_fota_init(struct mqtt_client *const client, aws_fota_callback_t evt_handler) { int err; diff --git a/subsys/net/lib/aws_fota/src/aws_fota_json.c b/subsys/net/lib/aws_fota/src/aws_fota_json.c index 396fe18d391..0020ab94fca 100644 --- a/subsys/net/lib/aws_fota/src/aws_fota_json.c +++ b/subsys/net/lib/aws_fota/src/aws_fota_json.c @@ -35,14 +35,12 @@ int aws_fota_parse_UpdateJobExecution_rsp(const char *update_rsp_document, int ret; cJSON *update_response = cJSON_Parse(update_rsp_document); - if (update_response == NULL) { ret = -ENODATA; goto cleanup; } - cJSON *status = cJSON_GetObjectItemCaseSensitive(update_response, - "status"); + cJSON *status = cJSON_GetObjectItemCaseSensitive(update_response, "status"); if (cJSON_IsString(status) && status->valuestring != NULL) { strncpy_nullterm(status_buf, status->valuestring, STATUS_MAX_LEN); @@ -71,7 +69,7 @@ int aws_fota_parse_DescribeJobExecution_rsp(const char *job_document, || hostname_buf == NULL || file_path_buf == NULL || execution_version_number == NULL) { - return -EINVAL; + return AWS_FOTA_JSON_RES_INVALID_PARAMS; } int ret; @@ -79,40 +77,46 @@ int aws_fota_parse_DescribeJobExecution_rsp(const char *job_document, cJSON *json_data = cJSON_Parse(job_document); if (json_data == NULL) { - ret = -ENODATA; + ret = AWS_FOTA_JSON_RES_INVALID_JOB; goto cleanup; } - cJSON *execution = cJSON_GetObjectItemCaseSensitive(json_data, - "execution"); + cJSON *execution = cJSON_GetObjectItemCaseSensitive(json_data, "execution"); + if (execution == NULL) { - ret = 0; + ret = AWS_FOTA_JSON_RES_SKIPPED; goto cleanup; } cJSON *job_id = cJSON_GetObjectItemCaseSensitive(execution, "jobId"); if (cJSON_GetStringValue(job_id) != NULL) { - strncpy_nullterm(job_id_buf, job_id->valuestring, - AWS_JOBS_JOB_ID_MAX_LEN); + strncpy_nullterm(job_id_buf, job_id->valuestring, AWS_JOBS_JOB_ID_MAX_LEN); } else { - ret = -ENODATA; + ret = AWS_FOTA_JSON_RES_INVALID_JOB; goto cleanup; } - cJSON *job_data = cJSON_GetObjectItemCaseSensitive(execution, - "jobDocument"); + cJSON *version_number = cJSON_GetObjectItemCaseSensitive(execution, "versionNumber"); + + if (cJSON_IsNumber(version_number)) { + *execution_version_number = version_number->valueint; + } else { + ret = AWS_FOTA_JSON_RES_INVALID_JOB; + goto cleanup; + } + + cJSON *job_data = cJSON_GetObjectItemCaseSensitive(execution, "jobDocument"); if (!cJSON_IsObject(job_data)) { - ret = -ENODATA; + ret = AWS_FOTA_JSON_RES_INVALID_DOCUMENT; goto cleanup; } - cJSON *location = cJSON_GetObjectItemCaseSensitive(job_data, - "location"); + cJSON *location = cJSON_GetObjectItemCaseSensitive(job_data, "location"); if (!cJSON_IsObject(location)) { - ret = -ENODATA; + ret = AWS_FOTA_JSON_RES_INVALID_DOCUMENT; goto cleanup; } @@ -126,46 +130,45 @@ int aws_fota_parse_DescribeJobExecution_rsp(const char *job_document, http_parser_url_init(&u); http_parser_parse_url(url->valuestring, strlen(url->valuestring), false, &u); - /* Determine size of hostname and clamp to buffer size. + /* Determine size of hostname and path (consisting of path + query). * Length increased by one to get null termination at right spot when copying. */ - uint16_t parsed_host_len = - MIN(hostname_buf_size, u.field_data[UF_HOST].len + 1); + uint16_t parsed_host_len = u.field_data[UF_HOST].len + 1; + uint16_t parsed_file_len = u.field_data[UF_PATH].len + + u.field_data[UF_QUERY].len + 1; + + if (parsed_host_len > hostname_buf_size || parsed_file_len > file_path_buf_size) { + ret = AWS_FOTA_JSON_RES_URL_TOO_LONG; + goto cleanup; + } + + /* Copy slices of the hostname and path (url path + query) to the + * respective buffers. Increase path offset by one to omit initial slash. + */ strncpy_nullterm(hostname_buf, url->valuestring + u.field_data[UF_HOST].off, parsed_host_len); - - /* Determine size of file path, consisting of the path and query parts of the URL. - * Length increased by one for proper null termination and clamped to buffer size. - * Copy starting from the path offset, increased by one to omit the initial slash. - */ - uint16_t parsed_file_len = - MIN(file_path_buf_size, - u.field_data[UF_PATH].len + u.field_data[UF_QUERY].len + 1); strncpy_nullterm(file_path_buf, url->valuestring + u.field_data[UF_PATH].off + 1, parsed_file_len); } else if ((cJSON_GetStringValue(hostname) != NULL) && (cJSON_GetStringValue(path) != NULL)) { - strncpy_nullterm(hostname_buf, hostname->valuestring, hostname_buf_size); - strncpy_nullterm(file_path_buf, path->valuestring, file_path_buf_size); - } else { - ret = -ENODATA; - goto cleanup; - } - cJSON *version_number = cJSON_GetObjectItemCaseSensitive( - execution, "versionNumber"); + if (strlen(hostname->valuestring) >= hostname_buf_size + || strlen(path->valuestring) >= file_path_buf_size) { + ret = AWS_FOTA_JSON_RES_URL_TOO_LONG; + goto cleanup; + } - if (cJSON_IsNumber(version_number)) { - *execution_version_number = version_number->valueint; + strncpy_nullterm(hostname_buf, hostname->valuestring, hostname_buf_size); + strncpy_nullterm(file_path_buf, path->valuestring, file_path_buf_size); } else { - ret = -ENODATA; + ret = AWS_FOTA_JSON_RES_INVALID_DOCUMENT; goto cleanup; } - ret = 1; + ret = AWS_FOTA_JSON_RES_SUCCESS; cleanup: cJSON_Delete(json_data); return ret; diff --git a/tests/subsys/net/lib/aws_fota/aws_fota_json/src/main.c b/tests/subsys/net/lib/aws_fota/aws_fota_json/src/main.c index 2a5673dc622..765c4b5317e 100644 --- a/tests/subsys/net/lib/aws_fota/aws_fota_json/src/main.c +++ b/tests/subsys/net/lib/aws_fota/aws_fota_json/src/main.c @@ -34,7 +34,7 @@ void test_parse_job_execution(void) hostname, sizeof(hostname), file_path, sizeof(file_path), &version_number); - TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(AWS_FOTA_JSON_RES_SUCCESS, ret); TEST_ASSERT_EQUAL_STRING(expected_job_id, job_id); TEST_ASSERT_EQUAL(expected_version_number, version_number); TEST_ASSERT_EQUAL_STRING(expected_hostname, hostname); @@ -76,7 +76,7 @@ void test_parse_job_execution_single_url(void) hostname, sizeof(hostname), file_path, sizeof(file_path), &version_number); - TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(AWS_FOTA_JSON_RES_SUCCESS, ret); TEST_ASSERT_EQUAL_STRING(expected_job_id, job_id); TEST_ASSERT_EQUAL(expected_version_number, version_number); TEST_ASSERT_EQUAL_STRING(expected_hostname, hostname); @@ -98,7 +98,7 @@ void test_parse_malformed_job_execution(void) hostname, sizeof(hostname), file_path, sizeof(file_path), &version_number); - TEST_ASSERT_EQUAL(-ENODATA, ret); + TEST_ASSERT_EQUAL(AWS_FOTA_JSON_RES_INVALID_JOB, ret); } void test_parse_job_execution_missing_host_field(void) @@ -117,7 +117,7 @@ void test_parse_job_execution_missing_host_field(void) hostname, sizeof(hostname), file_path, sizeof(file_path), &version_number); - TEST_ASSERT_EQUAL(-ENODATA, ret); + TEST_ASSERT_EQUAL(AWS_FOTA_JSON_RES_INVALID_DOCUMENT, ret); } void test_parse_job_execution_missing_path_field(void) @@ -135,7 +135,7 @@ void test_parse_job_execution_missing_path_field(void) hostname, sizeof(hostname), file_path, sizeof(file_path), &version_number); - TEST_ASSERT_EQUAL(-ENODATA, ret); + TEST_ASSERT_EQUAL(AWS_FOTA_JSON_RES_INVALID_DOCUMENT, ret); } void test_parse_job_execution_missing_job_id_field(void) @@ -153,7 +153,7 @@ void test_parse_job_execution_missing_job_id_field(void) hostname, sizeof(hostname), file_path, sizeof(file_path), &version_number); - TEST_ASSERT_EQUAL(-ENODATA, ret); + TEST_ASSERT_EQUAL(AWS_FOTA_JSON_RES_INVALID_JOB, ret); } void test_parse_job_execution_missing_location_obj(void) @@ -171,7 +171,7 @@ void test_parse_job_execution_missing_location_obj(void) hostname, sizeof(hostname), file_path, sizeof(file_path), &version_number); - TEST_ASSERT_EQUAL(-ENODATA, ret); + TEST_ASSERT_EQUAL(AWS_FOTA_JSON_RES_INVALID_DOCUMENT, ret); } void test_update_job_longer_than_max(void) @@ -206,7 +206,7 @@ void test_timestamp_only(void) hostname, sizeof(hostname), file_path, sizeof(file_path), &version_number); - TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(AWS_FOTA_JSON_RES_SKIPPED, ret); } void test_update_job_exec_rsp_minimal(void)