diff --git a/Makefile b/Makefile index 5d40830..da4d894 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC ?= cc -CFLAGS ?= -O2 -std=c11 \ +CFLAGS ?= -O2 -std=c99 \ -Iinclude -Itests \ -Wall -Wextra -Wpedantic -Wconversion -Wshadow \ -Wcast-align -Wcast-qual -Wpointer-arith -Wformat=2 \ diff --git a/README.md b/README.md index d522949..b5c5133 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Minimal ECSS Packet Utilisation Standard (PUS) implementation in C, designed for | Service | Name | Subtypes | |---------|------|----------| -| ST[01] | Request Verification | Acceptance, Start, Progress, Completion (success & failure), Routing failure | +| ST[01] | Request Verification | Acceptance, Start, Completion (success & failure), Routing failure; Progress reports must be emitted manually by the handler | | ST[03] | Housekeeping | TM[3,25] HK report, TM[3,26] diagnostic report | | ST[05] | Event Reporting | Info, Low/Medium/High severity | | ST[17] | Test | TC[17,1] are-you-alive, TC[17,3] on-board connection test | @@ -115,10 +115,10 @@ void app_on_tc_received(const uint8_t *raw, uint16_t len) void app_periodic(void) { /* 7. Emit periodic housekeeping */ - pus_service_3_emit_hk(&g_pus, &g_s3, 0x0001); + pus_service_3_emit_hk(&g_pus, &g_s3, 0x0001u); /* 8. Emit an event */ - pus_service_5_emit(&g_pus, PUS_SUBTYPE_EVENT_INFO, 0x0101, NULL, 0); + pus_service_5_emit(&g_pus, PUS_SUBTYPE_EVENT_INFO, 0x0101u, NULL, 0u); } ``` diff --git a/example/obc_example.c b/example/obc_example.c index 5b3514f..720153a 100644 --- a/example/obc_example.c +++ b/example/obc_example.c @@ -109,7 +109,7 @@ static pus_status_t setter_batt_alert(uint16_t pid, const uint8_t *buf, uint16_t len, void *ud) { (void)pid; (void)len; (void)ud; - g_batt_alert_mv = (uint16_t)((uint16_t)(buf[0] << 8u) | buf[1]); + g_batt_alert_mv = (uint16_t)(((uint16_t)buf[0] << 8u) | (uint16_t)buf[1]); return PUS_STATUS_OK; } diff --git a/include/pus.h b/include/pus.h index 9281fb3..8cfc800 100644 --- a/include/pus.h +++ b/include/pus.h @@ -25,7 +25,11 @@ pus_status_t pus_tc_decode( /** * @brief Decode, route, and process a raw TC buffer. - * Emits ST[01] verification reports according to the TC ack_flags. + * + * Automatically emits ST[01] verification reports for the acceptance, start, + * and completion ACK flags. Progress reports (PUS_ACK_PROGRESS) are NOT + * emitted automatically — handlers that need them must call + * pus_service_1_emit_success() / pus_service_1_emit_failure() directly. * Unrecognised commands receive a TM[1,10] routing failure report. * * @param[in,out] ctx Active PUS context. diff --git a/include/pus_codec.h b/include/pus_codec.h index bb34262..0400fa2 100644 --- a/include/pus_codec.h +++ b/include/pus_codec.h @@ -5,36 +5,6 @@ #include #include "pus_config.h" #include "pus_types.h" -#include "pus_context.h" - -/** - * @brief Populate a TM secondary header with fields common to every outgoing packet. - * @note msg_type_counter is read from ctx->tm_counter but NOT incremented here; - * callers must increment after all error paths are cleared. - * - * @param[in] ctx Active PUS context (provides counter and time source). - * @param[out] hdr Header struct to fill. - * @param[in] service Service type identifier. - * @param[in] subtype Service subtype identifier. - * @param[in] dest_id Destination APID. - */ -static inline void pus_tm_hdr_fill( - pus_context_t *ctx, - pus_tm_sec_header_t *hdr, - pus_service_t service, - pus_subtype_t subtype, - uint16_t dest_id) -{ - hdr->version = PUS_VERSION; - hdr->time_ref_status = 0u; - hdr->service_type_id = service; - hdr->subtype_id = subtype; - hdr->msg_type_counter = ctx->tm_counter; - hdr->destination_id = dest_id; - hdr->time = (ctx->time_source != NULL) - ? ctx->time_source(ctx->time_source_user_data) : 0u; - hdr->spare = 0u; -} /** * @brief Decode a raw TC secondary header from a byte buffer. diff --git a/include/pus_context.h b/include/pus_context.h index 7cc957f..07fb055 100644 --- a/include/pus_context.h +++ b/include/pus_context.h @@ -69,7 +69,10 @@ typedef struct { /** * @brief Runtime state for one EmbeddedPUS instance. - * Allocate statically or on the stack; do not share across threads without a mutex. + * Allocate statically or on the stack. + * @warning Not thread-safe. If the context is accessed from more than one task + * or interrupt, all calls must be serialised with a mutex or critical + * section. */ struct pus_context { pus_handler_entry_t handler_table[PUS_MAX_TC_HANDLERS]; /**< TC dispatch table. */ diff --git a/include/pus_handler.h b/include/pus_handler.h index e76f172..f46b46c 100644 --- a/include/pus_handler.h +++ b/include/pus_handler.h @@ -7,13 +7,19 @@ * @brief Register or update a TC handler for a (service, subtype) pair. * If the pair is already registered, the handler and user_data are replaced. * + * Passing NULL for @p handler deregisters the entry for that (service, subtype) + * pair. If no entry is found for that pair, PUS_STATUS_NO_HANDLER is returned. + * * @param[in,out] ctx Active PUS context. * @param[in] service Service type identifier. * @param[in] subtype Service subtype identifier. - * @param[in] handler Callback invoked when a matching TC is received. - * @param[in] user_data Opaque pointer forwarded to the handler. + * @param[in] handler Callback to register, or NULL to deregister. + * @param[in] user_data Opaque pointer forwarded to the handler (ignored when deregistering). * - * @return PUS_STATUS_NULL, PUS_STATUS_TABLE_FULL, or PUS_STATUS_OK. + * @return PUS_STATUS_NULL if ctx is NULL. + * @return PUS_STATUS_NO_HANDLER if handler is NULL and no matching entry exists. + * @return PUS_STATUS_TABLE_FULL if the table is full and no matching entry exists. + * @return PUS_STATUS_OK on success. */ pus_status_t pus_handler_register( pus_context_t *ctx, @@ -23,17 +29,24 @@ pus_status_t pus_handler_register( void *user_data); /** - * @brief Return the handler table index for a (service, subtype) pair. + * @brief Invoke the registered handler for a (service, subtype) pair. + * + * Calls the handler with @p ctx and @p tc, passing through the stored + * user_data. * - * @param[in] ctx Active PUS context. - * @param[in] service Service type identifier. - * @param[in] subtype Service subtype identifier. + * @param[in,out] ctx Active PUS context. + * @param[in] service Service type identifier. + * @param[in] subtype Service subtype identifier. + * @param[in] tc TC packet to pass to the handler. * - * @return Table index (>= 0) if found, -1 if not registered. + * @return PUS_STATUS_NULL if ctx or tc is NULL. + * @return PUS_STATUS_NO_HANDLER if no handler is registered for the pair. + * @return Whatever the handler returns otherwise. */ -int pus_handler_find( - const pus_context_t *ctx, - pus_service_t service, - pus_subtype_t subtype); +pus_status_t pus_handler_invoke( + pus_context_t *ctx, + pus_service_t service, + pus_subtype_t subtype, + const pus_tc_packet_t *tc); #endif /* PUS_HANDLER_H */ diff --git a/include/pus_service_1.h b/include/pus_service_1.h index 177e040..6368845 100644 --- a/include/pus_service_1.h +++ b/include/pus_service_1.h @@ -9,7 +9,20 @@ /** @{ */ #define PUS_ACK_ACCEPTANCE 0x08u /**< Request acceptance verification report. */ #define PUS_ACK_START 0x04u /**< Request start of execution verification report. */ -#define PUS_ACK_PROGRESS 0x02u /**< Request progress verification report. */ +/** + * @brief Request progress verification report. + * @note pus_tc_process() does NOT emit progress reports automatically. + * Handlers that need them must call pus_service_1_emit_success() with + * PUS_SUBTYPE_VERIFICATION_PROGRESS_SUCCESS (or _FAILURE) at suitable + * points during their own execution, checking this flag themselves: + * @code + * if (tc->sec_header.ack_flags & PUS_ACK_PROGRESS) { + * pus_service_1_emit_success(ctx, tc, + * PUS_SUBTYPE_VERIFICATION_PROGRESS_SUCCESS); + * } + * @endcode + */ +#define PUS_ACK_PROGRESS 0x02u #define PUS_ACK_COMPLETION 0x01u /**< Request completion verification report. */ /** @} */ @@ -56,8 +69,8 @@ pus_status_t pus_service_1_build_failure( uint16_t *out_len); /** - * @brief Build and forward a success report to ctx->tm_sink. - * Returns PUS_STATUS_OK silently when no sink is configured. + * @brief Build a success report and forward it to ctx->tm_sink. + * Increments ctx->tm_counter regardless of whether a TM sink is configured. * * @param[in,out] ctx Active PUS context. * @param[in] tc The TC being verified. @@ -71,8 +84,8 @@ pus_status_t pus_service_1_emit_success( pus_subtype_t subtype); /** - * @brief Build and forward a failure report to ctx->tm_sink. - * Returns PUS_STATUS_OK silently when no sink is configured. + * @brief Build a failure report and forward it to ctx->tm_sink. + * Increments ctx->tm_counter regardless of whether a TM sink is configured. * * @param[in,out] ctx Active PUS context. * @param[in] tc The TC being verified. diff --git a/src/pus.c b/src/pus.c index 2e35f02..ca0f5f3 100644 --- a/src/pus.c +++ b/src/pus.c @@ -1,5 +1,6 @@ #include "pus.h" #include "pus_codec.h" +#include "pus_internal.h" #include "pus_handler.h" #include "pus_service_1.h" #include diff --git a/src/pus_handler.c b/src/pus_handler.c index 7a26947..000e799 100644 --- a/src/pus_handler.c +++ b/src/pus_handler.c @@ -1,4 +1,5 @@ #include "pus_handler.h" +#include "pus_internal.h" #include pus_status_t pus_handler_register( @@ -8,32 +9,47 @@ pus_status_t pus_handler_register( pus_tc_handler_t handler, void *user_data) { - if (ctx == NULL || handler == NULL) { + if (ctx == NULL) { return PUS_STATUS_NULL; } - for (uint16_t i = 0u; i < PUS_MAX_TC_HANDLERS; i++) { - if (ctx->handler_table[i].is_used && - ctx->handler_table[i].service == service && - ctx->handler_table[i].subtype == subtype) { - ctx->handler_table[i].handler = handler; - ctx->handler_table[i].user_data = user_data; - return PUS_STATUS_OK; + /* NULL handler means deregister: find and clear the existing entry. */ + if (handler == NULL) { + for (uint16_t i = 0u; i < PUS_MAX_TC_HANDLERS; i++) { + if (ctx->handler_table[i].is_used && + ctx->handler_table[i].service == service && + ctx->handler_table[i].subtype == subtype) { + ctx->handler_table[i].is_used = 0u; + return PUS_STATUS_OK; + } } + return PUS_STATUS_NO_HANDLER; } - for (uint16_t i = 0u; i < PUS_MAX_TC_HANDLERS; i++) { - if (!ctx->handler_table[i].is_used) { - ctx->handler_table[i].service = service; - ctx->handler_table[i].subtype = subtype; - ctx->handler_table[i].handler = handler; - ctx->handler_table[i].user_data = user_data; - ctx->handler_table[i].is_used = 1u; - return PUS_STATUS_OK; + { + int32_t first_free = -1; + for (uint16_t i = 0u; i < PUS_MAX_TC_HANDLERS; i++) { + if (ctx->handler_table[i].is_used) { + if (ctx->handler_table[i].service == service && + ctx->handler_table[i].subtype == subtype) { + ctx->handler_table[i].handler = handler; + ctx->handler_table[i].user_data = user_data; + return PUS_STATUS_OK; + } + } else if (first_free < 0) { + first_free = (int32_t)i; + } + } + if (first_free < 0) { + return PUS_STATUS_TABLE_FULL; } + ctx->handler_table[first_free].service = service; + ctx->handler_table[first_free].subtype = subtype; + ctx->handler_table[first_free].handler = handler; + ctx->handler_table[first_free].user_data = user_data; + ctx->handler_table[first_free].is_used = 1u; + return PUS_STATUS_OK; } - - return PUS_STATUS_TABLE_FULL; } int pus_handler_find( @@ -55,3 +71,24 @@ int pus_handler_find( return -1; } + +pus_status_t pus_handler_invoke( + pus_context_t *ctx, + pus_service_t service, + pus_subtype_t subtype, + const pus_tc_packet_t *tc) +{ + int idx; + + if (ctx == NULL || tc == NULL) { + return PUS_STATUS_NULL; + } + + idx = pus_handler_find(ctx, service, subtype); + if (idx < 0) { + return PUS_STATUS_NO_HANDLER; + } + + return ctx->handler_table[idx].handler(ctx, tc, + ctx->handler_table[idx].user_data); +} diff --git a/src/pus_internal.h b/src/pus_internal.h new file mode 100644 index 0000000..7661159 --- /dev/null +++ b/src/pus_internal.h @@ -0,0 +1,41 @@ +#ifndef PUS_INTERNAL_H +#define PUS_INTERNAL_H + +#include +#include "pus_config.h" +#include "pus_context.h" +#include "pus_types.h" + +/* + * Fill the fields common to every outgoing TM secondary header. + * msg_type_counter is copied from ctx->tm_counter but NOT incremented here; + * callers must increment after all error paths are cleared. + */ +static inline void pus_tm_hdr_fill( + pus_context_t *ctx, + pus_tm_sec_header_t *hdr, + pus_service_t service, + pus_subtype_t subtype, + uint16_t dest_id) +{ + hdr->version = PUS_VERSION; + hdr->time_ref_status = 0u; + hdr->service_type_id = service; + hdr->subtype_id = subtype; + hdr->msg_type_counter = ctx->tm_counter; + hdr->destination_id = dest_id; + hdr->time = (ctx->time_source != NULL) + ? ctx->time_source(ctx->time_source_user_data) : 0u; + hdr->spare = 0u; +} + +/* + * Returns the table index of the handler for (service, subtype), or -1 if + * not found. Internal use only — callers must not store or expose the index. + */ +int pus_handler_find( + const pus_context_t *ctx, + pus_service_t service, + pus_subtype_t subtype); + +#endif /* PUS_INTERNAL_H */ diff --git a/src/pus_service_1.c b/src/pus_service_1.c index e082929..de6905a 100644 --- a/src/pus_service_1.c +++ b/src/pus_service_1.c @@ -2,6 +2,7 @@ #include "pus_service_1.h" #include "pus_services.h" #include "pus_codec.h" +#include "pus_internal.h" #include /* @@ -91,27 +92,22 @@ pus_status_t pus_service_1_emit_success( const pus_tc_packet_t *tc, pus_subtype_t subtype) { - uint8_t payload[SUCCESS_PAYLOAD_LEN]; uint8_t out[REPORT_BUF_LEN]; uint16_t out_len; - uint16_t source_id; + pus_status_t st; if (ctx == NULL || tc == NULL) { return PUS_STATUS_NULL; } - if (ctx->tm_sink == NULL) { - return PUS_STATUS_OK; + st = pus_service_1_build_success(ctx, tc, subtype, + out, sizeof(out), &out_len); + if (st != PUS_STATUS_OK) { + return st; } - - source_id = tc->sec_header.source_id; - payload[0] = tc->sec_header.service_type_id; - payload[1] = tc->sec_header.subtype_id; - payload[2] = (uint8_t)(source_id >> 8u); - payload[3] = (uint8_t)(source_id & 0xFFu); - - return pus_tm_build(ctx, PUS_SERVICE_REQUEST_VERIFICATION, subtype, - source_id, payload, SUCCESS_PAYLOAD_LEN, - out, sizeof(out), &out_len); + if (ctx->tm_sink != NULL) { + return ctx->tm_sink(ctx->tm_sink_user_data, out, out_len); + } + return PUS_STATUS_OK; } pus_status_t pus_service_1_emit_failure( @@ -120,27 +116,20 @@ pus_status_t pus_service_1_emit_failure( pus_subtype_t subtype, uint16_t failure_code) { - uint8_t payload[FAILURE_PAYLOAD_LEN]; uint8_t out[REPORT_BUF_LEN]; uint16_t out_len; - uint16_t source_id; + pus_status_t st; if (ctx == NULL || tc == NULL) { return PUS_STATUS_NULL; } - if (ctx->tm_sink == NULL) { - return PUS_STATUS_OK; + st = pus_service_1_build_failure(ctx, tc, subtype, failure_code, + out, sizeof(out), &out_len); + if (st != PUS_STATUS_OK) { + return st; } - - source_id = tc->sec_header.source_id; - payload[0] = tc->sec_header.service_type_id; - payload[1] = tc->sec_header.subtype_id; - payload[2] = (uint8_t)(source_id >> 8u); - payload[3] = (uint8_t)(source_id & 0xFFu); - payload[4] = (uint8_t)(failure_code >> 8u); - payload[5] = (uint8_t)(failure_code & 0xFFu); - - return pus_tm_build(ctx, PUS_SERVICE_REQUEST_VERIFICATION, subtype, - source_id, payload, FAILURE_PAYLOAD_LEN, - out, sizeof(out), &out_len); + if (ctx->tm_sink != NULL) { + return ctx->tm_sink(ctx->tm_sink_user_data, out, out_len); + } + return PUS_STATUS_OK; } diff --git a/src/pus_service_20.c b/src/pus_service_20.c index 5297843..53c287d 100644 --- a/src/pus_service_20.c +++ b/src/pus_service_20.c @@ -1,3 +1,4 @@ +#include "pus.h" #include "pus_service_20.h" #include "pus_services.h" #include "pus_codec.h" @@ -38,31 +39,32 @@ pus_status_t pus_service_20_register_param( return PUS_STATUS_NULL; } - /* Update existing entry */ - for (uint8_t i = 0u; i < PUS_SERVICE_20_MAX_PARAMS; i++) { - if (s20->params[i].is_used && s20->params[i].param_id == param_id) { - s20->params[i].value_len = value_len; - s20->params[i].getter = getter; - s20->params[i].setter = setter; - s20->params[i].user_data = user_data; - return PUS_STATUS_OK; + { + int32_t first_free = -1; + for (uint8_t i = 0u; i < PUS_SERVICE_20_MAX_PARAMS; i++) { + if (s20->params[i].is_used) { + if (s20->params[i].param_id == param_id) { + s20->params[i].value_len = value_len; + s20->params[i].getter = getter; + s20->params[i].setter = setter; + s20->params[i].user_data = user_data; + return PUS_STATUS_OK; + } + } else if (first_free < 0) { + first_free = (int32_t)i; + } } - } - - /* Find free slot */ - for (uint8_t i = 0u; i < PUS_SERVICE_20_MAX_PARAMS; i++) { - if (!s20->params[i].is_used) { - s20->params[i].param_id = param_id; - s20->params[i].value_len = value_len; - s20->params[i].getter = getter; - s20->params[i].setter = setter; - s20->params[i].user_data = user_data; - s20->params[i].is_used = 1u; - return PUS_STATUS_OK; + if (first_free < 0) { + return PUS_STATUS_TABLE_FULL; } + s20->params[first_free].param_id = param_id; + s20->params[first_free].value_len = value_len; + s20->params[first_free].getter = getter; + s20->params[first_free].setter = setter; + s20->params[first_free].user_data = user_data; + s20->params[first_free].is_used = 1u; + return PUS_STATUS_OK; } - - return PUS_STATUS_TABLE_FULL; } pus_status_t pus_service_20_emit_report( @@ -71,27 +73,18 @@ pus_status_t pus_service_20_emit_report( const uint16_t *param_ids, uint8_t count) { - pus_status_t st; - pus_tm_sec_header_t hdr; - uint8_t out[MAX_OUT_LEN]; - uint16_t hdr_len; - uint16_t off; + pus_status_t st; + uint8_t payload[PUS_MAX_TM_PAYLOAD_LEN]; + uint8_t out[MAX_OUT_LEN]; + uint16_t out_len; + uint16_t off = 0u; if (ctx == NULL || s20 == NULL || param_ids == NULL) { return PUS_STATUS_NULL; } - /* Fill header fields but do NOT increment ctx->tm_counter yet — any error - * below must not consume a sequence number. */ - pus_tm_hdr_fill(ctx, &hdr, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_VALUE_REPORT, - ctx->default_destination_id); - - (void)pus_tm_sec_header_encode(&hdr, out, sizeof(out), &hdr_len); - /* Payload: N (1 byte) + N × [PID (2) + value (value_len)] */ - off = hdr_len; - out[off++] = count; + payload[off++] = count; for (uint8_t i = 0u; i < count; i++) { int idx = find_param(s20, param_ids[i]); @@ -102,28 +95,26 @@ pus_status_t pus_service_20_emit_report( uint16_t pid = param_ids[i]; uint16_t value_len = s20->params[idx].value_len; - if ((uint16_t)(off + 2u + value_len) > (uint16_t)sizeof(out)) { + if ((uint16_t)(off + 2u + value_len) > (uint16_t)sizeof(payload)) { return PUS_STATUS_BUFFER_TOO_SMALL; } - out[off] = (uint8_t)(pid >> 8u); - out[off + 1u] = (uint8_t)(pid & 0xFFu); + payload[off] = (uint8_t)(pid >> 8u); + payload[off + 1u] = (uint8_t)(pid & 0xFFu); off += 2u; - st = s20->params[idx].getter(pid, &out[off], value_len, s20->params[idx].user_data); + st = s20->params[idx].getter(pid, &payload[off], value_len, s20->params[idx].user_data); if (st != PUS_STATUS_OK) { return st; } off += value_len; } - /* All params resolved successfully — now commit the counter. */ - ctx->tm_counter++; - - if (ctx->tm_sink == NULL) { - return PUS_STATUS_OK; - } - return ctx->tm_sink(ctx->tm_sink_user_data, out, off); + return pus_tm_build(ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_VALUE_REPORT, + ctx->default_destination_id, + payload, off, + out, sizeof(out), &out_len); } static pus_status_t handle_tc_20_1( diff --git a/src/pus_service_3.c b/src/pus_service_3.c index 9c691b7..c1a7fd1 100644 --- a/src/pus_service_3.c +++ b/src/pus_service_3.c @@ -1,3 +1,4 @@ +#include "pus.h" #include "pus_service_3.h" #include "pus_services.h" #include "pus_codec.h" @@ -6,7 +7,8 @@ /* TM[3,25/26] payload: SID(2) + provider data (up to PUS_MAX_TM_PAYLOAD_LEN) */ #define SID_LEN 2u -#define MAX_OUT_LEN (PUS_TM_SEC_HEADER_LEN + SID_LEN + PUS_MAX_TM_PAYLOAD_LEN) +#define MAX_PAYLOAD (SID_LEN + PUS_MAX_TM_PAYLOAD_LEN) +#define MAX_OUT_LEN (PUS_TM_SEC_HEADER_LEN + MAX_PAYLOAD) static int find_entry(const pus_service_3_ctx_t *s3, uint16_t sid, uint8_t kind) { @@ -31,28 +33,30 @@ static pus_status_t register_structure( return PUS_STATUS_NULL; } - for (uint8_t i = 0u; i < PUS_SERVICE_3_MAX_STRUCTURES; i++) { - if (s3->structures[i].is_used && - s3->structures[i].sid == sid && - s3->structures[i].kind == kind) { - s3->structures[i].provider = provider; - s3->structures[i].user_data = user_data; - return PUS_STATUS_OK; + { + int32_t first_free = -1; + for (uint8_t i = 0u; i < PUS_SERVICE_3_MAX_STRUCTURES; i++) { + if (s3->structures[i].is_used) { + if (s3->structures[i].sid == sid && + s3->structures[i].kind == kind) { + s3->structures[i].provider = provider; + s3->structures[i].user_data = user_data; + return PUS_STATUS_OK; + } + } else if (first_free < 0) { + first_free = (int32_t)i; + } } - } - - for (uint8_t i = 0u; i < PUS_SERVICE_3_MAX_STRUCTURES; i++) { - if (!s3->structures[i].is_used) { - s3->structures[i].sid = sid; - s3->structures[i].kind = kind; - s3->structures[i].provider = provider; - s3->structures[i].user_data = user_data; - s3->structures[i].is_used = 1u; - return PUS_STATUS_OK; + if (first_free < 0) { + return PUS_STATUS_TABLE_FULL; } + s3->structures[first_free].sid = sid; + s3->structures[first_free].kind = kind; + s3->structures[first_free].provider = provider; + s3->structures[first_free].user_data = user_data; + s3->structures[first_free].is_used = 1u; + return PUS_STATUS_OK; } - - return PUS_STATUS_TABLE_FULL; } static pus_status_t emit_report( @@ -61,14 +65,13 @@ static pus_status_t emit_report( uint16_t sid, uint8_t kind) { - pus_status_t st; - pus_tm_sec_header_t hdr; - uint8_t out[MAX_OUT_LEN]; - uint16_t hdr_len; - uint16_t data_len = 0u; - uint16_t max_data = (uint16_t)(sizeof(out) - PUS_TM_SEC_HEADER_LEN - SID_LEN); - pus_subtype_t subtype; - int idx; + pus_status_t st; + uint8_t payload[MAX_PAYLOAD]; + uint8_t out[MAX_OUT_LEN]; + uint16_t out_len; + uint16_t data_len = 0u; + pus_subtype_t subtype; + int idx; if (ctx == NULL || s3 == NULL) { return PUS_STATUS_NULL; @@ -83,36 +86,27 @@ static pus_status_t emit_report( ? PUS_SUBTYPE_HOUSEKEEPING_DIAGNOSTIC_REPORT : PUS_SUBTYPE_HOUSEKEEPING_PARAMETER_REPORT; - pus_tm_hdr_fill(ctx, &hdr, PUS_SERVICE_HOUSEKEEPING, subtype, - ctx->default_destination_id); - - (void)pus_tm_sec_header_encode(&hdr, out, sizeof(out), &hdr_len); - - out[hdr_len] = (uint8_t)(sid >> 8u); - out[hdr_len + 1u] = (uint8_t)(sid & 0xFFu); + payload[0] = (uint8_t)(sid >> 8u); + payload[1] = (uint8_t)(sid & 0xFFu); st = s3->structures[idx].provider( sid, - &out[hdr_len + SID_LEN], - max_data, + &payload[SID_LEN], + (uint16_t)(sizeof(payload) - SID_LEN), &data_len, s3->structures[idx].user_data); if (st != PUS_STATUS_OK) { return st; } - /* A misbehaving provider might report more bytes than the buffer holds. */ - if (data_len > max_data) { + if (data_len > (uint16_t)(sizeof(payload) - SID_LEN)) { return PUS_STATUS_BAD_LENGTH; } - ctx->tm_counter++; - - if (ctx->tm_sink == NULL) { - return PUS_STATUS_OK; - } - return ctx->tm_sink(ctx->tm_sink_user_data, out, - (uint16_t)(hdr_len + SID_LEN + data_len)); + return pus_tm_build(ctx, PUS_SERVICE_HOUSEKEEPING, subtype, + ctx->default_destination_id, + payload, (uint16_t)(SID_LEN + data_len), + out, sizeof(out), &out_len); } pus_status_t pus_service_3_init(pus_service_3_ctx_t *s3) diff --git a/tests/test_helpers.h b/tests/test_helpers.h new file mode 100644 index 0000000..60158cf --- /dev/null +++ b/tests/test_helpers.h @@ -0,0 +1,28 @@ +#ifndef TEST_HELPERS_H +#define TEST_HELPERS_H + +#include +#include "pus_context.h" +#include "pus_types.h" + +/* Shared capture buffer for the TM sink across all test files. */ +static uint8_t th_buf[512]; +static uint16_t th_len = 0; + +static pus_status_t th_sink(void *ud, const uint8_t *data, uint16_t len) +{ + (void)ud; + memcpy(th_buf, data, len < sizeof(th_buf) ? len : sizeof(th_buf)); + th_len = len; + return PUS_STATUS_OK; +} + +static pus_context_t th_make_ctx(void) +{ + pus_context_t ctx; + pus_init(&ctx); + ctx.tm_sink = th_sink; + return ctx; +} + +#endif /* TEST_HELPERS_H */ diff --git a/tests/test_pus.c b/tests/test_pus.c index 9a0d7cf..cbd12ed 100644 --- a/tests/test_pus.c +++ b/tests/test_pus.c @@ -197,6 +197,48 @@ static int test_tc_process_completion_failure(void) return 0; } +/* ---- PUS_ACK_PROGRESS — handler-side responsibility ---- */ + +/* + * Mock handler that manually emits TM[1,5] progress success when the TC has + * the PUS_ACK_PROGRESS flag set, as documented in pus_service_1.h. + */ +static pus_status_t progress_handler( + pus_context_t *ctx, + const pus_tc_packet_t *tc, + void *ud) +{ + (void)ud; + if (tc->sec_header.ack_flags & PUS_ACK_PROGRESS) { + pus_service_1_emit_success(ctx, tc, + PUS_SUBTYPE_VERIFICATION_PROGRESS_SUCCESS); + } + return PUS_STATUS_OK; +} + +static int test_tc_process_progress_ack_emitted_by_handler(void) +{ + pus_context_t ctx; + pus_init(&ctx); + ctx.tm_sink = test_sink; + pus_handler_register(&ctx, 17u, 1u, progress_handler, NULL); + + uint8_t buf[PUS_TC_SEC_HEADER_LEN]; + make_tc_buf(buf, 17u, 1u, + (uint8_t)(PUS_ACK_ACCEPTANCE | PUS_ACK_PROGRESS | PUS_ACK_COMPLETION)); + g_len = 0; + ASSERT_EQ_INT(PUS_STATUS_OK, pus_tc_process(&ctx, buf, sizeof(buf))); + + /* The last packet forwarded to the sink should be TM[1,7] completion success. + * TM[1,5] progress was emitted inside the handler; we verify the counter + * advanced by 3 (acceptance=1, progress=1, completion=1). */ + ASSERT_EQ_INT(3, ctx.tm_counter); + /* Last captured packet: service=1, subtype=7 (completion success) */ + ASSERT_EQ_INT(1u, g_buf[1]); + ASSERT_EQ_INT(PUS_SUBTYPE_VERIFICATION_COMPLETION_SUCCESS, g_buf[2]); + return 0; +} + /* ---- pus_tm_build ---- */ static int test_tm_build_null(void) @@ -268,6 +310,35 @@ static int test_tm_build_no_sink(void) return 0; } +/* ---- tm_counter increments ---- */ + +static int test_tm_counter_increments_with_sink(void) +{ + pus_context_t ctx; + pus_init(&ctx); + ctx.tm_sink = test_sink; + uint8_t buf[64]; uint16_t len; + + ASSERT_EQ_INT(0, ctx.tm_counter); + pus_tm_build(&ctx, 17u, 2u, 0u, NULL, 0u, buf, sizeof(buf), &len); + ASSERT_EQ_INT(1, ctx.tm_counter); + pus_tm_build(&ctx, 17u, 2u, 0u, NULL, 0u, buf, sizeof(buf), &len); + ASSERT_EQ_INT(2, ctx.tm_counter); + return 0; +} + +static int test_tm_counter_increments_without_sink(void) +{ + pus_context_t ctx; + pus_init(&ctx); /* no sink */ + uint8_t buf[64]; uint16_t len; + + ASSERT_EQ_INT(0, ctx.tm_counter); + pus_tm_build(&ctx, 17u, 2u, 0u, NULL, 0u, buf, sizeof(buf), &len); + ASSERT_EQ_INT(1, ctx.tm_counter); + return 0; +} + pus_test_result_t test_pus_run_all(void) { RUN_TEST(test_init_null); @@ -285,10 +356,13 @@ pus_test_result_t test_pus_run_all(void) RUN_TEST(test_tc_process_no_handler); RUN_TEST(test_tc_process_all_acks_success); RUN_TEST(test_tc_process_completion_failure); + RUN_TEST(test_tc_process_progress_ack_emitted_by_handler); RUN_TEST(test_tm_build_null); RUN_TEST(test_tm_build_payload_too_large); RUN_TEST(test_tm_build_capacity_too_small); RUN_TEST(test_tm_build_null_payload_zero_fills); RUN_TEST(test_tm_build_no_sink); + RUN_TEST(test_tm_counter_increments_with_sink); + RUN_TEST(test_tm_counter_increments_without_sink); return (pus_test_result_t){ cunit_total_tests - cunit_overall_failures, cunit_total_tests }; } diff --git a/tests/test_pus_handler.c b/tests/test_pus_handler.c index 67ccc6b..c4473bb 100644 --- a/tests/test_pus_handler.c +++ b/tests/test_pus_handler.c @@ -16,17 +16,25 @@ static pus_status_t other_handler(pus_context_t *ctx, const pus_tc_packet_t *tc, return PUS_STATUS_HANDLER_FAILED; } +static pus_tc_packet_t make_tc(void) +{ + pus_tc_packet_t tc; + memset(&tc, 0, sizeof(tc)); + return tc; +} + static int test_register_and_find(void) { pus_context_t ctx; pus_init(&ctx); + pus_tc_packet_t tc = make_tc(); ASSERT_EQ_INT(PUS_STATUS_OK, pus_handler_register(&ctx, 17u, 1u, dummy_handler, NULL)); - int idx = pus_handler_find(&ctx, 17u, 1u); - ASSERT_TRUE(idx >= 0); - ASSERT_TRUE(ctx.handler_table[idx].handler == dummy_handler); + /* Invocation returns whatever dummy_handler returns (OK) */ + ASSERT_EQ_INT(PUS_STATUS_OK, + pus_handler_invoke(&ctx, 17u, 1u, &tc)); return 0; } @@ -35,8 +43,10 @@ static int test_not_found(void) { pus_context_t ctx; pus_init(&ctx); + pus_tc_packet_t tc = make_tc(); - ASSERT_EQ_INT(-1, pus_handler_find(&ctx, 17u, 1u)); + ASSERT_EQ_INT(PUS_STATUS_NO_HANDLER, + pus_handler_invoke(&ctx, 17u, 1u, &tc)); return 0; } @@ -45,15 +55,17 @@ static int test_update_on_duplicate(void) { pus_context_t ctx; pus_init(&ctx); + pus_tc_packet_t tc = make_tc(); pus_handler_register(&ctx, 17u, 1u, dummy_handler, NULL); ASSERT_EQ_INT(PUS_STATUS_OK, pus_handler_register(&ctx, 17u, 1u, other_handler, NULL)); - int idx = pus_handler_find(&ctx, 17u, 1u); - ASSERT_TRUE(idx >= 0); - ASSERT_TRUE(ctx.handler_table[idx].handler == other_handler); - /* update must not add a new slot — verify by counting is_used entries */ + /* After update, other_handler (returns HANDLER_FAILED) must be active */ + ASSERT_EQ_INT(PUS_STATUS_HANDLER_FAILED, + pus_handler_invoke(&ctx, 17u, 1u, &tc)); + + /* Update must not add a new slot — verify by counting is_used entries */ int used = 0; for (uint16_t i = 0u; i < PUS_MAX_TC_HANDLERS; i++) { if (ctx.handler_table[i].is_used) used++; @@ -67,15 +79,17 @@ static int test_multiple_services(void) { pus_context_t ctx; pus_init(&ctx); + pus_tc_packet_t tc = make_tc(); pus_handler_register(&ctx, 17u, 1u, dummy_handler, NULL); pus_handler_register(&ctx, 17u, 3u, dummy_handler, NULL); pus_handler_register(&ctx, 20u, 1u, dummy_handler, NULL); - ASSERT_TRUE(pus_handler_find(&ctx, 17u, 1u) >= 0); - ASSERT_TRUE(pus_handler_find(&ctx, 17u, 3u) >= 0); - ASSERT_TRUE(pus_handler_find(&ctx, 20u, 1u) >= 0); - ASSERT_EQ_INT(-1, pus_handler_find(&ctx, 20u, 3u)); + ASSERT_TRUE(pus_handler_invoke(&ctx, 17u, 1u, &tc) != PUS_STATUS_NO_HANDLER); + ASSERT_TRUE(pus_handler_invoke(&ctx, 17u, 3u, &tc) != PUS_STATUS_NO_HANDLER); + ASSERT_TRUE(pus_handler_invoke(&ctx, 20u, 1u, &tc) != PUS_STATUS_NO_HANDLER); + ASSERT_EQ_INT(PUS_STATUS_NO_HANDLER, + pus_handler_invoke(&ctx, 20u, 3u, &tc)); return 0; } @@ -98,9 +112,41 @@ static int test_table_full(void) static int test_null_context(void) { + pus_tc_packet_t tc = make_tc(); + ASSERT_EQ_INT(PUS_STATUS_NULL, pus_handler_register(NULL, 1u, 1u, dummy_handler, NULL)); - ASSERT_EQ_INT(-1, pus_handler_find(NULL, 1u, 1u)); + ASSERT_EQ_INT(PUS_STATUS_NULL, + pus_handler_invoke(NULL, 1u, 1u, &tc)); + + return 0; +} + +static int test_deregister_with_null_handler(void) +{ + pus_context_t ctx; + pus_init(&ctx); + pus_tc_packet_t tc = make_tc(); + + /* Deregistering when nothing is registered returns NO_HANDLER */ + ASSERT_EQ_INT(PUS_STATUS_NO_HANDLER, + pus_handler_register(&ctx, 17u, 1u, NULL, NULL)); + + /* Register, then deregister */ + ASSERT_EQ_INT(PUS_STATUS_OK, + pus_handler_register(&ctx, 17u, 1u, dummy_handler, NULL)); + ASSERT_EQ_INT(PUS_STATUS_OK, + pus_handler_register(&ctx, 17u, 1u, NULL, NULL)); + + /* Slot must be free */ + ASSERT_EQ_INT(PUS_STATUS_NO_HANDLER, + pus_handler_invoke(&ctx, 17u, 1u, &tc)); + + /* Freed slot can be reused */ + ASSERT_EQ_INT(PUS_STATUS_OK, + pus_handler_register(&ctx, 17u, 1u, dummy_handler, NULL)); + ASSERT_EQ_INT(PUS_STATUS_OK, + pus_handler_invoke(&ctx, 17u, 1u, &tc)); return 0; } @@ -113,5 +159,6 @@ pus_test_result_t test_pus_handler_run_all(void) RUN_TEST(test_multiple_services); RUN_TEST(test_table_full); RUN_TEST(test_null_context); + RUN_TEST(test_deregister_with_null_handler); return (pus_test_result_t){ cunit_total_tests - cunit_overall_failures, cunit_total_tests }; } diff --git a/tests/test_pus_service_1.c b/tests/test_pus_service_1.c index 1c98cd0..7f9a63f 100644 --- a/tests/test_pus_service_1.c +++ b/tests/test_pus_service_1.c @@ -1,29 +1,15 @@ #include "cunit.h" #include "test_runners.h" +#include "test_helpers.h" #include "pus_service_1.h" #include "pus_context.h" #include "pus_services.h" #include "pus_codec.h" #include -static uint8_t g_buf[64]; -static uint16_t g_len = 0; - -static pus_status_t test_sink(void *ud, const uint8_t *data, uint16_t len) -{ - (void)ud; - memcpy(g_buf, data, len < sizeof(g_buf) ? len : sizeof(g_buf)); - g_len = len; - return PUS_STATUS_OK; -} - -static pus_context_t make_ctx(void) -{ - pus_context_t ctx; - pus_init(&ctx); - ctx.tm_sink = test_sink; - return ctx; -} +#define g_buf th_buf +#define g_len th_len +#define make_ctx th_make_ctx static pus_tc_packet_t make_tc(uint8_t svc, uint8_t sub, uint16_t src) { @@ -131,9 +117,12 @@ static int test_emit_no_sink_ok(void) ASSERT_EQ_INT(PUS_STATUS_OK, pus_service_1_emit_success(&ctx, &tc, PUS_SUBTYPE_VERIFICATION_ACCEPTANCE_SUCCESS)); + ASSERT_EQ_INT(1, ctx.tm_counter); /* counter always increments */ + ASSERT_EQ_INT(PUS_STATUS_OK, pus_service_1_emit_failure(&ctx, &tc, PUS_SUBTYPE_VERIFICATION_ROUTING_FAILURE, 0u)); + ASSERT_EQ_INT(2, ctx.tm_counter); return 0; } diff --git a/tests/test_pus_service_17.c b/tests/test_pus_service_17.c index 7a21ae9..c981450 100644 --- a/tests/test_pus_service_17.c +++ b/tests/test_pus_service_17.c @@ -1,29 +1,15 @@ #include "cunit.h" #include "test_runners.h" +#include "test_helpers.h" #include "pus_service_17.h" #include "pus_context.h" #include "pus_handler.h" #include "pus_services.h" #include -static uint8_t g_buf[64]; -static uint16_t g_len = 0; - -static pus_status_t test_sink(void *ud, const uint8_t *data, uint16_t len) -{ - (void)ud; - memcpy(g_buf, data, len < sizeof(g_buf) ? len : sizeof(g_buf)); - g_len = len; - return PUS_STATUS_OK; -} - -static pus_context_t make_ctx(void) -{ - pus_context_t ctx; - pus_init(&ctx); - ctx.tm_sink = test_sink; - return ctx; -} +#define g_buf th_buf +#define g_len th_len +#define make_ctx th_make_ctx static int test_emit_alive_report(void) { @@ -67,18 +53,14 @@ static int test_handler_17_1_responds(void) ASSERT_EQ_INT(PUS_STATUS_OK, pus_service_17_register_handlers(&ctx)); - int idx = pus_handler_find(&ctx, PUS_SERVICE_TEST, - PUS_SUBTYPE_TEST_ARE_YOU_ALIVE); - ASSERT_TRUE(idx >= 0); - pus_tc_packet_t tc; memset(&tc, 0, sizeof(tc)); tc.sec_header.source_id = 0x0099u; g_len = 0; ASSERT_EQ_INT(PUS_STATUS_OK, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_TEST, + PUS_SUBTYPE_TEST_ARE_YOU_ALIVE, &tc)); /* handler must emit TM[17,2] back to TC source */ ASSERT_EQ_INT(PUS_TM_SEC_HEADER_LEN, g_len); @@ -94,10 +76,6 @@ static int test_handler_17_3_echoes_apid(void) pus_context_t ctx = make_ctx(); pus_service_17_register_handlers(&ctx); - int idx = pus_handler_find(&ctx, PUS_SERVICE_TEST, - PUS_SUBTYPE_TEST_ON_BOARD_CONNECTION); - ASSERT_TRUE(idx >= 0); - const uint8_t payload[] = { 0x04u, 0x2Au }; /* APID = 0x042A */ pus_tc_packet_t tc; memset(&tc, 0, sizeof(tc)); @@ -107,8 +85,8 @@ static int test_handler_17_3_echoes_apid(void) g_len = 0; ASSERT_EQ_INT(PUS_STATUS_OK, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_TEST, + PUS_SUBTYPE_TEST_ON_BOARD_CONNECTION, &tc)); ASSERT_EQ_INT(PUS_TM_SEC_HEADER_LEN + 2, g_len); ASSERT_EQ_INT(PUS_SUBTYPE_TEST_ON_BOARD_CONNECTION_REPORT, g_buf[2]); @@ -123,18 +101,14 @@ static int test_handler_17_3_bad_length(void) pus_context_t ctx = make_ctx(); pus_service_17_register_handlers(&ctx); - int idx = pus_handler_find(&ctx, PUS_SERVICE_TEST, - PUS_SUBTYPE_TEST_ON_BOARD_CONNECTION); - ASSERT_TRUE(idx >= 0); - pus_tc_packet_t tc; memset(&tc, 0, sizeof(tc)); tc.payload = NULL; tc.payload_len = 1u; /* too short */ ASSERT_EQ_INT(PUS_STATUS_BAD_LENGTH, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_TEST, + PUS_SUBTYPE_TEST_ON_BOARD_CONNECTION, &tc)); return 0; } diff --git a/tests/test_pus_service_20.c b/tests/test_pus_service_20.c index 8ed046a..10548c4 100644 --- a/tests/test_pus_service_20.c +++ b/tests/test_pus_service_20.c @@ -1,29 +1,15 @@ #include "cunit.h" #include "test_runners.h" +#include "test_helpers.h" #include "pus_service_20.h" #include "pus_context.h" #include "pus_handler.h" #include "pus_services.h" #include -static uint8_t g_buf[512]; -static uint16_t g_len = 0; - -static pus_status_t test_sink(void *ud, const uint8_t *data, uint16_t len) -{ - (void)ud; - memcpy(g_buf, data, len < sizeof(g_buf) ? len : sizeof(g_buf)); - g_len = len; - return PUS_STATUS_OK; -} - -static pus_context_t make_ctx(void) -{ - pus_context_t ctx; - pus_init(&ctx); - ctx.tm_sink = test_sink; - return ctx; -} +#define g_buf th_buf +#define g_len th_len +#define make_ctx th_make_ctx /* 4-byte big-endian parameter */ static uint32_t g_param_val = 0xDEADBEEFu; @@ -120,10 +106,6 @@ static int test_set_values_tc(void) ASSERT_EQ_INT(PUS_STATUS_OK, pus_service_20_register_handlers(&ctx, &s20)); - int idx = pus_handler_find(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_SET_VALUES); - ASSERT_TRUE(idx >= 0); - /* TC[20,3] payload: N=1, PID=0x0010, value=0xCAFEBABE */ const uint8_t payload[] = { 0x01u, @@ -137,8 +119,8 @@ static int test_set_values_tc(void) g_param_val = 0u; ASSERT_EQ_INT(PUS_STATUS_OK, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_SET_VALUES, &tc)); ASSERT_EQ_INT((int)0xCAFEBABEu, (int)g_param_val); return 0; @@ -154,10 +136,6 @@ static int test_readonly_param_returns_failed(void) param_getter, NULL, NULL); pus_service_20_register_handlers(&ctx, &s20); - int idx = pus_handler_find(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_SET_VALUES); - ASSERT_TRUE(idx >= 0); - const uint8_t payload[] = { 0x01u, 0x00u, 0x20u, 0x00u, 0x00u, 0x00u, 0x01u }; pus_tc_packet_t tc; memset(&tc, 0, sizeof(tc)); @@ -165,8 +143,8 @@ static int test_readonly_param_returns_failed(void) tc.payload_len = sizeof(payload); ASSERT_EQ_INT(PUS_STATUS_HANDLER_FAILED, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_SET_VALUES, &tc)); return 0; } @@ -306,10 +284,6 @@ static int test_tc_20_1_success(void) pus_service_20_register_param(&s20, 0x0001u, 4u, param_getter, NULL, NULL); pus_service_20_register_handlers(&ctx, &s20); - int idx = pus_handler_find(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_REPORT_VALUES); - ASSERT_TRUE(idx >= 0); - /* payload: N=1, PID=0x0001 */ const uint8_t payload[] = { 0x01u, 0x00u, 0x01u }; pus_tc_packet_t tc; @@ -318,8 +292,8 @@ static int test_tc_20_1_success(void) tc.payload_len = sizeof(payload); ASSERT_EQ_INT(PUS_STATUS_OK, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_REPORT_VALUES, &tc)); return 0; } @@ -330,17 +304,13 @@ static int test_tc_20_1_bad_length(void) pus_service_20_init(&s20); pus_service_20_register_handlers(&ctx, &s20); - int idx = pus_handler_find(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_REPORT_VALUES); - ASSERT_TRUE(idx >= 0); - pus_tc_packet_t tc; memset(&tc, 0, sizeof(tc)); tc.payload = NULL; tc.payload_len = 0u; ASSERT_EQ_INT(PUS_STATUS_BAD_LENGTH, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_REPORT_VALUES, &tc)); return 0; } @@ -351,18 +321,14 @@ static int test_tc_20_1_n_too_large(void) pus_service_20_init(&s20); pus_service_20_register_handlers(&ctx, &s20); - int idx = pus_handler_find(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_REPORT_VALUES); - ASSERT_TRUE(idx >= 0); - const uint8_t payload[] = { (uint8_t)(PUS_SERVICE_20_MAX_PARAMS + 1u) }; pus_tc_packet_t tc; memset(&tc, 0, sizeof(tc)); tc.payload = payload; tc.payload_len = sizeof(payload); ASSERT_EQ_INT(PUS_STATUS_BAD_LENGTH, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_REPORT_VALUES, &tc)); return 0; } @@ -373,10 +339,6 @@ static int test_tc_20_1_payload_too_short(void) pus_service_20_init(&s20); pus_service_20_register_handlers(&ctx, &s20); - int idx = pus_handler_find(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_REPORT_VALUES); - ASSERT_TRUE(idx >= 0); - /* N=2 but only 2 bytes total — need 1+2*2=5 */ const uint8_t payload[] = { 0x02u, 0x00u }; pus_tc_packet_t tc; @@ -384,8 +346,8 @@ static int test_tc_20_1_payload_too_short(void) tc.payload = payload; tc.payload_len = sizeof(payload); ASSERT_EQ_INT(PUS_STATUS_BAD_LENGTH, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_REPORT_VALUES, &tc)); return 0; } @@ -398,17 +360,13 @@ static int test_tc_20_3_bad_length(void) pus_service_20_init(&s20); pus_service_20_register_handlers(&ctx, &s20); - int idx = pus_handler_find(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_SET_VALUES); - ASSERT_TRUE(idx >= 0); - pus_tc_packet_t tc; memset(&tc, 0, sizeof(tc)); tc.payload = NULL; tc.payload_len = 0u; ASSERT_EQ_INT(PUS_STATUS_BAD_LENGTH, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_SET_VALUES, &tc)); return 0; } @@ -419,10 +377,6 @@ static int test_tc_20_3_truncated_pid(void) pus_service_20_init(&s20); pus_service_20_register_handlers(&ctx, &s20); - int idx = pus_handler_find(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_SET_VALUES); - ASSERT_TRUE(idx >= 0); - /* N=1 but only 2 bytes (N + 1 PID byte) — need N + PID(2) = 3 */ const uint8_t payload[] = { 0x01u, 0x00u }; pus_tc_packet_t tc; @@ -430,8 +384,8 @@ static int test_tc_20_3_truncated_pid(void) tc.payload = payload; tc.payload_len = sizeof(payload); ASSERT_EQ_INT(PUS_STATUS_BAD_LENGTH, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_SET_VALUES, &tc)); return 0; } @@ -442,18 +396,14 @@ static int test_tc_20_3_unknown_pid(void) pus_service_20_init(&s20); pus_service_20_register_handlers(&ctx, &s20); - int idx = pus_handler_find(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_SET_VALUES); - ASSERT_TRUE(idx >= 0); - const uint8_t payload[] = { 0x01u, 0xFFu, 0xFFu }; /* PID=0xFFFF not registered */ pus_tc_packet_t tc; memset(&tc, 0, sizeof(tc)); tc.payload = payload; tc.payload_len = sizeof(payload); ASSERT_EQ_INT(PUS_STATUS_NO_HANDLER, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_SET_VALUES, &tc)); return 0; } @@ -465,10 +415,6 @@ static int test_tc_20_3_truncated_value(void) pus_service_20_register_param(&s20, 0x0010u, 4u, param_getter, param_setter, NULL); pus_service_20_register_handlers(&ctx, &s20); - int idx = pus_handler_find(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_SET_VALUES); - ASSERT_TRUE(idx >= 0); - /* N=1, PID=0x0010, only 3 value bytes instead of required 4 */ const uint8_t payload[] = { 0x01u, 0x00u, 0x10u, 0x01u, 0x02u, 0x03u }; pus_tc_packet_t tc; @@ -476,8 +422,8 @@ static int test_tc_20_3_truncated_value(void) tc.payload = payload; tc.payload_len = sizeof(payload); ASSERT_EQ_INT(PUS_STATUS_BAD_LENGTH, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_SET_VALUES, &tc)); return 0; } @@ -490,18 +436,33 @@ static int test_tc_20_3_setter_failure(void) param_getter, failing_setter, NULL); pus_service_20_register_handlers(&ctx, &s20); - int idx = pus_handler_find(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, - PUS_SUBTYPE_PARAMETER_SET_VALUES); - ASSERT_TRUE(idx >= 0); - const uint8_t payload[] = { 0x01u, 0x00u, 0x10u, 0xCAu, 0xFEu, 0xBAu, 0xBEu }; pus_tc_packet_t tc; memset(&tc, 0, sizeof(tc)); tc.payload = payload; tc.payload_len = sizeof(payload); ASSERT_EQ_INT(PUS_STATUS_HANDLER_FAILED, - ctx.handler_table[idx].handler(&ctx, &tc, - ctx.handler_table[idx].user_data)); + pus_handler_invoke(&ctx, PUS_SERVICE_PARAMETER_MANAGEMENT, + PUS_SUBTYPE_PARAMETER_SET_VALUES, &tc)); + return 0; +} + +static int test_emit_report_count_zero(void) +{ + pus_context_t ctx = make_ctx(); + pus_service_20_ctx_t s20; + pus_service_20_init(&s20); + g_len = 0; + + /* count=0: payload is just the N byte; no param data */ + const uint16_t ids[] = { 0u }; /* content irrelevant — loop won't execute */ + ASSERT_EQ_INT(PUS_STATUS_OK, + pus_service_20_emit_report(&ctx, &s20, ids, 0u)); + + /* header(12) + N(1) = 13 bytes */ + ASSERT_EQ_INT(PUS_TM_SEC_HEADER_LEN + 1, g_len); + ASSERT_EQ_INT(0, g_buf[PUS_TM_SEC_HEADER_LEN]); /* N = 0 */ + ASSERT_EQ_INT(1, ctx.tm_counter); return 0; } @@ -532,6 +493,7 @@ pus_test_result_t test_pus_service_20_run_all(void) RUN_TEST(test_emit_report_getter_failure); RUN_TEST(test_emit_report_buffer_too_small); RUN_TEST(test_emit_report_no_sink); + RUN_TEST(test_emit_report_count_zero); RUN_TEST(test_tc_20_1_success); RUN_TEST(test_tc_20_1_bad_length); RUN_TEST(test_tc_20_1_n_too_large); diff --git a/tests/test_pus_service_3.c b/tests/test_pus_service_3.c index 97e95e3..4542e2e 100644 --- a/tests/test_pus_service_3.c +++ b/tests/test_pus_service_3.c @@ -1,28 +1,14 @@ #include "cunit.h" #include "test_runners.h" +#include "test_helpers.h" #include "pus_service_3.h" #include "pus_context.h" #include "pus_services.h" #include -static uint8_t g_buf[512]; -static uint16_t g_len = 0; - -static pus_status_t test_sink(void *ud, const uint8_t *data, uint16_t len) -{ - (void)ud; - memcpy(g_buf, data, len < sizeof(g_buf) ? len : sizeof(g_buf)); - g_len = len; - return PUS_STATUS_OK; -} - -static pus_context_t make_ctx(void) -{ - pus_context_t ctx; - pus_init(&ctx); - ctx.tm_sink = test_sink; - return ctx; -} +#define g_buf th_buf +#define g_len th_len +#define make_ctx th_make_ctx /* Provider that writes 2 bytes: 0xCA, 0xFE */ static pus_status_t simple_provider(uint16_t sid, uint8_t *buf, diff --git a/tests/test_pus_service_5.c b/tests/test_pus_service_5.c index 8400246..0d83edc 100644 --- a/tests/test_pus_service_5.c +++ b/tests/test_pus_service_5.c @@ -1,28 +1,14 @@ #include "cunit.h" #include "test_runners.h" +#include "test_helpers.h" #include "pus_service_5.h" #include "pus_context.h" #include "pus_services.h" #include -static uint8_t g_buf[512]; -static uint16_t g_len = 0; - -static pus_status_t test_sink(void *ud, const uint8_t *data, uint16_t len) -{ - (void)ud; - memcpy(g_buf, data, len < sizeof(g_buf) ? len : sizeof(g_buf)); - g_len = len; - return PUS_STATUS_OK; -} - -static pus_context_t make_ctx(void) -{ - pus_context_t ctx; - pus_init(&ctx); - ctx.tm_sink = test_sink; - return ctx; -} +#define g_buf th_buf +#define g_len th_len +#define make_ctx th_make_ctx static int test_emit_info(void) {