diff --git a/CMakeLists.txt b/CMakeLists.txt index b33f24d6..0a2eeefa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,7 @@ file(GLOB AWS_MQTT_PRIV_EXPOSED_HEADERS file(GLOB AWS_MQTT_SRC "source/*.c" "source/v5/*.c" + "source/request-response/*.c" ) file(GLOB MQTT_HEADERS diff --git a/include/aws/mqtt/mqtt.h b/include/aws/mqtt/mqtt.h index 974c4576..8e7cd0ef 100644 --- a/include/aws/mqtt/mqtt.h +++ b/include/aws/mqtt/mqtt.h @@ -81,6 +81,7 @@ enum aws_mqtt_error { AWS_ERROR_MQTT_CONNECTION_RESET_FOR_ADAPTER_CONNECT, AWS_ERROR_MQTT_CONNECTION_RESUBSCRIBE_NO_TOPICS, AWS_ERROR_MQTT_CONNECTION_SUBSCRIBE_FAILURE, + AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE, AWS_ERROR_END_MQTT_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_MQTT_PACKAGE_ID), }; diff --git a/include/aws/mqtt/private/client_impl.h b/include/aws/mqtt/private/client_impl.h index 1d0dd67a..ce041b2d 100644 --- a/include/aws/mqtt/private/client_impl.h +++ b/include/aws/mqtt/private/client_impl.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -255,6 +256,9 @@ struct aws_mqtt_client_connection_311_impl { aws_mqtt_on_operation_statistics_fn *on_any_operation_statistics; void *on_any_operation_statistics_ud; + /* listener callbacks */ + struct aws_mqtt311_callback_set_manager callback_manager; + /* Connection tasks. */ struct aws_mqtt_reconnect_task *reconnect_task; struct aws_channel_task ping_task; diff --git a/include/aws/mqtt/private/client_impl_shared.h b/include/aws/mqtt/private/client_impl_shared.h index d244cfe7..fa5cbeb3 100644 --- a/include/aws/mqtt/private/client_impl_shared.h +++ b/include/aws/mqtt/private/client_impl_shared.h @@ -10,6 +10,20 @@ struct aws_mqtt_client_connection; +/* + * Internal enum that indicates what type of struct the underlying impl pointer actually is. We use this + * to safely interact with private APIs on the implementation or extract the adapted 5 client directly, as + * necessary. + */ +enum aws_mqtt311_impl_type { + + /* 311 connection impl can be cast to `struct aws_mqtt_client_connection_311_impl` */ + AWS_MQTT311_IT_311_CONNECTION, + + /* 311 connection impl can be cast to `struct aws_mqtt_client_connection_5_impl`*/ + AWS_MQTT311_IT_5_ADAPTER, +}; + struct aws_mqtt_client_connection_vtable { struct aws_mqtt_client_connection *(*acquire_fn)(void *impl); @@ -107,6 +121,8 @@ struct aws_mqtt_client_connection_vtable { void *userdata); int (*get_stats_fn)(void *impl, struct aws_mqtt_connection_operation_statistics *stats); + + enum aws_mqtt311_impl_type (*get_impl_type)(const void *impl); }; struct aws_mqtt_client_connection { @@ -114,6 +130,9 @@ struct aws_mqtt_client_connection { void *impl; }; +AWS_MQTT_API enum aws_mqtt311_impl_type aws_mqtt_client_connection_get_impl_type( + const struct aws_mqtt_client_connection *connection); + AWS_MQTT_API uint64_t aws_mqtt_hash_uint16_t(const void *item); AWS_MQTT_API bool aws_mqtt_compare_uint16_t_eq(const void *a, const void *b); diff --git a/include/aws/mqtt/private/mqtt311_listener.h b/include/aws/mqtt/private/mqtt311_listener.h new file mode 100644 index 00000000..2a4f5b53 --- /dev/null +++ b/include/aws/mqtt/private/mqtt311_listener.h @@ -0,0 +1,180 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#ifndef AWS_MQTT_MQTT311_LISTENER_H +#define AWS_MQTT_MQTT311_LISTENER_H + +#include + +#include +#include + +AWS_PUSH_SANE_WARNING_LEVEL + +/** + * Callback signature for when an mqtt311 listener has completely destroyed itself. + */ +typedef void(aws_mqtt311_listener_termination_completion_fn)(void *complete_ctx); + +/** + * A record that tracks MQTT311 client connection callbacks which can be dynamically injected via a listener. + * + * All the callbacks that are supported here are invoked only on the 311 connection's event loop. With the + * add/remove callback set also on the event loop, everything is correctly serialized without data races. + * + * If binding additional callbacks, they must only be invoked from the connection's event loop. + * + * We only listen to connection-success because the only connection-level event we care about is a failure + * to rejoin a session (which invalidates all subscriptions that were considered valid) + */ +struct aws_mqtt311_callback_set { + + /* Called from s_packet_handler_publish which is event-loop invoked */ + aws_mqtt_client_publish_received_fn *publish_received_handler; + + /* Called from s_packet_handler_connack which is event-loop invoked */ + aws_mqtt_client_on_connection_success_fn *connection_success_handler; + + void *user_data; +}; + +/** + * An internal type for managing chains of callbacks attached to an mqtt311 client connection. Supports chains for + * lifecycle events and incoming publish packet handling. + * + * Assumed to be owned and used only by an MQTT311 client connection. + */ +struct aws_mqtt311_callback_set_manager { + struct aws_allocator *allocator; + + struct aws_mqtt_client_connection *connection; + + struct aws_linked_list callback_set_entries; + + uint64_t next_callback_set_entry_id; +}; + +/** + * Configuration options for MQTT311 listener objects. + */ +struct aws_mqtt311_listener_config { + + /** + * MQTT311 client connection to listen to events on + */ + struct aws_mqtt_client_connection *connection; + + /** + * Callbacks to invoke when events occur on the MQTT311 client connection + */ + struct aws_mqtt311_callback_set listener_callbacks; + + /** + * Listener destruction is asynchronous and thus requires a termination callback and associated user data + * to notify the user that the listener has been fully destroyed and no further events will be received. + */ + aws_mqtt311_listener_termination_completion_fn *termination_callback; + void *termination_callback_user_data; +}; + +AWS_EXTERN_C_BEGIN + +/** + * Creates a new MQTT311 listener object. For as long as the listener lives, incoming publishes and lifecycle events + * will be forwarded to the callbacks configured on the listener. + * + * @param allocator allocator to use + * @param config listener configuration + * @return a new aws_mqtt311_listener object + */ +AWS_MQTT_API struct aws_mqtt311_listener *aws_mqtt311_listener_new( + struct aws_allocator *allocator, + struct aws_mqtt311_listener_config *config); + +/** + * Adds a reference to an mqtt311 listener. + * + * @param listener listener to add a reference to + * @return the listener object + */ +AWS_MQTT_API struct aws_mqtt311_listener *aws_mqtt311_listener_acquire(struct aws_mqtt311_listener *listener); + +/** + * Removes a reference to an mqtt311 listener. When the reference count drops to zero, the listener's asynchronous + * destruction will be started. + * + * @param listener listener to remove a reference from + * @return NULL + */ +AWS_MQTT_API struct aws_mqtt311_listener *aws_mqtt311_listener_release(struct aws_mqtt311_listener *listener); + +/** + * Initializes a callback set manager + */ +AWS_MQTT_API +void aws_mqtt311_callback_set_manager_init( + struct aws_mqtt311_callback_set_manager *manager, + struct aws_allocator *allocator, + struct aws_mqtt_client_connection *connection); + +/** + * Cleans up a callback set manager. + * + * aws_mqtt311_callback_set_manager_init must have been previously called or this will crash. + */ +AWS_MQTT_API +void aws_mqtt311_callback_set_manager_clean_up(struct aws_mqtt311_callback_set_manager *manager); + +/** + * Adds a callback set to the front of the handler chain. Returns an integer id that can be used to selectively + * remove the callback set from the manager. + * + * May only be called on the client's event loop thread. + */ +AWS_MQTT_API +uint64_t aws_mqtt311_callback_set_manager_push_front( + struct aws_mqtt311_callback_set_manager *manager, + struct aws_mqtt311_callback_set *callback_set); + +/** + * Removes a callback set from the handler chain. + * + * May only be called on the client's event loop thread. + */ +AWS_MQTT_API +void aws_mqtt311_callback_set_manager_remove( + struct aws_mqtt311_callback_set_manager *manager, + uint64_t callback_set_id); + +/** + * Walks the incoming publish handler chain for an MQTT311 connection, invoking each in sequence. + * + * May only be called on the client's event loop thread. + */ +AWS_MQTT_API +void aws_mqtt311_callback_set_manager_on_publish_received( + struct aws_mqtt311_callback_set_manager *manager, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain); + +/** + * Invokes a connection success event on each listener in the manager's collection of callback sets. + * + * May only be called on the client's event loop thread. + */ +AWS_MQTT_API +void aws_mqtt311_callback_set_manager_on_connection_success( + struct aws_mqtt311_callback_set_manager *manager, + enum aws_mqtt_connect_return_code return_code, + bool rejoined_session); + +AWS_EXTERN_C_END + +AWS_POP_SANE_WARNING_LEVEL + +#endif /* AWS_MQTT_MQTT311_LISTENER_H */ diff --git a/include/aws/mqtt/private/request-response/protocol_adapter.h b/include/aws/mqtt/private/request-response/protocol_adapter.h new file mode 100644 index 00000000..f09d6135 --- /dev/null +++ b/include/aws/mqtt/private/request-response/protocol_adapter.h @@ -0,0 +1,185 @@ +#ifndef AWS_MQTT_PRIVATE_REQUEST_RESPONSE_PROTOCOL_ADAPTER_H +#define AWS_MQTT_PRIVATE_REQUEST_RESPONSE_PROTOCOL_ADAPTER_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include + +#include + +struct aws_allocator; +struct aws_mqtt_client_connection; +struct aws_mqtt5_client; + +/* + * The request-response protocol adapter is a translation layer that sits between the request-response native client + * implementation and a protocol client capable of subscribing, unsubscribing, and publishing MQTT messages. + * Valid protocol clients include the CRT MQTT5 client, the CRT MQTT311 client, and an eventstream RPC connection + * that belongs to a Greengrass IPC client. Each of these protocol clients has a different (or even implicit) + * contract for carrying out pub-sub operations. The protocol adapter abstracts these details with a simple, + * minimal interface based on the requirements identified in the request-response design documents. + */ + +/* + * Minimal MQTT subscribe options + */ +struct aws_protocol_adapter_subscribe_options { + struct aws_byte_cursor topic_filter; + uint32_t ack_timeout_seconds; +}; + +/* + * Minimal MQTT unsubscribe options + */ +struct aws_protocol_adapter_unsubscribe_options { + struct aws_byte_cursor topic_filter; + uint32_t ack_timeout_seconds; +}; + +/* + * Minimal MQTT publish options + */ +struct aws_protocol_adapter_publish_options { + struct aws_byte_cursor topic; + struct aws_byte_cursor payload; + uint32_t ack_timeout_seconds; + + /* + * Invoked on success/failure of the publish itself. Our implementations use QoS1 which means that success + * will be on puback receipt. + */ + void (*completion_callback_fn)(int, void *); + + /* + * User data to pass in when invoking the completion callback + */ + void *user_data; +}; + +/* + * Describes the type of subscription event (relative to a topic filter) + */ +enum aws_protocol_adapter_subscription_event_type { + AWS_PASET_SUBSCRIBE, + AWS_PASET_UNSUBSCRIBE, +}; + +/* + * An event emitted by the protocol adapter when a subscribe or unsubscribe is completed by the adapted protocol + * client. + */ +struct aws_protocol_adapter_subscription_event { + struct aws_byte_cursor topic_filter; + enum aws_protocol_adapter_subscription_event_type event_type; + int error_code; +}; + +/* + * An event emitted by the protocol adapter whenever a publish is received by the protocol client. This will + * potentially include messages that are completely unrelated to MQTT request-response. The topic is the first + * thing that should be checked for relevance. + */ +struct aws_protocol_adapter_incoming_publish_event { + struct aws_byte_cursor topic; + struct aws_byte_cursor payload; +}; + +/* + * An event emitted by the protocol adapter whenever the protocol client successfully reconnects to the broker. + */ +struct aws_protocol_adapter_session_event { + bool joined_session; +}; + +typedef void( + aws_protocol_adapter_subscription_event_fn)(struct aws_protocol_adapter_subscription_event *event, void *user_data); +typedef void(aws_protocol_adapter_incoming_publish_fn)( + struct aws_protocol_adapter_incoming_publish_event *publish, + void *user_data); +typedef void(aws_protocol_adapter_terminate_callback_fn)(void *user_data); +typedef void(aws_protocol_adapter_session_event_fn)(struct aws_protocol_adapter_session_event *event, void *user_data); + +/* + * Set of callbacks invoked by the protocol adapter. These must all be set. + */ +struct aws_mqtt_protocol_adapter_options { + aws_protocol_adapter_subscription_event_fn *subscription_event_callback; + aws_protocol_adapter_incoming_publish_fn *incoming_publish_callback; + aws_protocol_adapter_terminate_callback_fn *terminate_callback; + aws_protocol_adapter_session_event_fn *session_event_callback; + + /* + * User data to pass into all singleton protocol adapter callbacks. Likely either the request-response client + * or the subscription manager component of the request-response client. + */ + void *user_data; +}; + +struct aws_mqtt_protocol_adapter_vtable { + + void (*aws_mqtt_protocol_adapter_destroy_fn)(void *); + + int (*aws_mqtt_protocol_adapter_subscribe_fn)(void *, struct aws_protocol_adapter_subscribe_options *); + + int (*aws_mqtt_protocol_adapter_unsubscribe_fn)(void *, struct aws_protocol_adapter_unsubscribe_options *); + + int (*aws_mqtt_protocol_adapter_publish_fn)(void *, struct aws_protocol_adapter_publish_options *); +}; + +struct aws_mqtt_protocol_adapter { + const struct aws_mqtt_protocol_adapter_vtable *vtable; + void *impl; +}; + +AWS_EXTERN_C_BEGIN + +/* + * Creates a new request-response protocol adapter from an MQTT311 client + */ +AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311( + struct aws_allocator *allocator, + struct aws_mqtt_protocol_adapter_options *options, + struct aws_mqtt_client_connection *connection); + +/* + * Creates a new request-response protocol adapter from an MQTT5 client + */ +AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5( + struct aws_allocator *allocator, + struct aws_mqtt_protocol_adapter_options *options, + struct aws_mqtt5_client *client); + +/* + * Destroys a request-response protocol adapter. Destruction is an asynchronous process and the caller must + * wait for the termination callback to be invoked before assuming that no further callbacks will be invoked. + */ +AWS_MQTT_API void aws_mqtt_protocol_adapter_destroy(struct aws_mqtt_protocol_adapter *adapter); + +/* + * Asks the adapted protocol client to perform an MQTT subscribe operation + */ +AWS_MQTT_API int aws_mqtt_protocol_adapter_subscribe( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_subscribe_options *options); + +/* + * Asks the adapted protocol client to perform an MQTT unsubscribe operation + */ +AWS_MQTT_API int aws_mqtt_protocol_adapter_unsubscribe( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_unsubscribe_options *options); + +/* + * Asks the adapted protocol client to perform an MQTT publish operation + */ +AWS_MQTT_API int aws_mqtt_protocol_adapter_publish( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_publish_options *options); + +AWS_EXTERN_C_END + +#endif /* AWS_MQTT_PRIVATE_REQUEST_RESPONSE_PROTOCOL_ADAPTER_H */ diff --git a/include/aws/mqtt/private/request-response/weak_ref.h b/include/aws/mqtt/private/request-response/weak_ref.h new file mode 100644 index 00000000..cff0a2d8 --- /dev/null +++ b/include/aws/mqtt/private/request-response/weak_ref.h @@ -0,0 +1,66 @@ +#ifndef AWS_MQTT_PRIVATE_REQUEST_RESPONSE_WEAK_REF_H +#define AWS_MQTT_PRIVATE_REQUEST_RESPONSE_WEAK_REF_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include + +/* + * This is a simplification of the notion of a weak reference particular to the needs of the request-response + * MQTT service clients. This is not suitable for general use but could be extended + * for general use in the future. Until then, it stays private, here. + * + * This weak reference is a ref-counted object with an opaque value. The opaque value may be cleared or + * queried. These two operations *do not* provide any thread safety. + * + * The primary use is to allow one object to safely use asynchronous callback-driven APIs on a second object, despite + * the fact that the first object may get destroyed unpredictably. The two objects must be exclusive to a single + * event loop (because there's no thread safety or mutual exclusion on the opaque value held by the weak ref). + * + * The initial use is the request-response protocol adapter submitting operations to an MQTT client or an + * eventstream RPC connection. We use a single weak ref to the protocol adapter and zero its opaque value when + * the protocol adapter is destroyed. Operation callbacks that subsequently resolve can then short circuit and do + * nothing rather than call into garbage and crash. + * + * We use this rather than explicitly tracking and zeroing all pending operations (like the 3-to-5 adapter does) + * because this approach is simpler and our usage does not require any of these callbacks to be invoked once the + * request-response client is destroyed. + */ +struct aws_weak_ref; + +AWS_EXTERN_C_BEGIN + +/* + * Creates a new weak reference to an opaque value. + */ +AWS_MQTT_API struct aws_weak_ref *aws_weak_ref_new(struct aws_allocator *allocator, void *referenced); + +/* + * Acquires a reference to the weak ref object. + */ +AWS_MQTT_API struct aws_weak_ref *aws_weak_ref_acquire(struct aws_weak_ref *weak_ref); + +/* + * Removes a reference to the weak ref object. When the last reference is removed, the weak ref object will be + * destroyed. This has no effect on the opaque value held by the weak ref. + */ +AWS_MQTT_API struct aws_weak_ref *aws_weak_ref_release(struct aws_weak_ref *weak_ref); + +/* + * Gets the current value of the opaque data held by the weak ref. + */ +AWS_MQTT_API void *aws_weak_ref_get_reference(struct aws_weak_ref *weak_ref); + +/* + * Clears the opaque data held by the weak ref. + */ +AWS_MQTT_API void aws_weak_ref_zero_reference(struct aws_weak_ref *weak_ref); + +AWS_EXTERN_C_END + +#endif /* AWS_MQTT_PRIVATE_REQUEST_RESPONSE_WEAK_REF_H */ diff --git a/include/aws/mqtt/v5/mqtt5_client.h b/include/aws/mqtt/v5/mqtt5_client.h index d04d2981..5cac371c 100644 --- a/include/aws/mqtt/v5/mqtt5_client.h +++ b/include/aws/mqtt/v5/mqtt5_client.h @@ -341,6 +341,7 @@ struct aws_mqtt5_publish_completion_options { aws_mqtt5_publish_completion_fn *completion_callback; void *completion_user_data; + /** Overrides the client's ack timeout with this value, for this operation only */ uint32_t ack_timeout_seconds_override; }; @@ -351,6 +352,7 @@ struct aws_mqtt5_subscribe_completion_options { aws_mqtt5_subscribe_completion_fn *completion_callback; void *completion_user_data; + /** Overrides the client's ack timeout with this value, for this operation only */ uint32_t ack_timeout_seconds_override; }; @@ -361,6 +363,7 @@ struct aws_mqtt5_unsubscribe_completion_options { aws_mqtt5_unsubscribe_completion_fn *completion_callback; void *completion_user_data; + /** Overrides the client's ack timeout with this value, for this operation only */ uint32_t ack_timeout_seconds_override; }; diff --git a/source/client.c b/source/client.c index c332c4da..8a8bc386 100644 --- a/source/client.c +++ b/source/client.c @@ -259,6 +259,7 @@ static void s_mqtt_client_shutdown( (void)channel; struct aws_mqtt_client_connection_311_impl *connection = user_data; + AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(connection->loop)); AWS_LOGF_TRACE( AWS_LS_MQTT_CLIENT, "id=%p: Channel has been shutdown with error code %d", (void *)connection, error_code); @@ -801,6 +802,8 @@ static void s_mqtt_client_connection_destroy_final(struct aws_mqtt_client_connec termination_handler_user_data = connection->on_termination_ud; } + aws_mqtt311_callback_set_manager_clean_up(&connection->callback_manager); + /* If the reconnect_task isn't freed, free it */ if (connection->reconnect_task) { aws_mem_release(connection->reconnect_task->allocator, connection->reconnect_task); @@ -3220,6 +3223,12 @@ static void s_aws_mqtt_client_connection_311_release(void *impl) { aws_ref_count_release(&connection->ref_count); } +enum aws_mqtt311_impl_type s_aws_mqtt_client_connection_3_get_impl(const void *impl) { + (void)impl; + + return AWS_MQTT311_IT_311_CONNECTION; +} + static struct aws_mqtt_client_connection_vtable s_aws_mqtt_client_connection_311_vtable = { .acquire_fn = s_aws_mqtt_client_connection_311_acquire, .release_fn = s_aws_mqtt_client_connection_311_release, @@ -3243,6 +3252,7 @@ static struct aws_mqtt_client_connection_vtable s_aws_mqtt_client_connection_311 .unsubscribe_fn = s_aws_mqtt_client_connection_311_unsubscribe, .publish_fn = s_aws_mqtt_client_connection_311_publish, .get_stats_fn = s_aws_mqtt_client_connection_311_get_stats, + .get_impl_type = s_aws_mqtt_client_connection_3_get_impl, }; static struct aws_mqtt_client_connection_vtable *s_aws_mqtt_client_connection_311_vtable_ptr = @@ -3344,6 +3354,8 @@ struct aws_mqtt_client_connection *aws_mqtt_client_connection_new(struct aws_mqt connection->handler.vtable = aws_mqtt_get_client_channel_vtable(); connection->handler.impl = connection; + aws_mqtt311_callback_set_manager_init(&connection->callback_manager, connection->allocator, &connection->base); + return &connection->base; failed_init_outstanding_requests_table: diff --git a/source/client_channel_handler.c b/source/client_channel_handler.c index 543f0950..b95a43fc 100644 --- a/source/client_channel_handler.c +++ b/source/client_channel_handler.c @@ -263,6 +263,9 @@ static int s_packet_handler_connack(struct aws_byte_cursor message_cursor, void MQTT_CLIENT_CALL_CALLBACK_ARGS( connection, on_connection_success, connack.connect_return_code, connack.session_present); + aws_mqtt311_callback_set_manager_on_connection_success( + &connection->callback_manager, connack.connect_return_code, connack.session_present); + AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: connection callback completed", (void *)connection); s_update_next_ping_time(connection); @@ -291,6 +294,9 @@ static int s_packet_handler_publish(struct aws_byte_cursor message_cursor, void MQTT_CLIENT_CALL_CALLBACK_ARGS(connection, on_any_publish, &publish.topic_name, &publish.payload, dup, qos, retain); + aws_mqtt311_callback_set_manager_on_publish_received( + &connection->callback_manager, &publish.topic_name, &publish.payload, dup, qos, retain); + AWS_LOGF_TRACE( AWS_LS_MQTT_CLIENT, "id=%p: publish received with msg id=%" PRIu16 " dup=%d qos=%d retain=%d payload-size=%zu topic=" PRInSTR, diff --git a/source/client_impl_shared.c b/source/client_impl_shared.c index 6f65eb88..525fb7e4 100644 --- a/source/client_impl_shared.c +++ b/source/client_impl_shared.c @@ -204,6 +204,11 @@ int aws_mqtt_client_connection_get_stats( return (*connection->vtable->get_stats_fn)(connection->impl, stats); } +enum aws_mqtt311_impl_type aws_mqtt_client_connection_get_impl_type( + const struct aws_mqtt_client_connection *connection) { + return (*connection->vtable->get_impl_type)(connection->impl); +} + uint64_t aws_mqtt_hash_uint16_t(const void *item) { return *(uint16_t *)item; } diff --git a/source/mqtt.c b/source/mqtt.c index c87ec0cc..fd16340a 100644 --- a/source/mqtt.c +++ b/source/mqtt.c @@ -233,6 +233,9 @@ bool aws_mqtt_is_valid_topic_filter(const struct aws_byte_cursor *topic_filter) AWS_DEFINE_ERROR_INFO_MQTT( AWS_ERROR_MQTT_CONNECTION_SUBSCRIBE_FAILURE, "MQTT subscribe operation failed"), + AWS_DEFINE_ERROR_INFO_MQTT( + AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE, + "MQTT operation returned a failing reason code"), }; /* clang-format on */ #undef AWS_DEFINE_ERROR_INFO_MQTT diff --git a/source/mqtt311_listener.c b/source/mqtt311_listener.c new file mode 100644 index 00000000..caf9b650 --- /dev/null +++ b/source/mqtt311_listener.c @@ -0,0 +1,291 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +static struct aws_event_loop *s_mqtt_client_connection_get_event_loop( + const struct aws_mqtt_client_connection *connection) { + AWS_FATAL_ASSERT(aws_mqtt_client_connection_get_impl_type(connection) == AWS_MQTT311_IT_311_CONNECTION); + + struct aws_mqtt_client_connection_311_impl *connection_impl = connection->impl; + + return connection_impl->loop; +} + +struct aws_mqtt311_listener { + struct aws_allocator *allocator; + + struct aws_ref_count ref_count; + + struct aws_mqtt311_listener_config config; + + uint64_t callback_set_id; + + struct aws_task initialize_task; + struct aws_task terminate_task; +}; + +static void s_mqtt311_listener_destroy(struct aws_mqtt311_listener *listener) { + + aws_mqtt_client_connection_release(listener->config.connection); + + aws_mqtt311_listener_termination_completion_fn *termination_callback = listener->config.termination_callback; + void *termination_callback_user_data = listener->config.termination_callback_user_data; + + aws_mem_release(listener->allocator, listener); + + if (termination_callback != NULL) { + (*termination_callback)(termination_callback_user_data); + } +} + +static void s_mqtt311_listener_initialize_task_fn(struct aws_task *task, void *arg, enum aws_task_status task_status) { + (void)task; + + struct aws_mqtt311_listener *listener = arg; + + if (task_status == AWS_TASK_STATUS_RUN_READY) { + struct aws_mqtt_client_connection_311_impl *connection_impl = listener->config.connection->impl; + listener->callback_set_id = aws_mqtt311_callback_set_manager_push_front( + &connection_impl->callback_manager, &listener->config.listener_callbacks); + AWS_LOGF_INFO( + AWS_LS_MQTT_GENERAL, + "id=%p: Mqtt311 Listener initialized, listener id=%p", + (void *)listener->config.connection, + (void *)listener); + aws_mqtt311_listener_release(listener); + } else { + s_mqtt311_listener_destroy(listener); + } +} + +static void s_mqtt311_listener_terminate_task_fn(struct aws_task *task, void *arg, enum aws_task_status task_status) { + (void)task; + + struct aws_mqtt311_listener *listener = arg; + + if (task_status == AWS_TASK_STATUS_RUN_READY) { + struct aws_mqtt_client_connection_311_impl *connection_impl = listener->config.connection->impl; + aws_mqtt311_callback_set_manager_remove(&connection_impl->callback_manager, listener->callback_set_id); + } + + AWS_LOGF_INFO( + AWS_LS_MQTT_GENERAL, + "id=%p: Mqtt311 Listener terminated, listener id=%p", + (void *)listener->config.connection, + (void *)listener); + + s_mqtt311_listener_destroy(listener); +} + +static void s_aws_mqtt311_listener_on_zero_ref_count(void *context) { + struct aws_mqtt311_listener *listener = context; + + aws_event_loop_schedule_task_now( + s_mqtt_client_connection_get_event_loop(listener->config.connection), &listener->terminate_task); +} + +struct aws_mqtt311_listener *aws_mqtt311_listener_new( + struct aws_allocator *allocator, + struct aws_mqtt311_listener_config *config) { + if (config->connection == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + if (aws_mqtt_client_connection_get_impl_type(config->connection) != AWS_MQTT311_IT_311_CONNECTION) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_mqtt311_listener *listener = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt311_listener)); + + listener->allocator = allocator; + listener->config = *config; + + aws_mqtt_client_connection_acquire(config->connection); + aws_ref_count_init(&listener->ref_count, listener, s_aws_mqtt311_listener_on_zero_ref_count); + + aws_task_init( + &listener->initialize_task, s_mqtt311_listener_initialize_task_fn, listener, "Mqtt311ListenerInitialize"); + aws_task_init( + &listener->terminate_task, s_mqtt311_listener_terminate_task_fn, listener, "Mqtt311ListenerTerminate"); + + aws_mqtt311_listener_acquire(listener); + aws_event_loop_schedule_task_now( + s_mqtt_client_connection_get_event_loop(config->connection), &listener->initialize_task); + + return listener; +} + +struct aws_mqtt311_listener *aws_mqtt311_listener_acquire(struct aws_mqtt311_listener *listener) { + if (listener != NULL) { + aws_ref_count_acquire(&listener->ref_count); + } + + return listener; +} + +struct aws_mqtt311_listener *aws_mqtt311_listener_release(struct aws_mqtt311_listener *listener) { + if (listener != NULL) { + aws_ref_count_release(&listener->ref_count); + } + + return NULL; +} + +struct aws_mqtt311_callback_set_entry { + struct aws_allocator *allocator; + + struct aws_linked_list_node node; + + uint64_t id; + + struct aws_mqtt311_callback_set callbacks; +}; + +void aws_mqtt311_callback_set_manager_init( + struct aws_mqtt311_callback_set_manager *manager, + struct aws_allocator *allocator, + struct aws_mqtt_client_connection *connection) { + + manager->allocator = allocator; + manager->connection = connection; /* no need to ref count, this is assumed to be owned by the client connection */ + manager->next_callback_set_entry_id = 1; + + aws_linked_list_init(&manager->callback_set_entries); +} + +void aws_mqtt311_callback_set_manager_clean_up(struct aws_mqtt311_callback_set_manager *manager) { + struct aws_linked_list_node *node = aws_linked_list_begin(&manager->callback_set_entries); + while (node != aws_linked_list_end(&manager->callback_set_entries)) { + struct aws_mqtt311_callback_set_entry *entry = + AWS_CONTAINER_OF(node, struct aws_mqtt311_callback_set_entry, node); + node = aws_linked_list_next(node); + + aws_linked_list_remove(&entry->node); + aws_mem_release(entry->allocator, entry); + } +} + +static struct aws_mqtt311_callback_set_entry *s_new_311_callback_set_entry( + struct aws_mqtt311_callback_set_manager *manager, + struct aws_mqtt311_callback_set *callback_set) { + struct aws_mqtt311_callback_set_entry *entry = + aws_mem_calloc(manager->allocator, 1, sizeof(struct aws_mqtt311_callback_set_entry)); + + entry->allocator = manager->allocator; + entry->id = manager->next_callback_set_entry_id++; + entry->callbacks = *callback_set; + + AWS_LOGF_INFO( + AWS_LS_MQTT_GENERAL, + "id=%p: MQTT311 callback manager created new entry :%" PRIu64, + (void *)manager->connection, + entry->id); + + return entry; +} + +uint64_t aws_mqtt311_callback_set_manager_push_front( + struct aws_mqtt311_callback_set_manager *manager, + struct aws_mqtt311_callback_set *callback_set) { + + AWS_FATAL_ASSERT( + aws_event_loop_thread_is_callers_thread(s_mqtt_client_connection_get_event_loop(manager->connection))); + + struct aws_mqtt311_callback_set_entry *entry = s_new_311_callback_set_entry(manager, callback_set); + + aws_linked_list_push_front(&manager->callback_set_entries, &entry->node); + + return entry->id; +} + +void aws_mqtt311_callback_set_manager_remove( + struct aws_mqtt311_callback_set_manager *manager, + uint64_t callback_set_id) { + + AWS_FATAL_ASSERT( + aws_event_loop_thread_is_callers_thread(s_mqtt_client_connection_get_event_loop(manager->connection))); + + struct aws_linked_list_node *node = aws_linked_list_begin(&manager->callback_set_entries); + while (node != aws_linked_list_end(&manager->callback_set_entries)) { + struct aws_mqtt311_callback_set_entry *entry = + AWS_CONTAINER_OF(node, struct aws_mqtt311_callback_set_entry, node); + node = aws_linked_list_next(node); + + if (entry->id == callback_set_id) { + aws_linked_list_remove(&entry->node); + + AWS_LOGF_INFO( + AWS_LS_MQTT_GENERAL, + "id=%p: MQTT311 callback manager removed entry id=%" PRIu64, + (void *)manager->connection, + entry->id); + aws_mem_release(entry->allocator, entry); + return; + } + } + AWS_LOGF_INFO( + AWS_LS_MQTT_GENERAL, + "id=%p: MQTT311 callback manager failed to remove entry id=%" PRIu64 ", callback set id not found.", + (void *)manager->connection, + callback_set_id); +} + +void aws_mqtt311_callback_set_manager_on_publish_received( + struct aws_mqtt311_callback_set_manager *manager, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain) { + + struct aws_mqtt_client_connection_311_impl *connection_impl = manager->connection->impl; + AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(connection_impl->loop)); + + struct aws_linked_list_node *node = aws_linked_list_begin(&manager->callback_set_entries); + while (node != aws_linked_list_end(&manager->callback_set_entries)) { + struct aws_mqtt311_callback_set_entry *entry = + AWS_CONTAINER_OF(node, struct aws_mqtt311_callback_set_entry, node); + node = aws_linked_list_next(node); + + struct aws_mqtt311_callback_set *callback_set = &entry->callbacks; + if (callback_set->publish_received_handler != NULL) { + (*callback_set->publish_received_handler)( + manager->connection, topic, payload, dup, qos, retain, callback_set->user_data); + } + } +} + +void aws_mqtt311_callback_set_manager_on_connection_success( + struct aws_mqtt311_callback_set_manager *manager, + enum aws_mqtt_connect_return_code return_code, + bool rejoined_session) { + + struct aws_mqtt_client_connection_311_impl *connection_impl = manager->connection->impl; + AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(connection_impl->loop)); + + struct aws_linked_list_node *node = aws_linked_list_begin(&manager->callback_set_entries); + while (node != aws_linked_list_end(&manager->callback_set_entries)) { + struct aws_mqtt311_callback_set_entry *entry = + AWS_CONTAINER_OF(node, struct aws_mqtt311_callback_set_entry, node); + node = aws_linked_list_next(node); + + struct aws_mqtt311_callback_set *callback_set = &entry->callbacks; + if (callback_set->connection_success_handler != NULL) { + (*callback_set->connection_success_handler)( + manager->connection, return_code, rejoined_session, callback_set->user_data); + } + } +} diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c new file mode 100644 index 00000000..68cc8667 --- /dev/null +++ b/source/request-response/protocol_adapter.c @@ -0,0 +1,427 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include +#include +#include +#include +#include + +/* + * Basic API contract + * + * Invariant 1: Subscribe is only called from the RR subscription manager when going from 0 to 1 pending operations + * Invariant 2: Unsubscribe is only called from the RR subscription manager when there are 0 pending operations, not + * necessarily on the exact transition to zero though. + * + * Additional Notes + * + * Entries are not tracked with the exception of eventstream impl which needs the stream handles to close. + * + * A subscribe failure does not trigger an unsubscribe, a status event. + * + * The sub manager is responsible for calling Unsubscribe on all its entries when shutting down + * (before releasing hold of the adapter). + * + * Retries, when appropriate, are the responsibility of the caller. + */ + +struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311( + struct aws_allocator *allocator, + struct aws_mqtt_protocol_adapter_options *options, + struct aws_mqtt_client_connection *connection) { + (void)allocator; + (void)options; + (void)connection; + + // TODO + return NULL; +} + +/******************************************************************************************************************/ + +struct aws_mqtt_protocol_adapter_5_impl { + struct aws_allocator *allocator; + struct aws_mqtt_protocol_adapter base; + struct aws_weak_ref *callback_ref; + struct aws_mqtt_protocol_adapter_options config; + + struct aws_event_loop *loop; + struct aws_mqtt5_client *client; + struct aws_mqtt5_listener *listener; +}; + +static void s_aws_mqtt_protocol_adapter_5_destroy(void *impl) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; + + // all the real cleanup is done in the listener termination callback + aws_mqtt5_listener_release(adapter->listener); +} + +// used by both subscribe and unsubscribe +struct aws_mqtt_protocol_adapter_5_subscription_op_data { + struct aws_allocator *allocator; + + struct aws_byte_buf topic_filter; + struct aws_weak_ref *callback_ref; +}; + +static struct aws_mqtt_protocol_adapter_5_subscription_op_data *s_aws_mqtt_protocol_adapter_5_subscription_op_data_new( + struct aws_allocator *allocator, + struct aws_byte_cursor topic_filter, + struct aws_weak_ref *callback_ref) { + struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = + aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_subscription_op_data)); + + subscribe_data->allocator = allocator; + subscribe_data->callback_ref = aws_weak_ref_acquire(callback_ref); + aws_byte_buf_init_copy_from_cursor(&subscribe_data->topic_filter, allocator, topic_filter); + + return subscribe_data; +} + +static void s_aws_mqtt_protocol_adapter_5_subscription_op_data_destroy( + struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data) { + aws_weak_ref_release(subscribe_data->callback_ref); + aws_byte_buf_clean_up(&subscribe_data->topic_filter); + + aws_mem_release(subscribe_data->allocator, subscribe_data); +} + +/* Subscribe */ + +static void s_protocol_adapter_5_subscribe_completion( + const struct aws_mqtt5_packet_suback_view *suback, + int error_code, + void *complete_ctx) { + struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = complete_ctx; + struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_weak_ref_get_reference(subscribe_data->callback_ref); + + if (adapter == NULL) { + goto done; + } + + if (error_code == AWS_ERROR_SUCCESS) { + if (suback == NULL || suback->reason_code_count != 1 || suback->reason_codes[0] >= 128) { + error_code = AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE; + } + } + + struct aws_protocol_adapter_subscription_event subscribe_event = { + .topic_filter = aws_byte_cursor_from_buf(&subscribe_data->topic_filter), + .event_type = AWS_PASET_SUBSCRIBE, + .error_code = error_code, + }; + + (*adapter->config.subscription_event_callback)(&subscribe_event, adapter->config.user_data); + +done: + + s_aws_mqtt_protocol_adapter_5_subscription_op_data_destroy(subscribe_data); +} + +int s_aws_mqtt_protocol_adapter_5_subscribe(void *impl, struct aws_protocol_adapter_subscribe_options *options) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; + + struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = + s_aws_mqtt_protocol_adapter_5_subscription_op_data_new( + adapter->allocator, options->topic_filter, adapter->callback_ref); + + struct aws_mqtt5_subscription_view subscription_view = { + .qos = AWS_MQTT5_QOS_AT_LEAST_ONCE, + .topic_filter = options->topic_filter, + }; + + struct aws_mqtt5_packet_subscribe_view subscribe_view = { + .subscriptions = &subscription_view, + .subscription_count = 1, + }; + + struct aws_mqtt5_subscribe_completion_options completion_options = { + .ack_timeout_seconds_override = options->ack_timeout_seconds, + .completion_callback = s_protocol_adapter_5_subscribe_completion, + .completion_user_data = subscribe_data, + }; + + if (aws_mqtt5_client_subscribe(adapter->client, &subscribe_view, &completion_options)) { + goto error; + } + + return AWS_OP_SUCCESS; + +error: + + s_aws_mqtt_protocol_adapter_5_subscription_op_data_destroy(subscribe_data); + + return AWS_OP_ERR; +} + +/* Unsubscribe */ + +static void s_protocol_adapter_5_unsubscribe_completion( + const struct aws_mqtt5_packet_unsuback_view *unsuback, + int error_code, + void *complete_ctx) { + struct aws_mqtt_protocol_adapter_5_subscription_op_data *unsubscribe_data = complete_ctx; + struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_weak_ref_get_reference(unsubscribe_data->callback_ref); + + if (adapter == NULL) { + goto done; + } + + if (error_code == AWS_ERROR_SUCCESS) { + if (unsuback == NULL || unsuback->reason_code_count != 1 || unsuback->reason_codes[0] >= 128) { + error_code = AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE; + } + } + + struct aws_protocol_adapter_subscription_event unsubscribe_event = { + .topic_filter = aws_byte_cursor_from_buf(&unsubscribe_data->topic_filter), + .event_type = AWS_PASET_UNSUBSCRIBE, + .error_code = error_code, + }; + + (*adapter->config.subscription_event_callback)(&unsubscribe_event, adapter->config.user_data); + +done: + + s_aws_mqtt_protocol_adapter_5_subscription_op_data_destroy(unsubscribe_data); +} + +int s_aws_mqtt_protocol_adapter_5_unsubscribe(void *impl, struct aws_protocol_adapter_unsubscribe_options *options) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; + + struct aws_mqtt_protocol_adapter_5_subscription_op_data *unsubscribe_data = + s_aws_mqtt_protocol_adapter_5_subscription_op_data_new( + adapter->allocator, options->topic_filter, adapter->callback_ref); + + struct aws_mqtt5_packet_unsubscribe_view unsubscribe_view = { + .topic_filters = &options->topic_filter, + .topic_filter_count = 1, + }; + + struct aws_mqtt5_unsubscribe_completion_options completion_options = { + .ack_timeout_seconds_override = options->ack_timeout_seconds, + .completion_callback = s_protocol_adapter_5_unsubscribe_completion, + .completion_user_data = unsubscribe_data, + }; + + if (aws_mqtt5_client_unsubscribe(adapter->client, &unsubscribe_view, &completion_options)) { + goto error; + } + + return AWS_OP_SUCCESS; + +error: + + s_aws_mqtt_protocol_adapter_5_subscription_op_data_destroy(unsubscribe_data); + + return AWS_OP_ERR; +} + +/* Publish */ + +struct aws_mqtt_protocol_adapter_5_publish_op_data { + struct aws_allocator *allocator; + struct aws_weak_ref *callback_ref; + + void (*completion_callback_fn)(int, void *); + void *user_data; +}; + +static struct aws_mqtt_protocol_adapter_5_publish_op_data *s_aws_mqtt_protocol_adapter_5_publish_op_data_new( + struct aws_allocator *allocator, + const struct aws_protocol_adapter_publish_options *publish_options, + struct aws_weak_ref *callback_ref) { + struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data = + aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_publish_op_data)); + + publish_data->allocator = allocator; + publish_data->callback_ref = aws_weak_ref_acquire(callback_ref); + publish_data->completion_callback_fn = publish_options->completion_callback_fn; + publish_data->user_data = publish_options->user_data; + + return publish_data; +} + +static void s_aws_mqtt_protocol_adapter_5_publish_op_data_destroy( + struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data) { + aws_weak_ref_release(publish_data->callback_ref); + + aws_mem_release(publish_data->allocator, publish_data); +} + +static void s_protocol_adapter_5_publish_completion( + enum aws_mqtt5_packet_type packet_type, + const void *packet, + int error_code, + void *complete_ctx) { + struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data = complete_ctx; + struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_weak_ref_get_reference(publish_data->callback_ref); + + if (adapter == NULL) { + goto done; + } + + if (error_code == AWS_ERROR_SUCCESS && packet_type == AWS_MQTT5_PT_PUBACK) { + const struct aws_mqtt5_packet_puback_view *puback = packet; + if (puback->reason_code >= 128) { + error_code = AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE; + } + } + + (*publish_data->completion_callback_fn)(error_code, publish_data->user_data); + +done: + + s_aws_mqtt_protocol_adapter_5_publish_op_data_destroy(publish_data); +} + +int s_aws_mqtt_protocol_adapter_5_publish(void *impl, struct aws_protocol_adapter_publish_options *options) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; + struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data = + s_aws_mqtt_protocol_adapter_5_publish_op_data_new(adapter->allocator, options, adapter->callback_ref); + + struct aws_mqtt5_packet_publish_view publish_view = { + .topic = options->topic, .qos = AWS_MQTT5_QOS_AT_LEAST_ONCE, .payload = options->payload}; + + struct aws_mqtt5_publish_completion_options completion_options = { + .ack_timeout_seconds_override = options->ack_timeout_seconds, + .completion_callback = s_protocol_adapter_5_publish_completion, + .completion_user_data = publish_data, + }; + + if (aws_mqtt5_client_publish(adapter->client, &publish_view, &completion_options)) { + goto error; + } + + return AWS_OP_SUCCESS; + +error: + + s_aws_mqtt_protocol_adapter_5_publish_op_data_destroy(publish_data); + + return AWS_OP_ERR; +} + +static bool s_protocol_adapter_mqtt5_listener_publish_received( + const struct aws_mqtt5_packet_publish_view *publish, + void *user_data) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = user_data; + + struct aws_protocol_adapter_incoming_publish_event publish_event = { + .topic = publish->topic, + .payload = publish->payload, + }; + + (*adapter->config.incoming_publish_callback)(&publish_event, adapter->config.user_data); + + return false; +} + +static void s_protocol_adapter_mqtt5_lifecycle_event_callback(const struct aws_mqtt5_client_lifecycle_event *event) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = event->user_data; + + if (event->event_type != AWS_MQTT5_CLET_CONNECTION_SUCCESS) { + return; + } + + struct aws_protocol_adapter_session_event session_event = { + .joined_session = event->settings->rejoined_session, + }; + + (*adapter->config.session_event_callback)(&session_event, adapter->config.user_data); +} + +static void s_protocol_adapter_mqtt5_listener_termination_callback(void *user_data) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = user_data; + + AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(adapter->client->loop)); + + aws_weak_ref_zero_reference(adapter->callback_ref); + aws_weak_ref_release(adapter->callback_ref); + + aws_mqtt5_client_release(adapter->client); + + aws_protocol_adapter_terminate_callback_fn *terminate_callback = adapter->config.terminate_callback; + void *terminate_user_data = adapter->config.user_data; + + aws_mem_release(adapter->allocator, adapter); + + if (terminate_callback) { + (*terminate_callback)(terminate_user_data); + } +} + +static struct aws_mqtt_protocol_adapter_vtable s_protocol_adapter_mqtt5_vtable = { + .aws_mqtt_protocol_adapter_destroy_fn = s_aws_mqtt_protocol_adapter_5_destroy, + .aws_mqtt_protocol_adapter_subscribe_fn = s_aws_mqtt_protocol_adapter_5_subscribe, + .aws_mqtt_protocol_adapter_unsubscribe_fn = s_aws_mqtt_protocol_adapter_5_unsubscribe, + .aws_mqtt_protocol_adapter_publish_fn = s_aws_mqtt_protocol_adapter_5_publish, +}; + +struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5( + struct aws_allocator *allocator, + struct aws_mqtt_protocol_adapter_options *options, + struct aws_mqtt5_client *client) { + + if (options == NULL || client == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_mqtt_protocol_adapter_5_impl *adapter = + aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_impl)); + + adapter->allocator = allocator; + adapter->base.impl = adapter; + adapter->base.vtable = &s_protocol_adapter_mqtt5_vtable; + adapter->callback_ref = aws_weak_ref_new(allocator, adapter); + adapter->config = *options; + adapter->loop = client->loop; + adapter->client = aws_mqtt5_client_acquire(client); + + struct aws_mqtt5_listener_config listener_options = { + .client = client, + .listener_callbacks = + { + .listener_publish_received_handler = s_protocol_adapter_mqtt5_listener_publish_received, + .listener_publish_received_handler_user_data = adapter, + .lifecycle_event_handler = s_protocol_adapter_mqtt5_lifecycle_event_callback, + .lifecycle_event_handler_user_data = adapter, + }, + .termination_callback = s_protocol_adapter_mqtt5_listener_termination_callback, + .termination_callback_user_data = adapter, + }; + + adapter->listener = aws_mqtt5_listener_new(allocator, &listener_options); + + return &adapter->base; +} + +void aws_mqtt_protocol_adapter_destroy(struct aws_mqtt_protocol_adapter *adapter) { + (*adapter->vtable->aws_mqtt_protocol_adapter_destroy_fn)(adapter->impl); +} + +int aws_mqtt_protocol_adapter_subscribe( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_subscribe_options *options) { + return (*adapter->vtable->aws_mqtt_protocol_adapter_subscribe_fn)(adapter->impl, options); +} + +int aws_mqtt_protocol_adapter_unsubscribe( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_unsubscribe_options *options) { + return (*adapter->vtable->aws_mqtt_protocol_adapter_unsubscribe_fn)(adapter->impl, options); +} + +int aws_mqtt_protocol_adapter_publish( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_publish_options *options) { + return (*adapter->vtable->aws_mqtt_protocol_adapter_publish_fn)(adapter->impl, options); +} diff --git a/source/request-response/weak_ref.c b/source/request-response/weak_ref.c new file mode 100644 index 00000000..949014e7 --- /dev/null +++ b/source/request-response/weak_ref.c @@ -0,0 +1,54 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include + +struct aws_weak_ref { + struct aws_allocator *allocator; + struct aws_ref_count refcount; + void *referenced; +}; + +static void s_destroy_weak_ref(void *value) { + struct aws_weak_ref *weak_ref = value; + + aws_mem_release(weak_ref->allocator, weak_ref); +} + +struct aws_weak_ref *aws_weak_ref_new(struct aws_allocator *allocator, void *referenced) { + struct aws_weak_ref *weak_ref = aws_mem_calloc(allocator, 1, sizeof(struct aws_weak_ref)); + + aws_ref_count_init(&weak_ref->refcount, weak_ref, s_destroy_weak_ref); + weak_ref->allocator = allocator; + weak_ref->referenced = referenced; + + return weak_ref; +} + +struct aws_weak_ref *aws_weak_ref_acquire(struct aws_weak_ref *weak_ref) { + if (NULL != weak_ref) { + aws_ref_count_acquire(&weak_ref->refcount); + } + + return weak_ref; +} + +struct aws_weak_ref *aws_weak_ref_release(struct aws_weak_ref *weak_ref) { + if (NULL != weak_ref) { + aws_ref_count_release(&weak_ref->refcount); + } + + return NULL; +} + +void *aws_weak_ref_get_reference(struct aws_weak_ref *weak_ref) { + return weak_ref->referenced; +} + +void aws_weak_ref_zero_reference(struct aws_weak_ref *weak_ref) { + weak_ref->referenced = NULL; +} diff --git a/source/v5/mqtt5_listener.c b/source/v5/mqtt5_listener.c index 04170394..ebb45b81 100644 --- a/source/v5/mqtt5_listener.c +++ b/source/v5/mqtt5_listener.c @@ -84,6 +84,7 @@ struct aws_mqtt5_listener *aws_mqtt5_listener_new( struct aws_allocator *allocator, struct aws_mqtt5_listener_config *config) { if (config->client == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } diff --git a/source/v5/mqtt5_to_mqtt3_adapter.c b/source/v5/mqtt5_to_mqtt3_adapter.c index 129c0132..72128a97 100644 --- a/source/v5/mqtt5_to_mqtt3_adapter.c +++ b/source/v5/mqtt5_to_mqtt3_adapter.c @@ -2854,6 +2854,12 @@ static uint16_t s_aws_mqtt_5_resubscribe_existing_topics( return 0; } +enum aws_mqtt311_impl_type s_aws_mqtt_client_connection_5_get_impl(const void *impl) { + (void)impl; + + return AWS_MQTT311_IT_5_ADAPTER; +} + static struct aws_mqtt_client_connection_vtable s_aws_mqtt_client_connection_5_vtable = { .acquire_fn = s_aws_mqtt_client_connection_5_acquire, .release_fn = s_aws_mqtt_client_connection_5_release, @@ -2877,6 +2883,7 @@ static struct aws_mqtt_client_connection_vtable s_aws_mqtt_client_connection_5_v .unsubscribe_fn = s_aws_mqtt_client_connection_5_unsubscribe, .publish_fn = s_aws_mqtt_client_connection_5_publish, .get_stats_fn = s_aws_mqtt_client_connection_5_get_stats, + .get_impl_type = s_aws_mqtt_client_connection_5_get_impl, }; static struct aws_mqtt_client_connection_vtable *s_aws_mqtt_client_connection_5_vtable_ptr = diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b8ef2587..e52138a6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,8 +3,8 @@ include(AwsTestHarness) include(AwsLibFuzzer) enable_testing() -file(GLOB TEST_HDRS "v3/*.h v5/*.h") -set(TEST_SRC v3/*.c v5/*.c *.c) +file(GLOB TEST_HDRS "v3/*.h" "v5/*.h" "request-response/*.h") +set(TEST_SRC "v3/*.c" "v5/*.c" "request-response/*.c" "*.c") file(GLOB TESTS ${TEST_HDRS} ${TEST_SRC}) add_test_case(mqtt_packet_puback) @@ -442,6 +442,27 @@ add_test_case(mqtt_subscription_set_publish_single_level_wildcards) add_test_case(mqtt_subscription_set_publish_multi_level_wildcards) add_test_case(mqtt_subscription_set_get_subscriptions) +add_test_case(request_response_mqtt5_protocol_adapter_subscribe_success) +add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_error_code) +add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code) +add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_timeout) +add_test_case(request_response_mqtt5_protocol_adapter_unsubscribe_success) +add_test_case(request_response_mqtt5_protocol_adapter_unsubscribe_failure_error_code) +add_test_case(request_response_mqtt5_protocol_adapter_unsubscribe_failure_reason_code) +add_test_case(request_response_mqtt5_protocol_adapter_unsubscribe_failure_timeout) +add_test_case(request_response_mqtt5_protocol_adapter_publish_success) +add_test_case(request_response_mqtt5_protocol_adapter_publish_failure_error_code) +add_test_case(request_response_mqtt5_protocol_adapter_publish_failure_reason_code) +add_test_case(request_response_mqtt5_protocol_adapter_publish_failure_timeout) +add_test_case(request_response_mqtt5_protocol_adapter_session_event_no_rejoin) +add_test_case(request_response_mqtt5_protocol_adapter_session_event_rejoin) +add_test_case(request_response_mqtt5_protocol_adapter_incoming_publish) +add_test_case(request_response_mqtt5_protocol_adapter_shutdown_while_pending) + +add_test_case(mqtt311_listener_connection_success_event_no_session) +add_test_case(mqtt311_listener_connection_success_event_with_session) +add_test_case(mqtt311_listener_publish_event) + generate_test_driver(${PROJECT_NAME}-tests) set(TEST_PAHO_CLIENT_BINARY_NAME ${PROJECT_NAME}-paho-client) diff --git a/tests/request-response/request_response_protocol_adapter_tests.c b/tests/request-response/request_response_protocol_adapter_tests.c new file mode 100644 index 00000000..2034f460 --- /dev/null +++ b/tests/request-response/request_response_protocol_adapter_tests.c @@ -0,0 +1,1048 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include "../v5/mqtt5_testing_utils.h" +#include + +#include "aws/mqtt/private/request-response/protocol_adapter.h" + +#include + +struct request_response_protocol_adapter_incoming_publish_event_record { + struct aws_byte_buf topic; + struct aws_byte_buf payload; +}; + +static void s_request_response_protocol_adapter_incoming_publish_event_record_init( + struct request_response_protocol_adapter_incoming_publish_event_record *record, + struct aws_allocator *allocator, + struct aws_byte_cursor topic, + struct aws_byte_cursor payload) { + + aws_byte_buf_init_copy_from_cursor(&record->topic, allocator, topic); + aws_byte_buf_init_copy_from_cursor(&record->payload, allocator, payload); +} + +static void s_request_response_protocol_adapter_incoming_publish_event_record_clean_up( + struct request_response_protocol_adapter_incoming_publish_event_record *record) { + aws_byte_buf_clean_up(&record->topic); + aws_byte_buf_clean_up(&record->payload); +} + +struct request_response_protocol_adapter_subscription_event_record { + enum aws_protocol_adapter_subscription_event_type event_type; + struct aws_byte_buf topic_filter; + int error_code; +}; + +static void s_request_response_protocol_adapter_subscription_event_record_init( + struct request_response_protocol_adapter_subscription_event_record *record, + struct aws_allocator *allocator, + enum aws_protocol_adapter_subscription_event_type event_type, + struct aws_byte_cursor topic_filter, + int error_code) { + + AWS_ZERO_STRUCT(*record); + + record->event_type = event_type; + record->error_code = error_code; + aws_byte_buf_init_copy_from_cursor(&record->topic_filter, allocator, topic_filter); +} + +static void s_request_response_protocol_adapter_subscription_event_record_cleanup( + struct request_response_protocol_adapter_subscription_event_record *record) { + aws_byte_buf_clean_up(&record->topic_filter); +} + +struct aws_request_response_mqtt5_adapter_test_fixture { + struct aws_allocator *allocator; + struct aws_mqtt5_client_mock_test_fixture mqtt5_fixture; + + struct aws_mqtt_protocol_adapter *protocol_adapter; + + struct aws_array_list incoming_publish_events; + struct aws_array_list session_events; + struct aws_array_list subscription_events; + struct aws_array_list publish_results; + + bool adapter_terminated; + + struct aws_mutex lock; + struct aws_condition_variable signal; +}; + +static void s_rr_mqtt5_protocol_adapter_test_on_subscription_event( + struct aws_protocol_adapter_subscription_event *event, + void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + struct request_response_protocol_adapter_subscription_event_record record; + s_request_response_protocol_adapter_subscription_event_record_init( + &record, fixture->allocator, event->event_type, event->topic_filter, event->error_code); + + aws_mutex_lock(&fixture->lock); + aws_array_list_push_back(&fixture->subscription_events, &record); + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); +} + +static void s_rr_mqtt5_protocol_adapter_test_on_incoming_publish( + struct aws_protocol_adapter_incoming_publish_event *publish, + void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + struct request_response_protocol_adapter_incoming_publish_event_record record; + AWS_ZERO_STRUCT(record); + s_request_response_protocol_adapter_incoming_publish_event_record_init( + &record, fixture->allocator, publish->topic, publish->payload); + + aws_mutex_lock(&fixture->lock); + aws_array_list_push_back(&fixture->incoming_publish_events, &record); + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); +} + +static void s_rr_mqtt5_protocol_adapter_test_on_terminate_callback(void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + aws_mutex_lock(&fixture->lock); + fixture->adapter_terminated = true; + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); +} + +static void s_rr_mqtt5_protocol_adapter_test_on_session_event( + struct aws_protocol_adapter_session_event *event, + void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + aws_mutex_lock(&fixture->lock); + aws_array_list_push_back(&fixture->session_events, event); + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); +} + +static void s_rr_mqtt5_protocol_adapter_test_on_publish_result(int error_code, void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + aws_mutex_lock(&fixture->lock); + aws_array_list_push_back(&fixture->publish_results, &error_code); + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); +} + +static int s_aws_request_response_mqtt5_adapter_test_fixture_init( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct aws_allocator *allocator, + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options *mqtt5_fixture_config) { + + AWS_ZERO_STRUCT(*fixture); + + fixture->allocator = allocator; + + if (aws_mqtt5_client_mock_test_fixture_init(&fixture->mqtt5_fixture, allocator, mqtt5_fixture_config)) { + return AWS_OP_ERR; + } + + struct aws_mqtt_protocol_adapter_options protocol_adapter_options = { + .subscription_event_callback = s_rr_mqtt5_protocol_adapter_test_on_subscription_event, + .incoming_publish_callback = s_rr_mqtt5_protocol_adapter_test_on_incoming_publish, + .terminate_callback = s_rr_mqtt5_protocol_adapter_test_on_terminate_callback, + .session_event_callback = s_rr_mqtt5_protocol_adapter_test_on_session_event, + .user_data = fixture}; + + fixture->protocol_adapter = + aws_mqtt_protocol_adapter_new_from_5(allocator, &protocol_adapter_options, fixture->mqtt5_fixture.client); + AWS_FATAL_ASSERT(fixture->protocol_adapter != NULL); + + aws_array_list_init_dynamic( + &fixture->incoming_publish_events, + allocator, + 10, + sizeof(struct request_response_protocol_adapter_incoming_publish_event_record)); + aws_array_list_init_dynamic( + &fixture->session_events, allocator, 10, sizeof(struct aws_protocol_adapter_session_event)); + aws_array_list_init_dynamic( + &fixture->subscription_events, + allocator, + 10, + sizeof(struct request_response_protocol_adapter_subscription_event_record)); + aws_array_list_init_dynamic(&fixture->publish_results, allocator, 10, sizeof(int)); + + aws_mutex_init(&fixture->lock); + aws_condition_variable_init(&fixture->signal); + + return AWS_OP_SUCCESS; +} + +static bool s_is_adapter_terminated(void *context) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = context; + + return fixture->adapter_terminated; +} + +static void s_aws_request_response_mqtt5_adapter_test_fixture_destroy_adapters( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture) { + if (fixture->protocol_adapter != NULL) { + aws_mqtt_protocol_adapter_destroy(fixture->protocol_adapter); + + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_is_adapter_terminated, fixture); + aws_mutex_unlock(&fixture->lock); + fixture->protocol_adapter = NULL; + } +} + +static void s_aws_request_response_mqtt5_adapter_test_fixture_clean_up( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture) { + + s_aws_request_response_mqtt5_adapter_test_fixture_destroy_adapters(fixture); + + aws_mqtt5_client_mock_test_fixture_clean_up(&fixture->mqtt5_fixture); + + for (size_t i = 0; i < aws_array_list_length(&fixture->subscription_events); ++i) { + struct request_response_protocol_adapter_subscription_event_record record; + aws_array_list_get_at(&fixture->subscription_events, &record, i); + s_request_response_protocol_adapter_subscription_event_record_cleanup(&record); + } + aws_array_list_clean_up(&fixture->subscription_events); + + for (size_t i = 0; i < aws_array_list_length(&fixture->incoming_publish_events); ++i) { + struct request_response_protocol_adapter_incoming_publish_event_record record; + aws_array_list_get_at(&fixture->incoming_publish_events, &record, i); + s_request_response_protocol_adapter_incoming_publish_event_record_clean_up(&record); + } + aws_array_list_clean_up(&fixture->incoming_publish_events); + + aws_array_list_clean_up(&fixture->session_events); + aws_array_list_clean_up(&fixture->publish_results); + + aws_mutex_clean_up(&fixture->lock); + aws_condition_variable_clean_up(&fixture->signal); +} + +struct test_subscription_event_wait_context { + struct request_response_protocol_adapter_subscription_event_record *expected_event; + size_t expected_count; + struct aws_request_response_mqtt5_adapter_test_fixture *fixture; +}; + +static bool s_do_subscription_events_contain(void *context) { + struct test_subscription_event_wait_context *wait_context = context; + + size_t found = 0; + + size_t num_events = aws_array_list_length(&wait_context->fixture->subscription_events); + for (size_t i = 0; i < num_events; ++i) { + struct request_response_protocol_adapter_subscription_event_record record; + aws_array_list_get_at(&wait_context->fixture->subscription_events, &record, i); + + if (record.event_type != wait_context->expected_event->event_type) { + continue; + } + + if (record.error_code != wait_context->expected_event->error_code) { + continue; + } + + struct aws_byte_cursor record_topic_filter = aws_byte_cursor_from_buf(&record.topic_filter); + struct aws_byte_cursor expected_topic_filter = + aws_byte_cursor_from_buf(&wait_context->expected_event->topic_filter); + if (!aws_byte_cursor_eq(&record_topic_filter, &expected_topic_filter)) { + continue; + } + + ++found; + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_subscription_events_contains( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct request_response_protocol_adapter_subscription_event_record *expected_event, + size_t expected_count) { + + struct test_subscription_event_wait_context context = { + .expected_event = expected_event, + .expected_count = expected_count, + .fixture = fixture, + }; + + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_do_subscription_events_contain, &context); + aws_mutex_unlock(&fixture->lock); +} + +struct test_session_event_wait_context { + struct aws_protocol_adapter_session_event *expected_event; + size_t expected_count; + struct aws_request_response_mqtt5_adapter_test_fixture *fixture; +}; + +static bool s_do_session_events_contain(void *context) { + struct test_session_event_wait_context *wait_context = context; + + size_t found = 0; + + size_t num_events = aws_array_list_length(&wait_context->fixture->session_events); + for (size_t i = 0; i < num_events; ++i) { + struct aws_protocol_adapter_session_event record; + aws_array_list_get_at(&wait_context->fixture->session_events, &record, i); + + if (record.joined_session != wait_context->expected_event->joined_session) { + continue; + } + + ++found; + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_session_events_contains( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct aws_protocol_adapter_session_event *expected_event, + size_t expected_count) { + + struct test_session_event_wait_context context = { + .expected_event = expected_event, + .expected_count = expected_count, + .fixture = fixture, + }; + + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_do_session_events_contain, &context); + aws_mutex_unlock(&fixture->lock); +} + +struct test_incoming_publish_event_wait_context { + struct request_response_protocol_adapter_incoming_publish_event_record *expected_event; + size_t expected_count; + struct aws_request_response_mqtt5_adapter_test_fixture *fixture; +}; + +static bool s_do_incoming_publish_events_contain(void *context) { + struct test_incoming_publish_event_wait_context *wait_context = context; + + size_t found = 0; + + size_t num_events = aws_array_list_length(&wait_context->fixture->incoming_publish_events); + for (size_t i = 0; i < num_events; ++i) { + struct request_response_protocol_adapter_incoming_publish_event_record record; + aws_array_list_get_at(&wait_context->fixture->incoming_publish_events, &record, i); + + struct aws_byte_cursor record_topic = aws_byte_cursor_from_buf(&record.topic); + struct aws_byte_cursor expected_topic = aws_byte_cursor_from_buf(&wait_context->expected_event->topic); + if (!aws_byte_cursor_eq(&record_topic, &expected_topic)) { + continue; + } + + struct aws_byte_cursor record_payload = aws_byte_cursor_from_buf(&record.payload); + struct aws_byte_cursor expected_payload = aws_byte_cursor_from_buf(&wait_context->expected_event->payload); + if (!aws_byte_cursor_eq(&record_payload, &expected_payload)) { + continue; + } + + ++found; + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_incoming_publish_events_contains( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct request_response_protocol_adapter_incoming_publish_event_record *expected_event, + size_t expected_count) { + + struct test_incoming_publish_event_wait_context context = { + .expected_event = expected_event, + .expected_count = expected_count, + .fixture = fixture, + }; + + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_do_incoming_publish_events_contain, &context); + aws_mutex_unlock(&fixture->lock); +} + +struct test_publish_result_wait_context { + int expected_error_code; + size_t expected_count; + struct aws_request_response_mqtt5_adapter_test_fixture *fixture; +}; + +static bool s_do_publish_results_contain(void *context) { + struct test_publish_result_wait_context *wait_context = context; + + size_t found = 0; + + size_t num_events = aws_array_list_length(&wait_context->fixture->publish_results); + for (size_t i = 0; i < num_events; ++i) { + int error_code = AWS_ERROR_SUCCESS; + aws_array_list_get_at(&wait_context->fixture->publish_results, &error_code, i); + + if (error_code == wait_context->expected_error_code) { + ++found; + } + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_publish_results_contains( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + int expected_error_code, + size_t expected_count) { + + struct test_publish_result_wait_context context = { + .expected_error_code = expected_error_code, + .expected_count = expected_count, + .fixture = fixture, + }; + + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_do_publish_results_contain, &context); + aws_mutex_unlock(&fixture->lock); +} + +enum protocol_adapter_operation_test_type { + PAOTT_SUCCESS, + PAOTT_FAILURE_TIMEOUT, + PAOTT_FAILURE_REASON_CODE, + PAOTT_FAILURE_ERROR_CODE, +}; + +static enum aws_mqtt5_suback_reason_code s_failed_suback_reason_codes[] = { + AWS_MQTT5_SARC_NOT_AUTHORIZED, +}; + +static int s_aws_mqtt5_server_send_failed_suback_on_subscribe( + void *packet, + struct aws_mqtt5_server_mock_connection_context *connection, + void *user_data) { + (void)packet; + (void)user_data; + + struct aws_mqtt5_packet_subscribe_view *subscribe_view = packet; + + struct aws_mqtt5_packet_suback_view suback_view = { + .packet_id = subscribe_view->packet_id, + .reason_code_count = AWS_ARRAY_SIZE(s_failed_suback_reason_codes), + .reason_codes = s_failed_suback_reason_codes, + }; + + return aws_mqtt5_mock_server_send_packet(connection, AWS_MQTT5_PT_SUBACK, &suback_view); +} + +static int s_test_type_to_expected_error_code(enum protocol_adapter_operation_test_type test_type) { + switch (test_type) { + case PAOTT_FAILURE_TIMEOUT: + return AWS_ERROR_MQTT_TIMEOUT; + case PAOTT_FAILURE_REASON_CODE: + return AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE; + case PAOTT_FAILURE_ERROR_CODE: + return AWS_ERROR_MQTT5_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY; + default: + return AWS_ERROR_SUCCESS; + } +} + +static int s_do_request_response_mqtt5_protocol_adapter_subscribe_test( + struct aws_allocator *allocator, + enum protocol_adapter_operation_test_type test_type) { + aws_mqtt_library_init(allocator); + + struct mqtt5_client_test_options test_options; + aws_mqtt5_client_test_init_default_options(&test_options); + + if (test_type == PAOTT_SUCCESS) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = + aws_mqtt5_server_send_suback_on_subscribe; + } else if (test_type == PAOTT_FAILURE_REASON_CODE) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = + s_aws_mqtt5_server_send_failed_suback_on_subscribe; + } + + if (test_type == PAOTT_FAILURE_ERROR_CODE) { + test_options.client_options.offline_queue_behavior = AWS_MQTT5_COQBT_FAIL_ALL_ON_DISCONNECT; + } + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + + if (test_type != PAOTT_FAILURE_ERROR_CODE) { + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + } + + int expected_error_code = s_test_type_to_expected_error_code(test_type); + + struct request_response_protocol_adapter_subscription_event_record expected_outcome; + s_request_response_protocol_adapter_subscription_event_record_init( + &expected_outcome, + allocator, + AWS_PASET_SUBSCRIBE, + aws_byte_cursor_from_c_str("hello/world"), + expected_error_code); + + struct aws_protocol_adapter_subscribe_options subscribe_options = { + .topic_filter = aws_byte_cursor_from_buf(&expected_outcome.topic_filter), + .ack_timeout_seconds = 2, + }; + + aws_mqtt_protocol_adapter_subscribe(fixture.protocol_adapter, &subscribe_options); + + s_wait_for_subscription_events_contains(&fixture, &expected_outcome, 1); + + s_request_response_protocol_adapter_subscription_event_record_cleanup(&expected_outcome); + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +static int s_request_response_mqtt5_protocol_adapter_subscribe_success_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_subscribe_test(allocator, PAOTT_SUCCESS)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_subscribe_success, + s_request_response_mqtt5_protocol_adapter_subscribe_success_fn) + +static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_timeout_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_subscribe_test(allocator, PAOTT_FAILURE_TIMEOUT)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_subscribe_failure_timeout, + s_request_response_mqtt5_protocol_adapter_subscribe_failure_timeout_fn) + +static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_subscribe_test(allocator, PAOTT_FAILURE_REASON_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code, + s_request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code_fn) + +static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_error_code_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_subscribe_test(allocator, PAOTT_FAILURE_ERROR_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_subscribe_failure_error_code, + s_request_response_mqtt5_protocol_adapter_subscribe_failure_error_code_fn) + +static enum aws_mqtt5_unsuback_reason_code s_failed_unsuback_reason_codes[] = { + AWS_MQTT5_UARC_NOT_AUTHORIZED, +}; + +static int s_aws_mqtt5_server_send_failed_unsuback_on_unsubscribe( + void *packet, + struct aws_mqtt5_server_mock_connection_context *connection, + void *user_data) { + (void)packet; + (void)user_data; + + struct aws_mqtt5_packet_subscribe_view *unsubscribe_view = packet; + + struct aws_mqtt5_packet_unsuback_view unsuback_view = { + .packet_id = unsubscribe_view->packet_id, + .reason_code_count = AWS_ARRAY_SIZE(s_failed_unsuback_reason_codes), + .reason_codes = s_failed_unsuback_reason_codes, + }; + + return aws_mqtt5_mock_server_send_packet(connection, AWS_MQTT5_PT_UNSUBACK, &unsuback_view); +} + +static int s_do_request_response_mqtt5_protocol_adapter_unsubscribe_test( + struct aws_allocator *allocator, + enum protocol_adapter_operation_test_type test_type) { + aws_mqtt_library_init(allocator); + + struct mqtt5_client_test_options test_options; + aws_mqtt5_client_test_init_default_options(&test_options); + + if (test_type == PAOTT_SUCCESS) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_UNSUBSCRIBE] = + aws_mqtt5_mock_server_handle_unsubscribe_unsuback_success; + } else if (test_type == PAOTT_FAILURE_REASON_CODE) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_UNSUBSCRIBE] = + s_aws_mqtt5_server_send_failed_unsuback_on_unsubscribe; + } + + if (test_type == PAOTT_FAILURE_ERROR_CODE) { + test_options.client_options.offline_queue_behavior = AWS_MQTT5_COQBT_FAIL_ALL_ON_DISCONNECT; + } + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + + if (test_type != PAOTT_FAILURE_ERROR_CODE) { + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + } + + int expected_error_code = s_test_type_to_expected_error_code(test_type); + + struct request_response_protocol_adapter_subscription_event_record expected_outcome; + s_request_response_protocol_adapter_subscription_event_record_init( + &expected_outcome, + allocator, + AWS_PASET_UNSUBSCRIBE, + aws_byte_cursor_from_c_str("hello/world"), + expected_error_code); + + struct aws_protocol_adapter_unsubscribe_options unsubscribe_options = { + .topic_filter = aws_byte_cursor_from_buf(&expected_outcome.topic_filter), + .ack_timeout_seconds = 2, + }; + + aws_mqtt_protocol_adapter_unsubscribe(fixture.protocol_adapter, &unsubscribe_options); + + s_wait_for_subscription_events_contains(&fixture, &expected_outcome, 1); + + s_request_response_protocol_adapter_subscription_event_record_cleanup(&expected_outcome); + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +static int s_request_response_mqtt5_protocol_adapter_unsubscribe_success_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_unsubscribe_test(allocator, PAOTT_SUCCESS)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_unsubscribe_success, + s_request_response_mqtt5_protocol_adapter_unsubscribe_success_fn) + +static int s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_timeout_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_unsubscribe_test(allocator, PAOTT_FAILURE_TIMEOUT)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_unsubscribe_failure_timeout, + s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_timeout_fn) + +static int s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_reason_code_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_unsubscribe_test(allocator, PAOTT_FAILURE_REASON_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_unsubscribe_failure_reason_code, + s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_reason_code_fn) + +static int s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_error_code_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_unsubscribe_test(allocator, PAOTT_FAILURE_ERROR_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_unsubscribe_failure_error_code, + s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_error_code_fn) + +static int s_aws_mqtt5_server_send_failed_puback_on_publish( + void *packet, + struct aws_mqtt5_server_mock_connection_context *connection, + void *user_data) { + (void)packet; + (void)user_data; + + struct aws_mqtt5_packet_publish_view *publish_view = packet; + + if (publish_view->qos == AWS_MQTT5_QOS_AT_LEAST_ONCE) { + struct aws_mqtt5_packet_puback_view puback_view = { + .packet_id = publish_view->packet_id, + .reason_code = AWS_MQTT5_PARC_NOT_AUTHORIZED, + }; + + return aws_mqtt5_mock_server_send_packet(connection, AWS_MQTT5_PT_PUBACK, &puback_view); + } + + return AWS_OP_SUCCESS; +} + +static int s_do_request_response_mqtt5_protocol_adapter_publish_test( + struct aws_allocator *allocator, + enum protocol_adapter_operation_test_type test_type) { + aws_mqtt_library_init(allocator); + + struct mqtt5_client_test_options test_options; + aws_mqtt5_client_test_init_default_options(&test_options); + + if (test_type == PAOTT_SUCCESS) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_PUBLISH] = + aws_mqtt5_mock_server_handle_publish_puback; + } else if (test_type == PAOTT_FAILURE_REASON_CODE) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_PUBLISH] = + s_aws_mqtt5_server_send_failed_puback_on_publish; + } + + if (test_type == PAOTT_FAILURE_ERROR_CODE) { + test_options.client_options.offline_queue_behavior = AWS_MQTT5_COQBT_FAIL_ALL_ON_DISCONNECT; + } + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + + if (test_type != PAOTT_FAILURE_ERROR_CODE) { + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + } + + struct aws_protocol_adapter_publish_options publish_options = { + .topic = aws_byte_cursor_from_c_str("hello/world"), + .payload = aws_byte_cursor_from_c_str("SomePayload"), + .ack_timeout_seconds = 2, + .completion_callback_fn = s_rr_mqtt5_protocol_adapter_test_on_publish_result, + .user_data = &fixture}; + + aws_mqtt_protocol_adapter_publish(fixture.protocol_adapter, &publish_options); + + int expected_error_code = s_test_type_to_expected_error_code(test_type); + s_wait_for_publish_results_contains(&fixture, expected_error_code, 1); + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +static int s_request_response_mqtt5_protocol_adapter_publish_success_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_publish_test(allocator, PAOTT_SUCCESS)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_publish_success, + s_request_response_mqtt5_protocol_adapter_publish_success_fn) + +static int s_request_response_mqtt5_protocol_adapter_publish_failure_timeout_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_publish_test(allocator, PAOTT_FAILURE_TIMEOUT)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_publish_failure_timeout, + s_request_response_mqtt5_protocol_adapter_publish_failure_timeout_fn) + +static int s_request_response_mqtt5_protocol_adapter_publish_failure_reason_code_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_publish_test(allocator, PAOTT_FAILURE_REASON_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_publish_failure_reason_code, + s_request_response_mqtt5_protocol_adapter_publish_failure_reason_code_fn) + +static int s_request_response_mqtt5_protocol_adapter_publish_failure_error_code_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_publish_test(allocator, PAOTT_FAILURE_ERROR_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_publish_failure_error_code, + s_request_response_mqtt5_protocol_adapter_publish_failure_error_code_fn) + +static int s_do_request_response_mqtt5_protocol_adapter_session_event_test( + struct aws_allocator *allocator, + bool rejoin_session) { + aws_mqtt_library_init(allocator); + + struct mqtt5_client_test_options test_options; + aws_mqtt5_client_test_init_default_options(&test_options); + + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_CONNECT] = + aws_mqtt5_mock_server_handle_connect_honor_session_unconditional; + test_options.client_options.session_behavior = rejoin_session ? AWS_MQTT5_CSBT_REJOIN_ALWAYS : AWS_MQTT5_CSBT_CLEAN; + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + + struct aws_protocol_adapter_session_event expected_session_record = { + .joined_session = rejoin_session, + }; + + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + s_wait_for_session_events_contains(&fixture, &expected_session_record, 1); + + ASSERT_SUCCESS(aws_mqtt5_client_stop(client, NULL, NULL)); + aws_wait_for_stopped_lifecycle_event(&fixture.mqtt5_fixture); + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +static int s_request_response_mqtt5_protocol_adapter_session_event_no_rejoin_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + return s_do_request_response_mqtt5_protocol_adapter_session_event_test(allocator, false); +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_session_event_no_rejoin, + s_request_response_mqtt5_protocol_adapter_session_event_no_rejoin_fn) + +static int s_request_response_mqtt5_protocol_adapter_session_event_rejoin_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + return s_do_request_response_mqtt5_protocol_adapter_session_event_test(allocator, true); +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_session_event_rejoin, + s_request_response_mqtt5_protocol_adapter_session_event_rejoin_fn) + +static int s_request_response_mqtt5_protocol_adapter_incoming_publish_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_mqtt_library_init(allocator); + + struct mqtt5_client_test_options test_options; + aws_mqtt5_client_test_init_default_options(&test_options); + + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_PUBLISH] = + aws_mqtt5_mock_server_handle_publish_puback_and_forward; + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + + struct request_response_protocol_adapter_incoming_publish_event_record expected_publish; + AWS_ZERO_STRUCT(expected_publish); + + s_request_response_protocol_adapter_incoming_publish_event_record_init( + &expected_publish, + allocator, + aws_byte_cursor_from_c_str("hello/world"), + aws_byte_cursor_from_c_str("SomePayload")); + + struct aws_protocol_adapter_publish_options publish_options = { + .topic = aws_byte_cursor_from_buf(&expected_publish.topic), + .payload = aws_byte_cursor_from_buf(&expected_publish.payload), + .ack_timeout_seconds = 2, + .completion_callback_fn = s_rr_mqtt5_protocol_adapter_test_on_publish_result, + .user_data = &fixture}; + + aws_mqtt_protocol_adapter_publish(fixture.protocol_adapter, &publish_options); + + s_wait_for_incoming_publish_events_contains(&fixture, &expected_publish, 1); + + s_request_response_protocol_adapter_incoming_publish_event_record_clean_up(&expected_publish); + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_incoming_publish, + s_request_response_mqtt5_protocol_adapter_incoming_publish_fn) + +static int s_request_response_mqtt5_protocol_adapter_shutdown_while_pending_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + aws_mqtt_library_init(allocator); + + struct mqtt5_client_test_options test_options; + aws_mqtt5_client_test_init_default_options(&test_options); + + // don't respond to anything + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_PUBLISH] = NULL; + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = NULL; + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_UNSUBSCRIBE] = NULL; + + test_options.client_options.offline_queue_behavior = AWS_MQTT5_COQBT_FAIL_ALL_ON_DISCONNECT; + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + + // publish + struct aws_protocol_adapter_publish_options publish_options = { + .topic = aws_byte_cursor_from_c_str("hello/world"), + .payload = aws_byte_cursor_from_c_str("SomePayload"), + .ack_timeout_seconds = 5, + .completion_callback_fn = s_rr_mqtt5_protocol_adapter_test_on_publish_result, + .user_data = &fixture}; + + aws_mqtt_protocol_adapter_publish(fixture.protocol_adapter, &publish_options); + + // subscribe + struct aws_protocol_adapter_subscribe_options subscribe_options = { + .topic_filter = aws_byte_cursor_from_c_str("hello/world"), + .ack_timeout_seconds = 5, + }; + + aws_mqtt_protocol_adapter_subscribe(fixture.protocol_adapter, &subscribe_options); + + // unsubscribe + struct aws_protocol_adapter_unsubscribe_options unsubscribe_options = { + .topic_filter = aws_byte_cursor_from_c_str("hello/world"), + .ack_timeout_seconds = 5, + }; + + aws_mqtt_protocol_adapter_unsubscribe(fixture.protocol_adapter, &unsubscribe_options); + + // tear down the adapter, leaving the in-progress operations with nothing to call back into + s_aws_request_response_mqtt5_adapter_test_fixture_destroy_adapters(&fixture); + + // stop the mqtt client, which fails the pending MQTT operations + ASSERT_SUCCESS(aws_mqtt5_client_stop(client, NULL, NULL)); + + // wait for the stop to complete, which implies all the operations have been completed without calling back + // into a deleted adapter + aws_wait_for_n_lifecycle_events(&fixture.mqtt5_fixture, AWS_MQTT5_CLET_STOPPED, 1); + + // nothing to verify, we just don't want to crash + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_shutdown_while_pending, + s_request_response_mqtt5_protocol_adapter_shutdown_while_pending_fn) diff --git a/tests/v3/connection_state_test.c b/tests/v3/connection_state_test.c index 6a3e086f..65f8b9e7 100644 --- a/tests/v3/connection_state_test.c +++ b/tests/v3/connection_state_test.c @@ -3,661 +3,22 @@ * SPDX-License-Identifier: Apache-2.0. */ -#include "mqtt_mock_server_handler.h" - -#include +#include -#include -#include -#include +#include "mqtt311_testing_utils.h" +#include "mqtt_mock_server_handler.h" #include -#include - #include -#include - -static const int TEST_LOG_SUBJECT = 60000; -static const int ONE_SEC = 1000000000; -// The value is extract from aws-c-mqtt/source/client.c -static const int AWS_RESET_RECONNECT_BACKOFF_DELAY_SECONDS = 10; -static const uint64_t RECONNECT_BACKOFF_DELAY_ERROR_MARGIN_NANO_SECONDS = 500000000; -#define DEFAULT_MIN_RECONNECT_DELAY_SECONDS 1 - -#define DEFAULT_TEST_PING_TIMEOUT_MS 1000 -#define DEFAULT_TEST_KEEP_ALIVE_S 2 - -struct received_publish_packet { - struct aws_byte_buf topic; - struct aws_byte_buf payload; - bool dup; - enum aws_mqtt_qos qos; - bool retain; -}; - -struct mqtt_connection_state_test { - struct aws_allocator *allocator; - struct aws_channel *server_channel; - struct aws_channel_handler *mock_server; - struct aws_client_bootstrap *client_bootstrap; - struct aws_server_bootstrap *server_bootstrap; - struct aws_event_loop_group *el_group; - struct aws_host_resolver *host_resolver; - struct aws_socket_endpoint endpoint; - struct aws_socket *listener; - struct aws_mqtt_client *mqtt_client; - struct aws_mqtt_client_connection *mqtt_connection; - struct aws_socket_options socket_options; - - bool session_present; - bool connection_completed; - bool connection_success; - bool connection_failure; - bool client_disconnect_completed; - bool server_disconnect_completed; - bool connection_interrupted; - bool connection_resumed; - bool subscribe_completed; - bool listener_destroyed; - bool connection_terminated; - int interruption_error; - int subscribe_complete_error; - int op_complete_error; - enum aws_mqtt_connect_return_code mqtt_return_code; - int error; - struct aws_condition_variable cvar; - struct aws_mutex lock; - /* any published messages from mock server, that you may not subscribe to. (Which should not happen in real life) */ - struct aws_array_list any_published_messages; /* list of struct received_publish_packet */ - size_t any_publishes_received; - size_t expected_any_publishes; - /* the published messages from mock server, that you did subscribe to. */ - struct aws_array_list published_messages; /* list of struct received_publish_packet */ - size_t publishes_received; - size_t expected_publishes; - /* The returned QoS from mock server */ - struct aws_array_list qos_returned; /* list of uint_8 */ - size_t ops_completed; - size_t expected_ops_completed; - size_t connection_close_calls; /* All of the times on_connection_closed has been called */ - - size_t connection_termination_calls; /* How many times on_connection_termination has been called, should be 1 */ -}; - static struct mqtt_connection_state_test test_data = {0}; -static void s_on_any_publish_received( - struct aws_mqtt_client_connection *connection, - const struct aws_byte_cursor *topic, - const struct aws_byte_cursor *payload, - bool dup, - enum aws_mqtt_qos qos, - bool retain, - void *userdata); - -static void s_on_incoming_channel_setup_fn( - struct aws_server_bootstrap *bootstrap, - int error_code, - struct aws_channel *channel, - void *user_data) { - (void)bootstrap; - struct mqtt_connection_state_test *state_test_data = user_data; - - state_test_data->error = error_code; - - if (!error_code) { - aws_mutex_lock(&state_test_data->lock); - state_test_data->server_disconnect_completed = false; - aws_mutex_unlock(&state_test_data->lock); - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "server channel setup completed"); - - state_test_data->server_channel = channel; - struct aws_channel_slot *test_handler_slot = aws_channel_slot_new(channel); - aws_channel_slot_insert_end(channel, test_handler_slot); - mqtt_mock_server_handler_update_slot(state_test_data->mock_server, test_handler_slot); - aws_channel_slot_set_handler(test_handler_slot, state_test_data->mock_server); - } -} - -static void s_on_incoming_channel_shutdown_fn( - struct aws_server_bootstrap *bootstrap, - int error_code, - struct aws_channel *channel, - void *user_data) { - (void)bootstrap; - (void)error_code; - (void)channel; - struct mqtt_connection_state_test *state_test_data = user_data; - aws_mutex_lock(&state_test_data->lock); - state_test_data->server_disconnect_completed = true; - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "server channel shutdown completed"); - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static void s_on_listener_destroy(struct aws_server_bootstrap *bootstrap, void *user_data) { - (void)bootstrap; - struct mqtt_connection_state_test *state_test_data = user_data; - aws_mutex_lock(&state_test_data->lock); - state_test_data->listener_destroyed = true; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_listener_destroyed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->listener_destroyed; -} - -static void s_wait_on_listener_cleanup(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_listener_destroyed, state_test_data); - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_connection_interrupted(struct aws_mqtt_client_connection *connection, int error_code, void *userdata) { - (void)connection; - (void)error_code; - struct mqtt_connection_state_test *state_test_data = userdata; - - aws_mutex_lock(&state_test_data->lock); - state_test_data->connection_interrupted = true; - state_test_data->interruption_error = error_code; - aws_mutex_unlock(&state_test_data->lock); - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "connection interrupted"); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_connection_interrupted(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_interrupted; -} - -static void s_wait_for_interrupt_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_connection_interrupted, state_test_data); - state_test_data->connection_interrupted = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_connection_resumed( - struct aws_mqtt_client_connection *connection, - enum aws_mqtt_connect_return_code return_code, - bool session_present, - void *userdata) { - (void)connection; - (void)return_code; - (void)session_present; - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "reconnect completed"); - - struct mqtt_connection_state_test *state_test_data = userdata; - - aws_mutex_lock(&state_test_data->lock); - state_test_data->connection_resumed = true; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_connection_resumed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_resumed; -} - -static void s_wait_for_reconnect_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_connection_resumed, state_test_data); - state_test_data->connection_resumed = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_connection_success( - struct aws_mqtt_client_connection *connection, - enum aws_mqtt_connect_return_code return_code, - bool session_present, - void *userdata) { - (void)connection; - struct mqtt_connection_state_test *state_test_data = userdata; - aws_mutex_lock(&state_test_data->lock); - - state_test_data->session_present = session_present; - state_test_data->mqtt_return_code = return_code; - state_test_data->connection_success = true; - aws_mutex_unlock(&state_test_data->lock); - - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static void s_on_connection_failure(struct aws_mqtt_client_connection *connection, int error_code, void *userdata) { - (void)connection; - struct mqtt_connection_state_test *state_test_data = userdata; - aws_mutex_lock(&state_test_data->lock); - - state_test_data->error = error_code; - state_test_data->connection_failure = true; - aws_mutex_unlock(&state_test_data->lock); - - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_connection_succeed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_success; -} - -static bool s_is_connection_failed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_failure; -} - -static void s_wait_for_connection_to_succeed(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_connection_succeed, state_test_data); - state_test_data->connection_success = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_wait_for_connection_to_fail(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_connection_failed, state_test_data); - state_test_data->connection_failure = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static bool s_is_termination_completed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_terminated; -} - -static void s_wait_for_termination_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_termination_completed, state_test_data); - state_test_data->connection_terminated = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_connection_termination_fn(void *userdata) { - struct mqtt_connection_state_test *state_test_data = (struct mqtt_connection_state_test *)userdata; - - aws_mutex_lock(&state_test_data->lock); - state_test_data->connection_termination_calls += 1; - state_test_data->connection_terminated = true; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -/** sets up a unix domain socket server and socket options. Creates an mqtt connection configured to use - * the domain socket. - */ static int s_setup_mqtt_server_fn(struct aws_allocator *allocator, void *ctx) { - aws_mqtt_library_init(allocator); - - struct mqtt_connection_state_test *state_test_data = ctx; - - AWS_ZERO_STRUCT(*state_test_data); - - state_test_data->allocator = allocator; - state_test_data->el_group = aws_event_loop_group_new_default(allocator, 1, NULL); - - state_test_data->mock_server = new_mqtt_mock_server(allocator); - ASSERT_NOT_NULL(state_test_data->mock_server); - - state_test_data->server_bootstrap = aws_server_bootstrap_new(allocator, state_test_data->el_group); - ASSERT_NOT_NULL(state_test_data->server_bootstrap); - - struct aws_socket_options socket_options = { - .connect_timeout_ms = 100, - .domain = AWS_SOCKET_LOCAL, - }; - - state_test_data->socket_options = socket_options; - ASSERT_SUCCESS(aws_condition_variable_init(&state_test_data->cvar)); - ASSERT_SUCCESS(aws_mutex_init(&state_test_data->lock)); - - aws_socket_endpoint_init_local_address_for_test(&state_test_data->endpoint); - - struct aws_server_socket_channel_bootstrap_options server_bootstrap_options = { - .bootstrap = state_test_data->server_bootstrap, - .host_name = state_test_data->endpoint.address, - .port = state_test_data->endpoint.port, - .socket_options = &state_test_data->socket_options, - .incoming_callback = s_on_incoming_channel_setup_fn, - .shutdown_callback = s_on_incoming_channel_shutdown_fn, - .destroy_callback = s_on_listener_destroy, - .user_data = state_test_data, - }; - state_test_data->listener = aws_server_bootstrap_new_socket_listener(&server_bootstrap_options); - - ASSERT_NOT_NULL(state_test_data->listener); - - struct aws_host_resolver_default_options resolver_options = { - .el_group = state_test_data->el_group, - .max_entries = 1, - }; - state_test_data->host_resolver = aws_host_resolver_new_default(allocator, &resolver_options); - - struct aws_client_bootstrap_options bootstrap_options = { - .event_loop_group = state_test_data->el_group, - .user_data = state_test_data, - .host_resolver = state_test_data->host_resolver, - }; - - state_test_data->client_bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); - - state_test_data->mqtt_client = aws_mqtt_client_new(allocator, state_test_data->client_bootstrap); - state_test_data->mqtt_connection = aws_mqtt_client_connection_new(state_test_data->mqtt_client); - ASSERT_NOT_NULL(state_test_data->mqtt_connection); - - ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_interruption_handlers( - state_test_data->mqtt_connection, - s_on_connection_interrupted, - state_test_data, - s_on_connection_resumed, - state_test_data)); - - ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_result_handlers( - state_test_data->mqtt_connection, - s_on_connection_success, - state_test_data, - s_on_connection_failure, - state_test_data)); - - ASSERT_SUCCESS(aws_mqtt_client_connection_set_on_any_publish_handler( - state_test_data->mqtt_connection, s_on_any_publish_received, state_test_data)); - - ASSERT_SUCCESS(aws_array_list_init_dynamic( - &state_test_data->published_messages, allocator, 4, sizeof(struct received_publish_packet))); - ASSERT_SUCCESS(aws_array_list_init_dynamic( - &state_test_data->any_published_messages, allocator, 4, sizeof(struct received_publish_packet))); - ASSERT_SUCCESS(aws_array_list_init_dynamic(&state_test_data->qos_returned, allocator, 2, sizeof(uint8_t))); - - ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_termination_handler( - state_test_data->mqtt_connection, s_on_connection_termination_fn, state_test_data)); - - return AWS_OP_SUCCESS; -} - -static void s_received_publish_packet_list_clean_up(struct aws_array_list *list) { - for (size_t i = 0; i < aws_array_list_length(list); ++i) { - struct received_publish_packet *val_ptr = NULL; - aws_array_list_get_at_ptr(list, (void **)&val_ptr, i); - aws_byte_buf_clean_up(&val_ptr->payload); - aws_byte_buf_clean_up(&val_ptr->topic); - } - aws_array_list_clean_up(list); + return aws_test311_setup_mqtt_server_fn(allocator, ctx); } static int s_clean_up_mqtt_server_fn(struct aws_allocator *allocator, int setup_result, void *ctx) { - (void)allocator; - - if (!setup_result) { - struct mqtt_connection_state_test *state_test_data = ctx; - - s_received_publish_packet_list_clean_up(&state_test_data->published_messages); - s_received_publish_packet_list_clean_up(&state_test_data->any_published_messages); - aws_array_list_clean_up(&state_test_data->qos_returned); - aws_mqtt_client_connection_release(state_test_data->mqtt_connection); - - s_wait_for_termination_to_complete(state_test_data); - ASSERT_UINT_EQUALS(1, state_test_data->connection_termination_calls); - - aws_mqtt_client_release(state_test_data->mqtt_client); - aws_client_bootstrap_release(state_test_data->client_bootstrap); - aws_host_resolver_release(state_test_data->host_resolver); - aws_server_bootstrap_destroy_socket_listener(state_test_data->server_bootstrap, state_test_data->listener); - s_wait_on_listener_cleanup(state_test_data); - aws_server_bootstrap_release(state_test_data->server_bootstrap); - aws_event_loop_group_release(state_test_data->el_group); - destroy_mqtt_mock_server(state_test_data->mock_server); - } - - aws_mqtt_library_clean_up(); - return AWS_OP_SUCCESS; -} - -static void s_on_connection_complete_fn( - struct aws_mqtt_client_connection *connection, - int error_code, - enum aws_mqtt_connect_return_code return_code, - bool session_present, - void *userdata) { - (void)connection; - struct mqtt_connection_state_test *state_test_data = userdata; - aws_mutex_lock(&state_test_data->lock); - - state_test_data->session_present = session_present; - state_test_data->mqtt_return_code = return_code; - state_test_data->error = error_code; - state_test_data->connection_completed = true; - aws_mutex_unlock(&state_test_data->lock); - - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_connection_completed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_completed; -} - -static void s_wait_for_connection_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_connection_completed, state_test_data); - state_test_data->connection_completed = false; - aws_mutex_unlock(&state_test_data->lock); -} - -void s_on_disconnect_fn(struct aws_mqtt_client_connection *connection, void *userdata) { - (void)connection; - struct mqtt_connection_state_test *state_test_data = userdata; - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "disconnect completed"); - aws_mutex_lock(&state_test_data->lock); - state_test_data->client_disconnect_completed = true; - aws_mutex_unlock(&state_test_data->lock); - - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_disconnect_completed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->client_disconnect_completed && state_test_data->server_disconnect_completed; -} - -static void s_wait_for_disconnect_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_disconnect_completed, state_test_data); - state_test_data->client_disconnect_completed = false; - state_test_data->server_disconnect_completed = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_any_publish_received( - struct aws_mqtt_client_connection *connection, - const struct aws_byte_cursor *topic, - const struct aws_byte_cursor *payload, - bool dup, - enum aws_mqtt_qos qos, - bool retain, - void *userdata) { - (void)connection; - struct mqtt_connection_state_test *state_test_data = userdata; - - struct aws_byte_buf payload_cp; - aws_byte_buf_init_copy_from_cursor(&payload_cp, state_test_data->allocator, *payload); - struct aws_byte_buf topic_cp; - aws_byte_buf_init_copy_from_cursor(&topic_cp, state_test_data->allocator, *topic); - struct received_publish_packet received_packet = { - .payload = payload_cp, - .topic = topic_cp, - .dup = dup, - .qos = qos, - .retain = retain, - }; - - aws_mutex_lock(&state_test_data->lock); - aws_array_list_push_back(&state_test_data->any_published_messages, &received_packet); - state_test_data->any_publishes_received++; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_any_publish_received(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->any_publishes_received == state_test_data->expected_any_publishes; -} - -static void s_wait_for_any_publish(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_any_publish_received, state_test_data); - state_test_data->any_publishes_received = 0; - state_test_data->expected_any_publishes = 0; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_publish_received( - struct aws_mqtt_client_connection *connection, - const struct aws_byte_cursor *topic, - const struct aws_byte_cursor *payload, - bool dup, - enum aws_mqtt_qos qos, - bool retain, - void *userdata) { - - (void)connection; - (void)topic; - struct mqtt_connection_state_test *state_test_data = userdata; - - struct aws_byte_buf payload_cp; - aws_byte_buf_init_copy_from_cursor(&payload_cp, state_test_data->allocator, *payload); - struct aws_byte_buf topic_cp; - aws_byte_buf_init_copy_from_cursor(&topic_cp, state_test_data->allocator, *topic); - struct received_publish_packet received_packet = { - .payload = payload_cp, - .topic = topic_cp, - .dup = dup, - .qos = qos, - .retain = retain, - }; - - aws_mutex_lock(&state_test_data->lock); - aws_array_list_push_back(&state_test_data->published_messages, &received_packet); - state_test_data->publishes_received++; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_publish_received(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->publishes_received == state_test_data->expected_publishes; -} - -static void s_wait_for_publish(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_publish_received, state_test_data); - state_test_data->publishes_received = 0; - state_test_data->expected_publishes = 0; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_suback( - struct aws_mqtt_client_connection *connection, - uint16_t packet_id, - const struct aws_byte_cursor *topic, - enum aws_mqtt_qos qos, - int error_code, - void *userdata) { - (void)connection; - (void)packet_id; - (void)topic; - - struct mqtt_connection_state_test *state_test_data = userdata; - - aws_mutex_lock(&state_test_data->lock); - if (!error_code) { - aws_array_list_push_back(&state_test_data->qos_returned, &qos); - } - state_test_data->subscribe_completed = true; - state_test_data->subscribe_complete_error = error_code; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_subscribe_completed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->subscribe_completed; -} - -static void s_wait_for_subscribe_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_subscribe_completed, state_test_data); - state_test_data->subscribe_completed = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_multi_suback( - struct aws_mqtt_client_connection *connection, - uint16_t packet_id, - const struct aws_array_list *topic_subacks, /* contains aws_mqtt_topic_subscription pointers */ - int error_code, - void *userdata) { - (void)connection; - (void)packet_id; - (void)topic_subacks; - (void)error_code; - - struct mqtt_connection_state_test *state_test_data = userdata; - - aws_mutex_lock(&state_test_data->lock); - state_test_data->subscribe_completed = true; - if (!error_code) { - size_t length = aws_array_list_length(topic_subacks); - for (size_t i = 0; i < length; ++i) { - struct aws_mqtt_topic_subscription *subscription = NULL; - aws_array_list_get_at(topic_subacks, &subscription, i); - aws_array_list_push_back(&state_test_data->qos_returned, &subscription->qos); - } - } - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static void s_on_op_complete( - struct aws_mqtt_client_connection *connection, - uint16_t packet_id, - int error_code, - void *userdata) { - (void)connection; - (void)packet_id; - - struct mqtt_connection_state_test *state_test_data = userdata; - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "pub op completed"); - aws_mutex_lock(&state_test_data->lock); - state_test_data->ops_completed++; - state_test_data->op_complete_error = error_code; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_ops_completed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->ops_completed == state_test_data->expected_ops_completed; -} - -static void s_wait_for_ops_completed(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_for_pred( - &state_test_data->cvar, &state_test_data->lock, 10000000000, s_is_ops_completed, state_test_data); - aws_mutex_unlock(&state_test_data->lock); + return aws_test311_clean_up_mqtt_server_fn(allocator, setup_result, ctx); } /* @@ -673,14 +34,14 @@ static int s_test_mqtt_connect_disconnect_fn(struct aws_allocator *allocator, vo .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -730,14 +91,14 @@ static int s_test_mqtt_connect_set_will_login_fn(struct aws_allocator *allocator .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -766,7 +127,7 @@ static int s_test_mqtt_connect_set_will_login_fn(struct aws_allocator *allocator /* Connect to the mock server again. If set will&loggin message is not called before the next connect, the * will&loggin message will still be there and be sent to the server again */ ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); /* The second CONNECT packet */ @@ -784,9 +145,9 @@ static int s_test_mqtt_connect_set_will_login_fn(struct aws_allocator *allocator ASSERT_TRUE(aws_byte_cursor_eq(&received_packet->password, &password)); /* disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* set new will & loggin message, before next connect, the next CONNECT packet will contain the new information */ struct aws_byte_cursor new_will_payload = aws_byte_cursor_from_c_str("this is a new will."); @@ -803,7 +164,7 @@ static int s_test_mqtt_connect_set_will_login_fn(struct aws_allocator *allocator /* connect again */ ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); /* The third CONNECT packet */ @@ -821,9 +182,9 @@ static int s_test_mqtt_connect_set_will_login_fn(struct aws_allocator *allocator ASSERT_TRUE(aws_byte_cursor_eq(&received_packet->password, &new_password)); /* disconnect. FINISHED */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -852,14 +213,14 @@ static int s_test_mqtt_connection_interrupted_fn(struct aws_allocator *allocator .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; aws_mqtt_client_connection_set_reconnect_timeout( state_test_data->mqtt_connection, MIN_RECONNECT_DELAY_SECONDS, MAX_RECONNECT_DELAY_SECONDS); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* shut it down and make sure the client automatically reconnects.*/ uint64_t now = 0; @@ -867,7 +228,7 @@ static int s_test_mqtt_connection_interrupted_fn(struct aws_allocator *allocator uint64_t start_shutdown = now; aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&now); uint64_t reconnect_complete = now; @@ -877,9 +238,9 @@ static int s_test_mqtt_connection_interrupted_fn(struct aws_allocator *allocator aws_timestamp_convert(elapsed_time, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL) >= MIN_RECONNECT_DELAY_SECONDS); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -922,21 +283,21 @@ static int s_test_mqtt_connection_timeout_fn(struct aws_allocator *allocator, vo .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, }; mqtt_mock_server_set_max_ping_resp(state_test_data->mock_server, 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* this should take about 1.1 seconds for the timeout and reconnect.*/ - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); ASSERT_INT_EQUALS(AWS_ERROR_MQTT_TIMEOUT, state_test_data->interruption_error); @@ -962,14 +323,14 @@ static int s_test_mqtt_connection_any_publish_fn(struct aws_allocator *allocator .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor topic_1 = aws_byte_cursor_from_c_str("/test/topic1"); struct aws_byte_cursor topic_2 = aws_byte_cursor_from_c_str("/test/topic2"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* NOTE: mock server sends to client with no subscription at all, which should not happen in the real world! */ state_test_data->expected_any_publishes = 2; @@ -990,12 +351,12 @@ static int s_test_mqtt_connection_any_publish_fn(struct aws_allocator *allocator AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 2); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1048,14 +409,14 @@ static int s_test_mqtt_connection_connack_timeout_fn(struct aws_allocator *alloc .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, }; mqtt_mock_server_set_max_connack(state_test_data->mock_server, 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); ASSERT_INT_EQUALS(AWS_ERROR_MQTT_TIMEOUT, state_test_data->error); @@ -1080,14 +441,14 @@ static int s_test_mqtt_connection_failure_callback_fn(struct aws_allocator *allo .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, }; mqtt_mock_server_set_max_connack(state_test_data->mock_server, 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_fail(state_test_data); + aws_test311_wait_for_connection_to_fail(state_test_data); ASSERT_INT_EQUALS(AWS_ERROR_MQTT_TIMEOUT, state_test_data->error); @@ -1112,11 +473,11 @@ static int s_test_mqtt_connection_success_callback_fn(struct aws_allocator *allo .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_succeed(state_test_data); + aws_test311_wait_for_connection_to_succeed(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1129,9 +490,9 @@ static int s_test_mqtt_connection_success_callback_fn(struct aws_allocator *allo ASSERT_TRUE(aws_byte_cursor_eq(&received_packet->client_identifier, &connection_options.client_id)); // Disconnect and finish - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -1155,7 +516,7 @@ static int s_test_mqtt_subscribe_fn(struct aws_allocator *allocator, void *ctx) .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor sub_topic = aws_byte_cursor_from_c_str("/test/topic"); @@ -1164,17 +525,17 @@ static int s_test_mqtt_subscribe_fn(struct aws_allocator *allocator, void *ctx) state_test_data->mqtt_connection, &sub_topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_id > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); state_test_data->expected_publishes = 2; state_test_data->expected_any_publishes = 2; @@ -1195,13 +556,13 @@ static int s_test_mqtt_subscribe_fn(struct aws_allocator *allocator, void *ctx) AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); - s_wait_for_publish(state_test_data); - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 2); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1282,7 +643,7 @@ static int s_test_mqtt_subscribe_incoming_dup_fn(struct aws_allocator *allocator .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor subscribed_topic = aws_byte_cursor_from_c_str("/test/topic"); @@ -1292,16 +653,16 @@ static int s_test_mqtt_subscribe_incoming_dup_fn(struct aws_allocator *allocator state_test_data->mqtt_connection, &subscribed_topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_id > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); state_test_data->expected_publishes = 4; state_test_data->expected_any_publishes = 8; @@ -1330,13 +691,13 @@ static int s_test_mqtt_subscribe_incoming_dup_fn(struct aws_allocator *allocator false /*retain*/)); } - s_wait_for_publish(state_test_data); - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 8); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1419,11 +780,11 @@ static int s_test_mqtt_connect_subscribe_fail_from_broker_fn(struct aws_allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disable the auto ACK packets sent by the server, we will send failure SUBACK */ mqtt_mock_server_disable_auto_ack(state_test_data->mock_server); @@ -1434,15 +795,15 @@ static int s_test_mqtt_connect_subscribe_fail_from_broker_fn(struct aws_allocato state_test_data->mqtt_connection, &sub_topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_id > 0); ASSERT_SUCCESS(mqtt_mock_server_send_single_suback(state_test_data->mock_server, packet_id, AWS_MQTT_QOS_FAILURE)); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); /* Check the subscribe returned QoS is failure */ size_t length = aws_array_list_length(&state_test_data->qos_returned); ASSERT_UINT_EQUALS(1, length); @@ -1450,9 +811,9 @@ static int s_test_mqtt_connect_subscribe_fail_from_broker_fn(struct aws_allocato ASSERT_SUCCESS(aws_array_list_get_at(&state_test_data->qos_returned, &qos, 0)); ASSERT_UINT_EQUALS(AWS_MQTT_QOS_FAILURE, qos); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -1476,7 +837,7 @@ static int s_test_mqtt_subscribe_multi_fn(struct aws_allocator *allocator, void .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor sub_topic_1 = aws_byte_cursor_from_c_str("/test/topic1"); @@ -1485,14 +846,14 @@ static int s_test_mqtt_subscribe_multi_fn(struct aws_allocator *allocator, void struct aws_mqtt_topic_subscription sub1 = { .topic = sub_topic_1, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; struct aws_mqtt_topic_subscription sub2 = { .topic = sub_topic_2, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; @@ -1506,13 +867,13 @@ static int s_test_mqtt_subscribe_multi_fn(struct aws_allocator *allocator, void aws_array_list_push_back(&topic_filters, &sub2); uint16_t packet_id = aws_mqtt_client_connection_subscribe_multiple( - state_test_data->mqtt_connection, &topic_filters, s_on_multi_suback, state_test_data); + state_test_data->mqtt_connection, &topic_filters, aws_test311_on_multi_suback, state_test_data); ASSERT_TRUE(packet_id > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); /* Check the subscribe returned QoS is expected */ size_t length = aws_array_list_length(&state_test_data->qos_returned); ASSERT_UINT_EQUALS(2, length); @@ -1539,7 +900,7 @@ static int s_test_mqtt_subscribe_multi_fn(struct aws_allocator *allocator, void false /*dup*/, AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); - s_wait_for_publish(state_test_data); + aws_test311_wait_for_publish(state_test_data); /* Let's do another publish on a topic that is not subscribed by client. * This can happen if the Server automatically assigned a subscription to the Client */ @@ -1553,13 +914,13 @@ static int s_test_mqtt_subscribe_multi_fn(struct aws_allocator *allocator, void false /*dup*/, AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 3); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1627,7 +988,7 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor sub_topic_1 = aws_byte_cursor_from_c_str("/test/topic1"); @@ -1636,14 +997,14 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx struct aws_mqtt_topic_subscription sub1 = { .topic = sub_topic_1, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; struct aws_mqtt_topic_subscription sub2 = { .topic = sub_topic_2, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; @@ -1657,13 +1018,13 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx aws_array_list_push_back(&topic_filters, &sub2); uint16_t sub_packet_id = aws_mqtt_client_connection_subscribe_multiple( - state_test_data->mqtt_connection, &topic_filters, s_on_multi_suback, state_test_data); + state_test_data->mqtt_connection, &topic_filters, aws_test311_on_multi_suback, state_test_data); ASSERT_TRUE(sub_packet_id > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); state_test_data->expected_any_publishes = 2; struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); @@ -1682,7 +1043,7 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx false /*dup*/, AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 2); aws_mutex_lock(&state_test_data->lock); @@ -1690,7 +1051,7 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx aws_mutex_unlock(&state_test_data->lock); /* unsubscribe to the first topic */ uint16_t unsub_packet_id = aws_mqtt_client_connection_unsubscribe( - state_test_data->mqtt_connection, &sub_topic_1, s_on_op_complete, state_test_data); + state_test_data->mqtt_connection, &sub_topic_1, aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(unsub_packet_id > 0); /* Even when the UNSUBACK has not received, the client will not invoke the on_pub callback for that topic */ ASSERT_SUCCESS(mqtt_mock_server_send_publish( @@ -1708,13 +1069,13 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); state_test_data->expected_any_publishes = 2; - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 2); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1797,7 +1158,7 @@ static int s_test_mqtt_resubscribe_fn(struct aws_allocator *allocator, void *ctx .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor sub_topic_1 = aws_byte_cursor_from_c_str("/test/topic1"); @@ -1807,21 +1168,21 @@ static int s_test_mqtt_resubscribe_fn(struct aws_allocator *allocator, void *ctx struct aws_mqtt_topic_subscription sub1 = { .topic = sub_topic_1, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; struct aws_mqtt_topic_subscription sub2 = { .topic = sub_topic_2, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; struct aws_mqtt_topic_subscription sub3 = { .topic = sub_topic_3, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; @@ -1836,31 +1197,31 @@ static int s_test_mqtt_resubscribe_fn(struct aws_allocator *allocator, void *ctx aws_array_list_push_back(&topic_filters, &sub3); uint16_t sub_packet_id = aws_mqtt_client_connection_subscribe_multiple( - state_test_data->mqtt_connection, &topic_filters, s_on_multi_suback, state_test_data); + state_test_data->mqtt_connection, &topic_filters, aws_test311_on_multi_suback, state_test_data); ASSERT_TRUE(sub_packet_id > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); aws_mutex_lock(&state_test_data->lock); state_test_data->expected_ops_completed = 1; aws_mutex_unlock(&state_test_data->lock); /* unsubscribe to the first topic */ uint16_t unsub_packet_id = aws_mqtt_client_connection_unsubscribe( - state_test_data->mqtt_connection, &sub_topic_1, s_on_op_complete, state_test_data); + state_test_data->mqtt_connection, &sub_topic_1, aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(unsub_packet_id > 0); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* client still subscribes to topic_2 & topic_3 */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* reconnection to the same server */ ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Get all the packets out of the way */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1870,14 +1231,14 @@ static int s_test_mqtt_resubscribe_fn(struct aws_allocator *allocator, void *ctx ASSERT_UINT_EQUALS(AWS_MQTT_PACKET_CONNECT, t_received_packet->type); /* resubscribes to topic_2 & topic_3 */ - uint16_t resub_packet_id = - aws_mqtt_resubscribe_existing_topics(state_test_data->mqtt_connection, s_on_multi_suback, state_test_data); + uint16_t resub_packet_id = aws_mqtt_resubscribe_existing_topics( + state_test_data->mqtt_connection, aws_test311_on_multi_suback, state_test_data); ASSERT_TRUE(resub_packet_id > 0); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); struct mqtt_decoded_packet *received_packet = @@ -1915,7 +1276,7 @@ static int s_test_mqtt_publish_fn(struct aws_allocator *allocator, void *ctx) { .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); @@ -1923,7 +1284,7 @@ static int s_test_mqtt_publish_fn(struct aws_allocator *allocator, void *ctx) { struct aws_byte_cursor payload_2 = aws_byte_cursor_from_c_str("Test Message 2"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); aws_mutex_lock(&state_test_data->lock); state_test_data->expected_ops_completed = 3; @@ -1934,7 +1295,7 @@ static int s_test_mqtt_publish_fn(struct aws_allocator *allocator, void *ctx) { AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); uint16_t packet_id_2 = aws_mqtt_client_connection_publish( @@ -1943,7 +1304,7 @@ static int s_test_mqtt_publish_fn(struct aws_allocator *allocator, void *ctx) { AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_2, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_2 > 0); @@ -1954,15 +1315,15 @@ static int s_test_mqtt_publish_fn(struct aws_allocator *allocator, void *ctx) { AWS_MQTT_QOS_AT_LEAST_ONCE, false, NULL, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_3 > 0); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -2012,7 +1373,7 @@ static int s_test_mqtt_publish_payload_fn(struct aws_allocator *allocator, void .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); @@ -2022,7 +1383,7 @@ static int s_test_mqtt_publish_payload_fn(struct aws_allocator *allocator, void struct aws_byte_cursor payload_curser = aws_byte_cursor_from_buf(&buf_payload); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); aws_mutex_lock(&state_test_data->lock); state_test_data->expected_ops_completed = 1; @@ -2033,17 +1394,17 @@ static int s_test_mqtt_publish_payload_fn(struct aws_allocator *allocator, void AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_curser, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id > 0); /* clean up the payload buf, as user don't need to manage the buf and keep it valid until publish completes */ aws_byte_buf_clean_up(&buf_payload); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -2087,19 +1448,19 @@ static int s_test_mqtt_connection_offline_publish_fn(struct aws_allocator *alloc .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); mqtt_mock_server_set_max_connack(state_test_data->mock_server, 0); /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_interrupt_to_complete(state_test_data); + aws_test311_wait_for_interrupt_to_complete(state_test_data); state_test_data->server_disconnect_completed = false; @@ -2118,7 +1479,7 @@ static int s_test_mqtt_connection_offline_publish_fn(struct aws_allocator *alloc AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_publish( @@ -2127,21 +1488,21 @@ static int s_test_mqtt_connection_offline_publish_fn(struct aws_allocator *alloc AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_2, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); aws_mutex_lock(&state_test_data->lock); ASSERT_FALSE(state_test_data->connection_resumed); aws_mutex_unlock(&state_test_data->lock); mqtt_mock_server_set_max_connack(state_test_data->mock_server, SIZE_MAX); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); aws_mutex_lock(&state_test_data->lock); ASSERT_TRUE(state_test_data->connection_resumed); aws_mutex_unlock(&state_test_data->lock); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -2205,19 +1566,19 @@ static int s_test_mqtt_connection_disconnect_while_reconnecting(struct aws_alloc .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); mqtt_mock_server_set_max_connack(state_test_data->mock_server, 0); /* shut it down and the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_interrupt_to_complete(state_test_data); + aws_test311_wait_for_interrupt_to_complete(state_test_data); struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); @@ -2234,7 +1595,7 @@ static int s_test_mqtt_connection_disconnect_while_reconnecting(struct aws_alloc AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_publish( @@ -2243,15 +1604,15 @@ static int s_test_mqtt_connection_disconnect_while_reconnecting(struct aws_alloc AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_2, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); aws_mqtt_client_connection_release(state_test_data->mqtt_connection); state_test_data->mqtt_connection = NULL; - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); return AWS_OP_SUCCESS; } @@ -2281,7 +1642,7 @@ static int s_test_mqtt_connection_closes_while_making_requests_fn(struct aws_all .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; @@ -2290,7 +1651,7 @@ static int s_test_mqtt_connection_closes_while_making_requests_fn(struct aws_all struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); aws_mutex_lock(&state_test_data->lock); state_test_data->expected_ops_completed = 1; @@ -2310,16 +1671,16 @@ static int s_test_mqtt_connection_closes_while_making_requests_fn(struct aws_all AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); - s_wait_for_reconnect_to_complete(state_test_data); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2381,7 +1742,7 @@ static int s_test_mqtt_connection_resend_packets_fn(struct aws_allocator *alloca .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; @@ -2393,7 +1754,7 @@ static int s_test_mqtt_connection_resend_packets_fn(struct aws_allocator *alloca struct aws_byte_cursor payload_3 = aws_byte_cursor_from_c_str("Test Message 3"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ mqtt_mock_server_disable_auto_ack(state_test_data->mock_server); @@ -2408,10 +1769,10 @@ static int s_test_mqtt_connection_resend_packets_fn(struct aws_allocator *alloca state_test_data->mqtt_connection, &sub_topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_ids[1] > 0); @@ -2435,15 +1796,15 @@ static int s_test_mqtt_connection_resend_packets_fn(struct aws_allocator *alloca /* shutdown the channel for some error */ aws_channel_shutdown(state_test_data->server_channel, AWS_ERROR_INVALID_STATE); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* Wait again, and ensure the publishes have been resent */ aws_thread_current_sleep(ONE_SEC); ASSERT_SUCCESS(s_check_resend_packets( state_test_data->mock_server, packet_count, true, packet_ids, AWS_ARRAY_SIZE(packet_ids))); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2468,7 +1829,7 @@ static int s_test_mqtt_connection_not_retry_publish_QoS_0_fn(struct aws_allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ }; @@ -2477,7 +1838,7 @@ static int s_test_mqtt_connection_not_retry_publish_QoS_0_fn(struct aws_allocato struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* kill the connection */ aws_channel_shutdown(state_test_data->server_channel, AWS_ERROR_INVALID_STATE); @@ -2493,23 +1854,23 @@ static int s_test_mqtt_connection_not_retry_publish_QoS_0_fn(struct aws_allocato AWS_MQTT_QOS_AT_MOST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); /* publish should complete after the shutdown */ - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* wait for reconnect */ - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* Check all received packets, no publish packets ever received by the server. Because the connection lost before it * ever get sent. */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); ASSERT_NULL( mqtt_mock_server_find_decoded_packet_by_type(state_test_data->mock_server, 0, AWS_MQTT_PACKET_PUBLISH, NULL)); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2535,7 +1896,7 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .protocol_operation_timeout_ms = 3000, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ @@ -2546,7 +1907,7 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato struct aws_byte_cursor sub_topic = aws_byte_cursor_from_c_str("/test/topic"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_channel_handler *handler = state_test_data->mock_server; /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ @@ -2563,7 +1924,7 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); /* make another subscribe */ @@ -2574,12 +1935,12 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato NULL, NULL, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_id_2 > 0); /* wait for reconnect */ - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* Wait for 1 sec. ensure all the requests have been received by the server */ aws_thread_current_sleep(ONE_SEC); @@ -2594,9 +1955,9 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato mqtt_mock_server_enable_auto_ack(handler); /* Kill the connection again, the requests will be retried since the response has not been received yet. */ aws_channel_shutdown(state_test_data->server_channel, AWS_ERROR_INVALID_STATE); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* subscribe should be able to completed now */ - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); /* Check all received packets, subscribe and publish has been resent */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(handler)); @@ -2606,9 +1967,9 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato mqtt_mock_server_find_decoded_packet_by_type(handler, packet_count, AWS_MQTT_PACKET_SUBSCRIBE, NULL)); ASSERT_NOT_NULL(mqtt_mock_server_find_decoded_packet_by_type(handler, packet_count, AWS_MQTT_PACKET_PUBLISH, NULL)); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2635,7 +1996,7 @@ static int s_test_mqtt_connection_not_resend_packets_on_healthy_connection_fn( .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; @@ -2645,7 +2006,7 @@ static int s_test_mqtt_connection_not_resend_packets_on_healthy_connection_fn( struct aws_byte_cursor sub_topic = aws_byte_cursor_from_c_str("/test/topic"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_channel_handler *handler = state_test_data->mock_server; /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ @@ -2661,7 +2022,7 @@ static int s_test_mqtt_connection_not_resend_packets_on_healthy_connection_fn( AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); /* make another subscribe */ @@ -2672,7 +2033,7 @@ static int s_test_mqtt_connection_not_resend_packets_on_healthy_connection_fn( NULL, NULL, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_id_2 > 0); @@ -2693,9 +2054,9 @@ static int s_test_mqtt_connection_not_resend_packets_on_healthy_connection_fn( mqtt_mock_server_find_decoded_packet_by_type(handler, pre_index + 1, AWS_MQTT_PACKET_SUBSCRIBE, NULL)); } - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2723,17 +2084,17 @@ static int s_test_mqtt_connection_destory_pending_requests_fn(struct aws_allocat AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_subscribe( state_test_data->mqtt_connection, &topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data) > 0); return AWS_OP_SUCCESS; @@ -2757,13 +2118,13 @@ static int s_test_mqtt_clean_session_not_retry_fn(struct aws_allocator *allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_channel_handler *handler = state_test_data->mock_server; mqtt_mock_server_disable_auto_ack(handler); @@ -2777,33 +2138,33 @@ static int s_test_mqtt_clean_session_not_retry_fn(struct aws_allocator *allocato AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_subscribe( state_test_data->mqtt_connection, &topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data) > 0); aws_mutex_lock(&state_test_data->lock); state_test_data->expected_ops_completed = 1; aws_mutex_unlock(&state_test_data->lock); /* Shutdown the connection */ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_interrupt_to_complete(state_test_data); + aws_test311_wait_for_interrupt_to_complete(state_test_data); /* Once the connection lost, the requests will fail */ ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION); ASSERT_UINT_EQUALS(state_test_data->subscribe_complete_error, AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2826,7 +2187,7 @@ static int s_test_mqtt_clean_session_discard_previous_fn(struct aws_allocator *a .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ }; @@ -2844,25 +2205,25 @@ static int s_test_mqtt_clean_session_discard_previous_fn(struct aws_allocator *a AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_subscribe( state_test_data->mqtt_connection, &topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data) > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_channel_handler *handler = state_test_data->mock_server; - s_wait_for_ops_completed(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION); ASSERT_UINT_EQUALS(state_test_data->subscribe_complete_error, AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION); @@ -2872,9 +2233,9 @@ static int s_test_mqtt_clean_session_discard_previous_fn(struct aws_allocator *a ASSERT_NULL(mqtt_mock_server_find_decoded_packet_by_type(handler, 0, AWS_MQTT_PACKET_SUBSCRIBE, NULL)); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2896,7 +2257,7 @@ static int s_test_mqtt_clean_session_keep_next_session_fn(struct aws_allocator * .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ }; @@ -2916,23 +2277,23 @@ static int s_test_mqtt_clean_session_keep_next_session_fn(struct aws_allocator * AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_subscribe( state_test_data->mqtt_connection, &topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data) > 0); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_channel_handler *handler = state_test_data->mock_server; - s_wait_for_ops_completed(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_SUCCESS); ASSERT_UINT_EQUALS(state_test_data->subscribe_complete_error, AWS_ERROR_SUCCESS); @@ -2942,9 +2303,9 @@ static int s_test_mqtt_clean_session_keep_next_session_fn(struct aws_allocator * ASSERT_NOT_NULL(mqtt_mock_server_find_decoded_packet_by_type(handler, 0, AWS_MQTT_PACKET_SUBSCRIBE, NULL)); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2968,7 +2329,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_fn(struct aws_allocator * .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .protocol_operation_timeout_ms = 3000, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ @@ -2978,7 +2339,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_fn(struct aws_allocator * struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ mqtt_mock_server_disable_auto_ack(state_test_data->mock_server); @@ -2993,17 +2354,17 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_fn(struct aws_allocator * AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); /* publish should complete after the shutdown */ - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Check the publish has been completed with timeout error */ ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_MQTT_TIMEOUT); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3028,7 +2389,7 @@ static int s_test_mqtt_connection_unsub_timeout_fn(struct aws_allocator *allocat .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .protocol_operation_timeout_ms = 3000, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ @@ -3037,7 +2398,7 @@ static int s_test_mqtt_connection_unsub_timeout_fn(struct aws_allocator *allocat struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ mqtt_mock_server_disable_auto_ack(state_test_data->mock_server); @@ -3048,16 +2409,16 @@ static int s_test_mqtt_connection_unsub_timeout_fn(struct aws_allocator *allocat /* unsubscribe to the first topic */ uint16_t unsub_packet_id = aws_mqtt_client_connection_unsubscribe( - state_test_data->mqtt_connection, &pub_topic, s_on_op_complete, state_test_data); + state_test_data->mqtt_connection, &pub_topic, aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(unsub_packet_id > 0); /* publish should complete after the shutdown */ - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Check the publish has been completed with timeout error */ ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_MQTT_TIMEOUT); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3084,7 +2445,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_connection_lost_reset_tim .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .protocol_operation_timeout_ms = 3000, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ @@ -3094,7 +2455,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_connection_lost_reset_tim struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ mqtt_mock_server_disable_auto_ack(state_test_data->mock_server); @@ -3109,7 +2470,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_connection_lost_reset_tim AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); @@ -3117,7 +2478,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_connection_lost_reset_tim aws_thread_current_sleep((uint64_t)ONE_SEC * 2); /* Kill the connection, the requests will be retried and the timeout will be reset. */ aws_channel_shutdown(state_test_data->server_channel, AWS_ERROR_INVALID_STATE); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* sleep for 2 sec again, in total the response has not received for more than 4 sec, timeout should happen if the * lost of connection not reset the timeout */ aws_thread_current_sleep((uint64_t)ONE_SEC * 2); @@ -3125,12 +2486,12 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_connection_lost_reset_tim ASSERT_SUCCESS(mqtt_mock_server_send_puback(state_test_data->mock_server, packet_id_1)); /* publish should complete after the shutdown */ - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Check the publish has been completed successfully since the lost of the connection reset the timeout */ ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_SUCCESS); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3170,21 +2531,21 @@ static int s_test_mqtt_connection_close_callback_simple_fn(struct aws_allocator .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; aws_mqtt_client_connection_set_connection_closed_handler( state_test_data->mqtt_connection, s_on_connection_closed_fn, state_test_data); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* sleep for 2 sec, just to make sure the connection is stable */ aws_thread_current_sleep((uint64_t)ONE_SEC * 2); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Make sure the callback was called and the value is what we expect */ ASSERT_UINT_EQUALS(1, state_test_data->connection_close_calls); @@ -3212,25 +2573,25 @@ static int s_test_mqtt_connection_close_callback_interrupted_fn(struct aws_alloc .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; aws_mqtt_client_connection_set_connection_closed_handler( state_test_data->mqtt_connection, s_on_connection_closed_fn, state_test_data); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Kill the connection */ aws_channel_shutdown(state_test_data->server_channel, AWS_ERROR_INVALID_STATE); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* sleep for 2 sec, just to make sure the connection is stable */ aws_thread_current_sleep((uint64_t)ONE_SEC * 2); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Make sure the callback was called only ONCE and the value is what we expect */ ASSERT_UINT_EQUALS(1, state_test_data->connection_close_calls); @@ -3258,7 +2619,7 @@ static int s_test_mqtt_connection_close_callback_multi_fn(struct aws_allocator * .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; aws_mqtt_client_connection_set_connection_closed_handler( state_test_data->mqtt_connection, s_on_connection_closed_fn, state_test_data); @@ -3266,12 +2627,12 @@ static int s_test_mqtt_connection_close_callback_multi_fn(struct aws_allocator * int disconnect_amount = 10; for (int i = 0; i < disconnect_amount; i++) { ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disconnect */ ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( - state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); } /* Make sure the callback was called disconnect_amount times */ @@ -3299,11 +2660,11 @@ static int s_test_mqtt_connection_reconnection_backoff_stable(struct aws_allocat .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); uint64_t time_before = 0; uint64_t time_after = 0; @@ -3317,7 +2678,7 @@ static int s_test_mqtt_connection_reconnection_backoff_stable(struct aws_allocat /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); @@ -3330,9 +2691,9 @@ static int s_test_mqtt_connection_reconnection_backoff_stable(struct aws_allocat } /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3356,11 +2717,11 @@ static int s_test_mqtt_connection_reconnection_backoff_unstable(struct aws_alloc .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); uint64_t time_before = 0; uint64_t time_after = 0; @@ -3371,7 +2732,7 @@ static int s_test_mqtt_connection_reconnection_backoff_unstable(struct aws_alloc /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); @@ -3387,9 +2748,9 @@ static int s_test_mqtt_connection_reconnection_backoff_unstable(struct aws_alloc } /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3413,11 +2774,11 @@ static int s_test_mqtt_connection_reconnection_backoff_reset(struct aws_allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); uint64_t time_before = 0; uint64_t time_after = 0; @@ -3429,7 +2790,7 @@ static int s_test_mqtt_connection_reconnection_backoff_reset(struct aws_allocato /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); reconnection_backoff = time_after - time_before; @@ -3449,7 +2810,7 @@ static int s_test_mqtt_connection_reconnection_backoff_reset(struct aws_allocato /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); reconnection_backoff = time_after - time_before; @@ -3460,9 +2821,9 @@ static int s_test_mqtt_connection_reconnection_backoff_reset(struct aws_allocato ASSERT_TRUE(remainder <= RECONNECT_BACKOFF_DELAY_ERROR_MARGIN_NANO_SECONDS); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3488,11 +2849,11 @@ static int s_test_mqtt_connection_reconnection_backoff_reset_after_disconnection .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); uint64_t time_before = 0; uint64_t time_after = 0; @@ -3503,7 +2864,7 @@ static int s_test_mqtt_connection_reconnection_backoff_reset_after_disconnection /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); reconnection_backoff = time_after - time_before; @@ -3514,18 +2875,18 @@ static int s_test_mqtt_connection_reconnection_backoff_reset_after_disconnection expected_reconnect_backoff = aws_min_u64(expected_reconnect_backoff * 2, 10); } /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* connect again */ ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_before); aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); reconnection_backoff = time_after - time_before; @@ -3536,9 +2897,9 @@ static int s_test_mqtt_connection_reconnection_backoff_reset_after_disconnection ASSERT_TRUE(remainder <= RECONNECT_BACKOFF_DELAY_ERROR_MARGIN_NANO_SECONDS); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3563,7 +2924,7 @@ static int s_test_mqtt_connection_ping_norm_fn(struct aws_allocator *allocator, .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = 1, .ping_timeout_ms = 100, }; @@ -3576,9 +2937,9 @@ static int s_test_mqtt_connection_ping_norm_fn(struct aws_allocator *allocator, /* Ensure the server got 4 PING packets */ ASSERT_INT_EQUALS(4, mqtt_mock_server_get_ping_count(state_test_data->mock_server)); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3604,13 +2965,13 @@ static int s_test_mqtt_connection_ping_no_fn(struct aws_allocator *allocator, vo .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = 1, .ping_timeout_ms = 100, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); @@ -3630,7 +2991,7 @@ static int s_test_mqtt_connection_ping_no_fn(struct aws_allocator *allocator, vo AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id > 0); @@ -3645,9 +3006,9 @@ static int s_test_mqtt_connection_ping_no_fn(struct aws_allocator *allocator, vo /* Ensure the server got 0 PING packets */ ASSERT_INT_EQUALS(0, mqtt_mock_server_get_ping_count(state_test_data->mock_server)); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3673,13 +3034,13 @@ static int s_test_mqtt_connection_ping_noack_fn(struct aws_allocator *allocator, .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = 1, .ping_timeout_ms = 100, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); @@ -3699,7 +3060,7 @@ static int s_test_mqtt_connection_ping_noack_fn(struct aws_allocator *allocator, AWS_MQTT_QOS_AT_MOST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id > 0); @@ -3713,9 +3074,9 @@ static int s_test_mqtt_connection_ping_noack_fn(struct aws_allocator *allocator, /* Ensure the server got 4 PING packets */ ASSERT_INT_EQUALS(4, mqtt_mock_server_get_ping_count(state_test_data->mock_server)); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3747,13 +3108,13 @@ static int s_test_mqtt_connection_ping_basic_scenario_fn(struct aws_allocator *a .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = 4, .ping_timeout_ms = 100, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* PING will be in 4 seconds */ aws_thread_current_sleep(3000000000); /* Wait 3 seconds */ @@ -3771,10 +3132,10 @@ static int s_test_mqtt_connection_ping_basic_scenario_fn(struct aws_allocator *a AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Publish packet written at 3 seconds */ aws_thread_current_sleep(1250000000); /* Wait 1.25 second (the extra 0.25 is to account for jitter) */ @@ -3797,9 +3158,9 @@ static int s_test_mqtt_connection_ping_basic_scenario_fn(struct aws_allocator *a ASSERT_INT_EQUALS(2, mqtt_mock_server_get_ping_count(state_test_data->mock_server)); /* Disconnect and finish! */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3825,13 +3186,13 @@ static int s_test_mqtt_connection_ping_double_scenario_fn(struct aws_allocator * .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = 4, .ping_timeout_ms = 100, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* PING will be in 4 seconds */ aws_thread_current_sleep(3000000000); /* Wait 3 seconds */ @@ -3849,10 +3210,10 @@ static int s_test_mqtt_connection_ping_double_scenario_fn(struct aws_allocator * AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Publish packet written at 3 seconds */ aws_thread_current_sleep(1250000000); /* Wait 1.25 second (the extra 0.25 is to account for jitter) */ @@ -3874,10 +3235,10 @@ static int s_test_mqtt_connection_ping_double_scenario_fn(struct aws_allocator * AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_2 > 0); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Publish packet written at 2 seconds (relative to PING that was scheduled above) */ aws_thread_current_sleep(4250000000); /* Wait 4.25 (the extra 0.25 is to account for jitter) seconds */ @@ -3895,9 +3256,9 @@ static int s_test_mqtt_connection_ping_double_scenario_fn(struct aws_allocator * */ /* Disconnect and finish! */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3918,7 +3279,7 @@ static int s_test_mqtt_connection_termination_callback_simple_fn(struct aws_allo struct mqtt_connection_state_test *state_test_data = ctx; ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_termination_handler( - state_test_data->mqtt_connection, s_on_connection_termination_fn, state_test_data)); + state_test_data->mqtt_connection, aws_test311_on_connection_termination_fn, state_test_data)); return AWS_OP_SUCCESS; } @@ -3943,11 +3304,11 @@ static int s_test_mqtt_validation_failure_publish_qos_fn(struct aws_allocator *a .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_byte_cursor topic = aws_byte_cursor_from_c_str("a/b"); ASSERT_INT_EQUALS( @@ -3958,14 +3319,14 @@ static int s_test_mqtt_validation_failure_publish_qos_fn(struct aws_allocator *a (enum aws_mqtt_qos)3, true, NULL, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data)); int error_code = aws_last_error(); ASSERT_INT_EQUALS(AWS_ERROR_MQTT_INVALID_QOS, error_code); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3990,11 +3351,11 @@ static int s_test_mqtt_validation_failure_subscribe_empty_fn(struct aws_allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_array_list topic_filters; size_t list_len = 2; @@ -4004,13 +3365,13 @@ static int s_test_mqtt_validation_failure_subscribe_empty_fn(struct aws_allocato ASSERT_INT_EQUALS( 0, aws_mqtt_client_connection_subscribe_multiple( - state_test_data->mqtt_connection, &topic_filters, s_on_multi_suback, state_test_data)); + state_test_data->mqtt_connection, &topic_filters, aws_test311_on_multi_suback, state_test_data)); int error_code = aws_last_error(); ASSERT_INT_EQUALS(AWS_ERROR_INVALID_ARGUMENT, error_code); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -4035,22 +3396,22 @@ static int s_test_mqtt_validation_failure_unsubscribe_null_fn(struct aws_allocat .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); ASSERT_INT_EQUALS( 0, aws_mqtt_client_connection_unsubscribe( - state_test_data->mqtt_connection, NULL, s_on_op_complete, state_test_data)); + state_test_data->mqtt_connection, NULL, aws_test311_on_op_complete, state_test_data)); int error_code = aws_last_error(); ASSERT_INT_EQUALS(AWS_ERROR_MQTT_INVALID_TOPIC, error_code); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -4078,7 +3439,7 @@ static int s_test_mqtt_validation_failure_connect_invalid_client_id_utf8_fn( .client_id = s_bad_client_id_utf8, .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_FAILS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); diff --git a/tests/v3/mqtt311_listener_test.c b/tests/v3/mqtt311_listener_test.c new file mode 100644 index 00000000..758ee89f --- /dev/null +++ b/tests/v3/mqtt311_listener_test.c @@ -0,0 +1,375 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include + +#include "mqtt311_testing_utils.h" +#include "mqtt_mock_server_handler.h" + +#include + +struct mqtt311_listener_connection_success_record { + bool joined_session; +}; + +struct mqtt311_listener_publish_record { + struct aws_byte_buf topic; + struct aws_byte_buf payload; +}; + +struct mqtt311_listener_test_context { + struct aws_allocator *allocator; + + struct aws_mqtt311_listener *listener; + + struct mqtt_connection_state_test *mqtt311_test_context; + int mqtt311_test_context_setup_result; + + struct aws_array_list connection_success_events; + struct aws_array_list publish_events; + bool terminated; + + struct aws_mutex lock; + struct aws_condition_variable signal; +}; + +static void s_311_listener_test_on_publish_received( + struct aws_mqtt_client_connection *connection, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain, + void *userdata) { + (void)connection; + (void)dup; + (void)qos; + (void)retain; + + struct mqtt311_listener_test_context *context = userdata; + + struct mqtt311_listener_publish_record publish_record; + AWS_ZERO_STRUCT(publish_record); + + aws_byte_buf_init_copy_from_cursor(&publish_record.topic, context->allocator, *topic); + aws_byte_buf_init_copy_from_cursor(&publish_record.payload, context->allocator, *payload); + + aws_mutex_lock(&context->lock); + aws_array_list_push_back(&context->publish_events, &publish_record); + aws_mutex_unlock(&context->lock); + aws_condition_variable_notify_all(&context->signal); +} + +static void s_311_listener_test_on_connection_success( + struct aws_mqtt_client_connection *connection, + enum aws_mqtt_connect_return_code return_code, + bool session_present, + void *userdata) { + (void)connection; + (void)return_code; + + struct mqtt311_listener_test_context *context = userdata; + + struct mqtt311_listener_connection_success_record connection_success_record = { + .joined_session = session_present, + }; + + aws_mutex_lock(&context->lock); + aws_array_list_push_back(&context->connection_success_events, &connection_success_record); + aws_mutex_unlock(&context->lock); + aws_condition_variable_notify_all(&context->signal); +} + +static void s_311_listener_test_on_termination(void *complete_ctx) { + struct mqtt311_listener_test_context *context = complete_ctx; + + aws_mutex_lock(&context->lock); + context->terminated = true; + aws_mutex_unlock(&context->lock); + aws_condition_variable_notify_all(&context->signal); +} + +static int mqtt311_listener_test_context_init( + struct mqtt311_listener_test_context *context, + struct aws_allocator *allocator, + struct mqtt_connection_state_test *mqtt311_test_context) { + AWS_ZERO_STRUCT(*context); + + context->allocator = allocator; + context->mqtt311_test_context = mqtt311_test_context; + + aws_array_list_init_dynamic( + &context->connection_success_events, allocator, 10, sizeof(struct mqtt311_listener_connection_success_record)); + aws_array_list_init_dynamic( + &context->publish_events, allocator, 10, sizeof(struct mqtt311_listener_publish_record)); + + aws_mutex_init(&context->lock); + aws_condition_variable_init(&context->signal); + + context->mqtt311_test_context_setup_result = aws_test311_setup_mqtt_server_fn(allocator, mqtt311_test_context); + ASSERT_SUCCESS(context->mqtt311_test_context_setup_result); + + struct aws_mqtt311_listener_config listener_config = { + .connection = mqtt311_test_context->mqtt_connection, + .listener_callbacks = + { + .publish_received_handler = s_311_listener_test_on_publish_received, + .connection_success_handler = s_311_listener_test_on_connection_success, + .user_data = context, + }, + .termination_callback = s_311_listener_test_on_termination, + .termination_callback_user_data = context, + }; + + context->listener = aws_mqtt311_listener_new(allocator, &listener_config); + + return AWS_OP_SUCCESS; +} + +static bool s_is_listener_terminated(void *userdata) { + struct mqtt311_listener_test_context *context = userdata; + + return context->terminated; +} + +static void s_wait_for_listener_termination_callback(struct mqtt311_listener_test_context *context) { + aws_mutex_lock(&context->lock); + aws_condition_variable_wait_pred(&context->signal, &context->lock, s_is_listener_terminated, context); + aws_mutex_unlock(&context->lock); +} + +static void mqtt311_listener_test_context_clean_up(struct mqtt311_listener_test_context *context) { + aws_mqtt311_listener_release(context->listener); + s_wait_for_listener_termination_callback(context); + + aws_test311_clean_up_mqtt_server_fn( + context->allocator, context->mqtt311_test_context_setup_result, context->mqtt311_test_context); + + aws_mutex_clean_up(&context->lock); + aws_condition_variable_clean_up(&context->signal); + + aws_array_list_clean_up(&context->connection_success_events); + + for (size_t i = 0; i < aws_array_list_length(&context->publish_events); ++i) { + struct mqtt311_listener_publish_record publish_record; + AWS_ZERO_STRUCT(publish_record); + + aws_array_list_get_at(&context->publish_events, &publish_record, i); + + aws_byte_buf_clean_up(&publish_record.topic); + aws_byte_buf_clean_up(&publish_record.payload); + } + + aws_array_list_clean_up(&context->publish_events); +} + +struct connection_success_event_test_context { + struct mqtt311_listener_test_context *context; + bool joined_session; + size_t expected_count; +}; + +static bool s_contains_connection_success_events(void *userdata) { + struct connection_success_event_test_context *wait_context = userdata; + struct mqtt311_listener_test_context *context = wait_context->context; + + size_t found = 0; + for (size_t i = 0; i < aws_array_list_length(&context->connection_success_events); ++i) { + struct mqtt311_listener_connection_success_record record; + aws_array_list_get_at(&context->connection_success_events, &record, i); + + if (record.joined_session == wait_context->joined_session) { + ++found; + } + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_connection_success_events( + struct mqtt311_listener_test_context *context, + bool joined_session, + size_t expected_count) { + struct connection_success_event_test_context wait_context = { + .context = context, + .joined_session = joined_session, + .expected_count = expected_count, + }; + + aws_mutex_lock(&context->lock); + aws_condition_variable_wait_pred( + &context->signal, &context->lock, s_contains_connection_success_events, &wait_context); + aws_mutex_unlock(&context->lock); +} + +static int s_do_mqtt311_listener_connection_success_event_test(struct aws_allocator *allocator, bool session_present) { + aws_mqtt_library_init(allocator); + + struct mqtt_connection_state_test mqtt311_context; + AWS_ZERO_STRUCT(mqtt311_context); + + struct mqtt311_listener_test_context test_context; + ASSERT_SUCCESS(mqtt311_listener_test_context_init(&test_context, allocator, &mqtt311_context)); + + mqtt_mock_server_set_session_present(mqtt311_context.mock_server, session_present); + + struct aws_mqtt_connection_options connection_options = { + .user_data = &mqtt311_context, + .clean_session = true, + .client_id = aws_byte_cursor_from_c_str("client1234"), + .host_name = aws_byte_cursor_from_c_str(mqtt311_context.endpoint.address), + .socket_options = &mqtt311_context.socket_options, + .on_connection_complete = aws_test311_on_connection_complete_fn, + }; + + ASSERT_SUCCESS(aws_mqtt_client_connection_connect(mqtt311_context.mqtt_connection, &connection_options)); + aws_test311_wait_for_connection_to_complete(&mqtt311_context); + + s_wait_for_connection_success_events(&test_context, session_present, 1); + + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + mqtt311_context.mqtt_connection, aws_test311_on_disconnect_fn, &mqtt311_context)); + aws_test311_wait_for_disconnect_to_complete(&mqtt311_context); + + ASSERT_SUCCESS(aws_mqtt_client_connection_connect(mqtt311_context.mqtt_connection, &connection_options)); + aws_test311_wait_for_connection_to_complete(&mqtt311_context); + + s_wait_for_connection_success_events(&test_context, session_present, 2); + + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + mqtt311_context.mqtt_connection, aws_test311_on_disconnect_fn, &mqtt311_context)); + aws_test311_wait_for_disconnect_to_complete(&mqtt311_context); + + mqtt311_listener_test_context_clean_up(&test_context); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +static int s_mqtt311_listener_connection_success_event_no_session_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + return s_do_mqtt311_listener_connection_success_event_test(allocator, false); +} + +AWS_TEST_CASE( + mqtt311_listener_connection_success_event_no_session, + s_mqtt311_listener_connection_success_event_no_session_fn) + +static int s_mqtt311_listener_connection_success_event_with_session_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + return s_do_mqtt311_listener_connection_success_event_test(allocator, true); +} + +AWS_TEST_CASE( + mqtt311_listener_connection_success_event_with_session, + s_mqtt311_listener_connection_success_event_with_session_fn) + +struct publish_event_test_context { + struct mqtt311_listener_test_context *context; + struct aws_byte_cursor expected_topic; + struct aws_byte_cursor expected_payload; + size_t expected_count; +}; + +static bool s_contains_publish_events(void *userdata) { + struct publish_event_test_context *wait_context = userdata; + struct mqtt311_listener_test_context *context = wait_context->context; + + size_t found = 0; + for (size_t i = 0; i < aws_array_list_length(&context->publish_events); ++i) { + struct mqtt311_listener_publish_record record; + aws_array_list_get_at(&context->publish_events, &record, i); + + struct aws_byte_cursor actual_topic = aws_byte_cursor_from_buf(&record.topic); + if (!aws_byte_cursor_eq(&wait_context->expected_topic, &actual_topic)) { + continue; + } + + struct aws_byte_cursor actual_payload = aws_byte_cursor_from_buf(&record.payload); + if (!aws_byte_cursor_eq(&wait_context->expected_payload, &actual_payload)) { + continue; + } + + ++found; + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_publish_events( + struct mqtt311_listener_test_context *context, + struct aws_byte_cursor topic, + struct aws_byte_cursor payload, + size_t expected_count) { + struct publish_event_test_context wait_context = { + .context = context, + .expected_topic = topic, + .expected_payload = payload, + .expected_count = expected_count, + }; + + aws_mutex_lock(&context->lock); + aws_condition_variable_wait_pred(&context->signal, &context->lock, s_contains_publish_events, &wait_context); + aws_mutex_unlock(&context->lock); +} + +static int s_mqtt311_listener_publish_event_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_mqtt_library_init(allocator); + + struct mqtt_connection_state_test mqtt311_context; + AWS_ZERO_STRUCT(mqtt311_context); + + struct mqtt311_listener_test_context test_context; + ASSERT_SUCCESS(mqtt311_listener_test_context_init(&test_context, allocator, &mqtt311_context)); + + mqtt_mock_server_set_publish_reflection(mqtt311_context.mock_server, true); + + struct aws_mqtt_connection_options connection_options = { + .user_data = &mqtt311_context, + .clean_session = true, + .client_id = aws_byte_cursor_from_c_str("client1234"), + .host_name = aws_byte_cursor_from_c_str(mqtt311_context.endpoint.address), + .socket_options = &mqtt311_context.socket_options, + .on_connection_complete = aws_test311_on_connection_complete_fn, + }; + + ASSERT_SUCCESS(aws_mqtt_client_connection_connect(mqtt311_context.mqtt_connection, &connection_options)); + aws_test311_wait_for_connection_to_complete(&mqtt311_context); + + struct aws_byte_cursor topic1 = aws_byte_cursor_from_c_str("hello/world/1"); + struct aws_byte_cursor payload1 = aws_byte_cursor_from_c_str("payload1"); + aws_mqtt_client_connection_publish( + mqtt311_context.mqtt_connection, &topic1, AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload1, NULL, NULL); + + s_wait_for_publish_events(&test_context, topic1, payload1, 1); + + struct aws_byte_cursor topic2 = aws_byte_cursor_from_c_str("nothing/important"); + struct aws_byte_cursor payload2 = aws_byte_cursor_from_c_str("somethingneeddoing?"); + aws_mqtt_client_connection_publish( + mqtt311_context.mqtt_connection, &topic2, AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload2, NULL, NULL); + aws_mqtt_client_connection_publish( + mqtt311_context.mqtt_connection, &topic2, AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload2, NULL, NULL); + + s_wait_for_publish_events(&test_context, topic2, payload2, 2); + + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + mqtt311_context.mqtt_connection, aws_test311_on_disconnect_fn, &mqtt311_context)); + aws_test311_wait_for_disconnect_to_complete(&mqtt311_context); + + mqtt311_listener_test_context_clean_up(&test_context); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(mqtt311_listener_publish_event, s_mqtt311_listener_publish_event_fn) \ No newline at end of file diff --git a/tests/v3/mqtt311_testing_utils.c b/tests/v3/mqtt311_testing_utils.c new file mode 100644 index 00000000..52a55e92 --- /dev/null +++ b/tests/v3/mqtt311_testing_utils.c @@ -0,0 +1,580 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include "mqtt311_testing_utils.h" + +#include +#include +#include +#include +#include +#include + +#include "mqtt_mock_server_handler.h" + +static void s_on_incoming_channel_setup_fn( + struct aws_server_bootstrap *bootstrap, + int error_code, + struct aws_channel *channel, + void *user_data) { + (void)bootstrap; + struct mqtt_connection_state_test *state_test_data = user_data; + + state_test_data->error = error_code; + + if (!error_code) { + aws_mutex_lock(&state_test_data->lock); + state_test_data->server_disconnect_completed = false; + aws_mutex_unlock(&state_test_data->lock); + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "server channel setup completed"); + + state_test_data->server_channel = channel; + struct aws_channel_slot *test_handler_slot = aws_channel_slot_new(channel); + aws_channel_slot_insert_end(channel, test_handler_slot); + mqtt_mock_server_handler_update_slot(state_test_data->mock_server, test_handler_slot); + aws_channel_slot_set_handler(test_handler_slot, state_test_data->mock_server); + } +} + +static void s_on_incoming_channel_shutdown_fn( + struct aws_server_bootstrap *bootstrap, + int error_code, + struct aws_channel *channel, + void *user_data) { + (void)bootstrap; + (void)error_code; + (void)channel; + struct mqtt_connection_state_test *state_test_data = user_data; + aws_mutex_lock(&state_test_data->lock); + state_test_data->server_disconnect_completed = true; + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "server channel shutdown completed"); + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static void s_on_listener_destroy(struct aws_server_bootstrap *bootstrap, void *user_data) { + (void)bootstrap; + struct mqtt_connection_state_test *state_test_data = user_data; + aws_mutex_lock(&state_test_data->lock); + state_test_data->listener_destroyed = true; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_listener_destroyed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->listener_destroyed; +} + +static void s_wait_on_listener_cleanup(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_listener_destroyed, state_test_data); + aws_mutex_unlock(&state_test_data->lock); +} + +static void s_on_connection_interrupted(struct aws_mqtt_client_connection *connection, int error_code, void *userdata) { + (void)connection; + (void)error_code; + struct mqtt_connection_state_test *state_test_data = userdata; + + aws_mutex_lock(&state_test_data->lock); + state_test_data->connection_interrupted = true; + state_test_data->interruption_error = error_code; + aws_mutex_unlock(&state_test_data->lock); + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "connection interrupted"); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_connection_interrupted(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_interrupted; +} + +void aws_test311_wait_for_interrupt_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_connection_interrupted, state_test_data); + state_test_data->connection_interrupted = false; + aws_mutex_unlock(&state_test_data->lock); +} + +static void s_on_connection_resumed( + struct aws_mqtt_client_connection *connection, + enum aws_mqtt_connect_return_code return_code, + bool session_present, + void *userdata) { + (void)connection; + (void)return_code; + (void)session_present; + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "reconnect completed"); + + struct mqtt_connection_state_test *state_test_data = userdata; + + aws_mutex_lock(&state_test_data->lock); + state_test_data->connection_resumed = true; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_connection_resumed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_resumed; +} + +void aws_test311_wait_for_reconnect_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_connection_resumed, state_test_data); + state_test_data->connection_resumed = false; + aws_mutex_unlock(&state_test_data->lock); +} + +static void s_on_connection_success( + struct aws_mqtt_client_connection *connection, + enum aws_mqtt_connect_return_code return_code, + bool session_present, + void *userdata) { + (void)connection; + struct mqtt_connection_state_test *state_test_data = userdata; + aws_mutex_lock(&state_test_data->lock); + + state_test_data->session_present = session_present; + state_test_data->mqtt_return_code = return_code; + state_test_data->connection_success = true; + aws_mutex_unlock(&state_test_data->lock); + + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static void s_on_connection_failure(struct aws_mqtt_client_connection *connection, int error_code, void *userdata) { + (void)connection; + struct mqtt_connection_state_test *state_test_data = userdata; + aws_mutex_lock(&state_test_data->lock); + + state_test_data->error = error_code; + state_test_data->connection_failure = true; + aws_mutex_unlock(&state_test_data->lock); + + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_connection_succeed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_success; +} + +static bool s_is_connection_failed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_failure; +} + +void aws_test311_wait_for_connection_to_succeed(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_connection_succeed, state_test_data); + state_test_data->connection_success = false; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_wait_for_connection_to_fail(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_connection_failed, state_test_data); + state_test_data->connection_failure = false; + aws_mutex_unlock(&state_test_data->lock); +} + +static bool s_is_termination_completed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_terminated; +} + +static void s_wait_for_termination_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_termination_completed, state_test_data); + state_test_data->connection_terminated = false; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_on_connection_termination_fn(void *userdata) { + struct mqtt_connection_state_test *state_test_data = (struct mqtt_connection_state_test *)userdata; + + aws_mutex_lock(&state_test_data->lock); + state_test_data->connection_termination_calls += 1; + state_test_data->connection_terminated = true; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static void s_on_any_publish_received( + struct aws_mqtt_client_connection *connection, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain, + void *userdata) { + (void)connection; + struct mqtt_connection_state_test *state_test_data = userdata; + + struct aws_byte_buf payload_cp; + aws_byte_buf_init_copy_from_cursor(&payload_cp, state_test_data->allocator, *payload); + struct aws_byte_buf topic_cp; + aws_byte_buf_init_copy_from_cursor(&topic_cp, state_test_data->allocator, *topic); + struct received_publish_packet received_packet = { + .payload = payload_cp, + .topic = topic_cp, + .dup = dup, + .qos = qos, + .retain = retain, + }; + + aws_mutex_lock(&state_test_data->lock); + aws_array_list_push_back(&state_test_data->any_published_messages, &received_packet); + state_test_data->any_publishes_received++; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +/** + * sets up a unix domain socket server and socket options. Creates an mqtt connection configured to use + * the domain socket. + */ +int aws_test311_setup_mqtt_server_fn(struct aws_allocator *allocator, void *ctx) { + aws_mqtt_library_init(allocator); + + struct mqtt_connection_state_test *state_test_data = ctx; + + AWS_ZERO_STRUCT(*state_test_data); + + state_test_data->allocator = allocator; + state_test_data->el_group = aws_event_loop_group_new_default(allocator, 1, NULL); + + state_test_data->mock_server = new_mqtt_mock_server(allocator); + ASSERT_NOT_NULL(state_test_data->mock_server); + + state_test_data->server_bootstrap = aws_server_bootstrap_new(allocator, state_test_data->el_group); + ASSERT_NOT_NULL(state_test_data->server_bootstrap); + + struct aws_socket_options socket_options = { + .connect_timeout_ms = 100, + .domain = AWS_SOCKET_LOCAL, + }; + + state_test_data->socket_options = socket_options; + ASSERT_SUCCESS(aws_condition_variable_init(&state_test_data->cvar)); + ASSERT_SUCCESS(aws_mutex_init(&state_test_data->lock)); + + aws_socket_endpoint_init_local_address_for_test(&state_test_data->endpoint); + + struct aws_server_socket_channel_bootstrap_options server_bootstrap_options = { + .bootstrap = state_test_data->server_bootstrap, + .host_name = state_test_data->endpoint.address, + .port = state_test_data->endpoint.port, + .socket_options = &state_test_data->socket_options, + .incoming_callback = s_on_incoming_channel_setup_fn, + .shutdown_callback = s_on_incoming_channel_shutdown_fn, + .destroy_callback = s_on_listener_destroy, + .user_data = state_test_data, + }; + state_test_data->listener = aws_server_bootstrap_new_socket_listener(&server_bootstrap_options); + + ASSERT_NOT_NULL(state_test_data->listener); + + struct aws_host_resolver_default_options resolver_options = { + .el_group = state_test_data->el_group, + .max_entries = 1, + }; + state_test_data->host_resolver = aws_host_resolver_new_default(allocator, &resolver_options); + + struct aws_client_bootstrap_options bootstrap_options = { + .event_loop_group = state_test_data->el_group, + .user_data = state_test_data, + .host_resolver = state_test_data->host_resolver, + }; + + state_test_data->client_bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); + + state_test_data->mqtt_client = aws_mqtt_client_new(allocator, state_test_data->client_bootstrap); + state_test_data->mqtt_connection = aws_mqtt_client_connection_new(state_test_data->mqtt_client); + ASSERT_NOT_NULL(state_test_data->mqtt_connection); + + ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_interruption_handlers( + state_test_data->mqtt_connection, + s_on_connection_interrupted, + state_test_data, + s_on_connection_resumed, + state_test_data)); + + ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_result_handlers( + state_test_data->mqtt_connection, + s_on_connection_success, + state_test_data, + s_on_connection_failure, + state_test_data)); + + ASSERT_SUCCESS(aws_mqtt_client_connection_set_on_any_publish_handler( + state_test_data->mqtt_connection, s_on_any_publish_received, state_test_data)); + + ASSERT_SUCCESS(aws_array_list_init_dynamic( + &state_test_data->published_messages, allocator, 4, sizeof(struct received_publish_packet))); + ASSERT_SUCCESS(aws_array_list_init_dynamic( + &state_test_data->any_published_messages, allocator, 4, sizeof(struct received_publish_packet))); + ASSERT_SUCCESS(aws_array_list_init_dynamic(&state_test_data->qos_returned, allocator, 2, sizeof(uint8_t))); + + ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_termination_handler( + state_test_data->mqtt_connection, aws_test311_on_connection_termination_fn, state_test_data)); + + return AWS_OP_SUCCESS; +} + +static void s_received_publish_packet_list_clean_up(struct aws_array_list *list) { + for (size_t i = 0; i < aws_array_list_length(list); ++i) { + struct received_publish_packet *val_ptr = NULL; + aws_array_list_get_at_ptr(list, (void **)&val_ptr, i); + aws_byte_buf_clean_up(&val_ptr->payload); + aws_byte_buf_clean_up(&val_ptr->topic); + } + aws_array_list_clean_up(list); +} + +int aws_test311_clean_up_mqtt_server_fn(struct aws_allocator *allocator, int setup_result, void *ctx) { + (void)allocator; + + if (!setup_result) { + struct mqtt_connection_state_test *state_test_data = ctx; + + s_received_publish_packet_list_clean_up(&state_test_data->published_messages); + s_received_publish_packet_list_clean_up(&state_test_data->any_published_messages); + aws_array_list_clean_up(&state_test_data->qos_returned); + aws_mqtt_client_connection_release(state_test_data->mqtt_connection); + + s_wait_for_termination_to_complete(state_test_data); + ASSERT_UINT_EQUALS(1, state_test_data->connection_termination_calls); + + aws_mqtt_client_release(state_test_data->mqtt_client); + aws_client_bootstrap_release(state_test_data->client_bootstrap); + aws_host_resolver_release(state_test_data->host_resolver); + aws_server_bootstrap_destroy_socket_listener(state_test_data->server_bootstrap, state_test_data->listener); + s_wait_on_listener_cleanup(state_test_data); + aws_server_bootstrap_release(state_test_data->server_bootstrap); + aws_event_loop_group_release(state_test_data->el_group); + destroy_mqtt_mock_server(state_test_data->mock_server); + } + + aws_mqtt_library_clean_up(); + return AWS_OP_SUCCESS; +} + +void aws_test311_on_connection_complete_fn( + struct aws_mqtt_client_connection *connection, + int error_code, + enum aws_mqtt_connect_return_code return_code, + bool session_present, + void *userdata) { + (void)connection; + struct mqtt_connection_state_test *state_test_data = userdata; + aws_mutex_lock(&state_test_data->lock); + + state_test_data->session_present = session_present; + state_test_data->mqtt_return_code = return_code; + state_test_data->error = error_code; + state_test_data->connection_completed = true; + aws_mutex_unlock(&state_test_data->lock); + + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_connection_completed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_completed; +} + +void aws_test311_wait_for_connection_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_connection_completed, state_test_data); + state_test_data->connection_completed = false; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_on_disconnect_fn(struct aws_mqtt_client_connection *connection, void *userdata) { + (void)connection; + struct mqtt_connection_state_test *state_test_data = userdata; + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "disconnect completed"); + aws_mutex_lock(&state_test_data->lock); + state_test_data->client_disconnect_completed = true; + aws_mutex_unlock(&state_test_data->lock); + + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_disconnect_completed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->client_disconnect_completed && state_test_data->server_disconnect_completed; +} + +void aws_test311_wait_for_disconnect_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_disconnect_completed, state_test_data); + state_test_data->client_disconnect_completed = false; + state_test_data->server_disconnect_completed = false; + aws_mutex_unlock(&state_test_data->lock); +} + +static bool s_is_any_publish_received(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->any_publishes_received == state_test_data->expected_any_publishes; +} + +void aws_test311_wait_for_any_publish(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_any_publish_received, state_test_data); + state_test_data->any_publishes_received = 0; + state_test_data->expected_any_publishes = 0; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_on_publish_received( + struct aws_mqtt_client_connection *connection, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain, + void *userdata) { + + (void)connection; + (void)topic; + struct mqtt_connection_state_test *state_test_data = userdata; + + struct aws_byte_buf payload_cp; + aws_byte_buf_init_copy_from_cursor(&payload_cp, state_test_data->allocator, *payload); + struct aws_byte_buf topic_cp; + aws_byte_buf_init_copy_from_cursor(&topic_cp, state_test_data->allocator, *topic); + struct received_publish_packet received_packet = { + .payload = payload_cp, + .topic = topic_cp, + .dup = dup, + .qos = qos, + .retain = retain, + }; + + aws_mutex_lock(&state_test_data->lock); + aws_array_list_push_back(&state_test_data->published_messages, &received_packet); + state_test_data->publishes_received++; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_publish_received(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->publishes_received == state_test_data->expected_publishes; +} + +void aws_test311_wait_for_publish(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_publish_received, state_test_data); + state_test_data->publishes_received = 0; + state_test_data->expected_publishes = 0; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_on_suback( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + const struct aws_byte_cursor *topic, + enum aws_mqtt_qos qos, + int error_code, + void *userdata) { + (void)connection; + (void)packet_id; + (void)topic; + + struct mqtt_connection_state_test *state_test_data = userdata; + + aws_mutex_lock(&state_test_data->lock); + if (!error_code) { + aws_array_list_push_back(&state_test_data->qos_returned, &qos); + } + state_test_data->subscribe_completed = true; + state_test_data->subscribe_complete_error = error_code; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_subscribe_completed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->subscribe_completed; +} + +void aws_test311_wait_for_subscribe_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_subscribe_completed, state_test_data); + state_test_data->subscribe_completed = false; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_on_multi_suback( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + const struct aws_array_list *topic_subacks, /* contains aws_mqtt_topic_subscription pointers */ + int error_code, + void *userdata) { + (void)connection; + (void)packet_id; + (void)topic_subacks; + (void)error_code; + + struct mqtt_connection_state_test *state_test_data = userdata; + + aws_mutex_lock(&state_test_data->lock); + state_test_data->subscribe_completed = true; + if (!error_code) { + size_t length = aws_array_list_length(topic_subacks); + for (size_t i = 0; i < length; ++i) { + struct aws_mqtt_topic_subscription *subscription = NULL; + aws_array_list_get_at(topic_subacks, &subscription, i); + aws_array_list_push_back(&state_test_data->qos_returned, &subscription->qos); + } + } + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +void aws_test311_on_op_complete( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + int error_code, + void *userdata) { + (void)connection; + (void)packet_id; + + struct mqtt_connection_state_test *state_test_data = userdata; + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "pub op completed"); + aws_mutex_lock(&state_test_data->lock); + state_test_data->ops_completed++; + state_test_data->op_complete_error = error_code; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_ops_completed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->ops_completed == state_test_data->expected_ops_completed; +} + +void aws_test311_wait_for_ops_completed(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_for_pred( + &state_test_data->cvar, &state_test_data->lock, 10000000000, s_is_ops_completed, state_test_data); + aws_mutex_unlock(&state_test_data->lock); +} \ No newline at end of file diff --git a/tests/v3/mqtt311_testing_utils.h b/tests/v3/mqtt311_testing_utils.h new file mode 100644 index 00000000..e9162c20 --- /dev/null +++ b/tests/v3/mqtt311_testing_utils.h @@ -0,0 +1,155 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#ifndef AWS_C_MQTT_MQTT311_TESTING_UTILS_H +#define AWS_C_MQTT_MQTT311_TESTING_UTILS_H + +#include + +#include +#include +#include +#include +#include + +#include + +#define TEST_LOG_SUBJECT 60000 +#define ONE_SEC 1000000000 +// The value is extract from aws-c-mqtt/source/client.c +#define AWS_RESET_RECONNECT_BACKOFF_DELAY_SECONDS 10 +#define RECONNECT_BACKOFF_DELAY_ERROR_MARGIN_NANO_SECONDS 500000000 +#define DEFAULT_MIN_RECONNECT_DELAY_SECONDS 1 + +#define DEFAULT_TEST_PING_TIMEOUT_MS 1000 +#define DEFAULT_TEST_KEEP_ALIVE_S 2 + +struct received_publish_packet { + struct aws_byte_buf topic; + struct aws_byte_buf payload; + bool dup; + enum aws_mqtt_qos qos; + bool retain; +}; + +struct mqtt_connection_state_test { + struct aws_allocator *allocator; + struct aws_channel *server_channel; + struct aws_channel_handler *mock_server; + struct aws_client_bootstrap *client_bootstrap; + struct aws_server_bootstrap *server_bootstrap; + struct aws_event_loop_group *el_group; + struct aws_host_resolver *host_resolver; + struct aws_socket_endpoint endpoint; + struct aws_socket *listener; + struct aws_mqtt_client *mqtt_client; + struct aws_mqtt_client_connection *mqtt_connection; + struct aws_socket_options socket_options; + + bool session_present; + bool connection_completed; + bool connection_success; + bool connection_failure; + bool client_disconnect_completed; + bool server_disconnect_completed; + bool connection_interrupted; + bool connection_resumed; + bool subscribe_completed; + bool listener_destroyed; + bool connection_terminated; + int interruption_error; + int subscribe_complete_error; + int op_complete_error; + enum aws_mqtt_connect_return_code mqtt_return_code; + int error; + struct aws_condition_variable cvar; + struct aws_mutex lock; + /* any published messages from mock server, that you may not subscribe to. (Which should not happen in real life) */ + struct aws_array_list any_published_messages; /* list of struct received_publish_packet */ + size_t any_publishes_received; + size_t expected_any_publishes; + /* the published messages from mock server, that you did subscribe to. */ + struct aws_array_list published_messages; /* list of struct received_publish_packet */ + size_t publishes_received; + size_t expected_publishes; + /* The returned QoS from mock server */ + struct aws_array_list qos_returned; /* list of uint_8 */ + size_t ops_completed; + size_t expected_ops_completed; + size_t connection_close_calls; /* All of the times on_connection_closed has been called */ + + size_t connection_termination_calls; /* How many times on_connection_termination has been called, should be 1 */ +}; + +AWS_EXTERN_C_BEGIN + +int aws_test311_setup_mqtt_server_fn(struct aws_allocator *allocator, void *ctx); + +int aws_test311_clean_up_mqtt_server_fn(struct aws_allocator *allocator, int setup_result, void *ctx); + +void aws_test311_wait_for_interrupt_to_complete(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_wait_for_reconnect_to_complete(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_wait_for_connection_to_succeed(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_wait_for_connection_to_fail(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_connection_complete_fn( + struct aws_mqtt_client_connection *connection, + int error_code, + enum aws_mqtt_connect_return_code return_code, + bool session_present, + void *userdata); + +void aws_test311_wait_for_connection_to_complete(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_disconnect_fn(struct aws_mqtt_client_connection *connection, void *userdata); + +void aws_test311_wait_for_disconnect_to_complete(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_wait_for_any_publish(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_publish_received( + struct aws_mqtt_client_connection *connection, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain, + void *userdata); + +void aws_test311_wait_for_publish(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_suback( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + const struct aws_byte_cursor *topic, + enum aws_mqtt_qos qos, + int error_code, + void *userdata); + +void aws_test311_wait_for_subscribe_to_complete(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_multi_suback( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + const struct aws_array_list *topic_subacks, + int error_code, + void *userdata); + +void aws_test311_on_op_complete( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + int error_code, + void *userdata); + +void aws_test311_wait_for_ops_completed(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_connection_termination_fn(void *userdata); + +AWS_EXTERN_C_END + +#endif // AWS_C_MQTT_MQTT311_TESTING_UTILS_H diff --git a/tests/v3/mqtt_mock_server_handler.c b/tests/v3/mqtt_mock_server_handler.c index 73576a7d..0e770690 100644 --- a/tests/v3/mqtt_mock_server_handler.c +++ b/tests/v3/mqtt_mock_server_handler.c @@ -30,6 +30,8 @@ struct mqtt_mock_server_handler { size_t pubacks_received; size_t ping_received; size_t connacks_avail; + bool session_present; + bool reflect_publishes; bool auto_ack; /* last ID used when sending PUBLISH (QoS1+) to client */ @@ -77,12 +79,14 @@ static int s_mqtt_mock_server_handler_process_packet( switch (packet_type) { case AWS_MQTT_PACKET_CONNECT: { size_t connacks_available = 0; + bool session_present = false; aws_mutex_lock(&server->synced.lock); AWS_LOGF_DEBUG( MOCK_LOG_SUBJECT, "server, CONNECT received, %llu available connacks.", (long long unsigned)server->synced.connacks_avail); connacks_available = server->synced.connacks_avail > 0 ? server->synced.connacks_avail-- : 0; + session_present = server->synced.session_present; aws_mutex_unlock(&server->synced.lock); if (connacks_available) { @@ -90,7 +94,7 @@ static int s_mqtt_mock_server_handler_process_packet( aws_channel_acquire_message_from_pool(server->slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, 256); struct aws_mqtt_packet_connack conn_ack; - err |= aws_mqtt_packet_connack_init(&conn_ack, false, AWS_MQTT_CONNECT_ACCEPTED); + err |= aws_mqtt_packet_connack_init(&conn_ack, session_present, AWS_MQTT_CONNECT_ACCEPTED); err |= aws_mqtt_packet_connack_encode(&connack_msg->message_data, &conn_ack); if (aws_channel_slot_send_message(server->slot, connack_msg, AWS_CHANNEL_DIR_WRITE)) { err |= 1; @@ -185,6 +189,7 @@ static int s_mqtt_mock_server_handler_process_packet( aws_mutex_lock(&server->synced.lock); bool auto_ack = server->synced.auto_ack; + bool reflect_publishes = server->synced.reflect_publishes; aws_mutex_unlock(&server->synced.lock); uint8_t qos = (publish_packet.fixed_header.flags >> 1) & 0x3; @@ -197,6 +202,23 @@ static int s_mqtt_mock_server_handler_process_packet( err |= aws_mqtt_packet_ack_encode(&puback_msg->message_data, &puback); err |= aws_channel_slot_send_message(server->slot, puback_msg, AWS_CHANNEL_DIR_WRITE); } + + if (reflect_publishes) { + struct aws_io_message *publish_msg = + aws_channel_acquire_message_from_pool(server->slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, 1024); + struct aws_mqtt_packet_publish publish; + // reusing the packet identifier here is weird but reasonably safe, they're separate id spaces + err |= aws_mqtt_packet_publish_init( + &publish, + false, + qos, + false, + publish_packet.topic_name, + publish_packet.packet_identifier, + publish_packet.payload); + err |= aws_mqtt_packet_publish_encode(&publish_msg->message_data, &publish); + err |= aws_channel_slot_send_message(server->slot, publish_msg, AWS_CHANNEL_DIR_WRITE); + } break; } @@ -466,6 +488,22 @@ void destroy_mqtt_mock_server(struct aws_channel_handler *handler) { aws_mem_release(handler->alloc, server); } +void mqtt_mock_server_set_session_present(struct aws_channel_handler *handler, bool session_present) { + struct mqtt_mock_server_handler *server = handler->impl; + + aws_mutex_lock(&server->synced.lock); + server->synced.session_present = session_present; + aws_mutex_unlock(&server->synced.lock); +} + +void mqtt_mock_server_set_publish_reflection(struct aws_channel_handler *handler, bool reflect_publishes) { + struct mqtt_mock_server_handler *server = handler->impl; + + aws_mutex_lock(&server->synced.lock); + server->synced.reflect_publishes = reflect_publishes; + aws_mutex_unlock(&server->synced.lock); +} + void mqtt_mock_server_set_max_ping_resp(struct aws_channel_handler *handler, size_t max_ping) { struct mqtt_mock_server_handler *server = handler->impl; diff --git a/tests/v3/mqtt_mock_server_handler.h b/tests/v3/mqtt_mock_server_handler.h index 170201e6..8187ae11 100644 --- a/tests/v3/mqtt_mock_server_handler.h +++ b/tests/v3/mqtt_mock_server_handler.h @@ -68,10 +68,21 @@ int mqtt_mock_server_send_publish_by_id( enum aws_mqtt_qos qos, bool retain); +/** + * Sets whether or not connacks return session present + */ +void mqtt_mock_server_set_session_present(struct aws_channel_handler *handler, bool session_present); + +/** + * Sets whether or not connacks return session present + */ +void mqtt_mock_server_set_publish_reflection(struct aws_channel_handler *handler, bool reflect_publishes); + /** * Set max number of PINGRESP that mock server will send back to client */ void mqtt_mock_server_set_max_ping_resp(struct aws_channel_handler *handler, size_t max_ping); + /** * Set max number of CONACK that mock server will send back to client */ diff --git a/tests/v5/mqtt5_client_tests.c b/tests/v5/mqtt5_client_tests.c index 2d436f85..b876adda 100644 --- a/tests/v5/mqtt5_client_tests.c +++ b/tests/v5/mqtt5_client_tests.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -18,40 +17,13 @@ #include -#include #include -#define TEST_IO_MESSAGE_LENGTH 4096 - static bool s_is_within_percentage_of(uint64_t expected_time, uint64_t actual_time, double percentage) { double actual_percent = 1.0 - (double)actual_time / (double)expected_time; return fabs(actual_percent) <= percentage; } -int aws_mqtt5_mock_server_send_packet( - struct aws_mqtt5_server_mock_connection_context *connection, - enum aws_mqtt5_packet_type packet_type, - void *packet) { - aws_mqtt5_encoder_append_packet_encoding(&connection->encoder, packet_type, packet); - - struct aws_io_message *message = aws_channel_acquire_message_from_pool( - connection->slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, TEST_IO_MESSAGE_LENGTH); - if (message == NULL) { - return AWS_OP_ERR; - } - - enum aws_mqtt5_encoding_result result = - aws_mqtt5_encoder_encode_to_buffer(&connection->encoder, &message->message_data); - AWS_FATAL_ASSERT(result == AWS_MQTT5_ER_FINISHED); - - if (aws_channel_slot_send_message(connection->slot, message, AWS_CHANNEL_DIR_WRITE)) { - aws_mem_release(message->allocator, message); - return AWS_OP_ERR; - } - - return AWS_OP_SUCCESS; -} - int aws_mqtt5_mock_server_handle_connect_always_succeed( void *packet, struct aws_mqtt5_server_mock_connection_context *connection, @@ -1892,7 +1864,7 @@ static void s_wait_for_suback_received(struct aws_mqtt5_client_mock_test_fixture aws_mutex_unlock(&test_context->lock); } -static int s_aws_mqtt5_server_send_suback_on_subscribe( +int aws_mqtt5_server_send_suback_on_subscribe( void *packet, struct aws_mqtt5_server_mock_connection_context *connection, void *user_data) { @@ -1920,7 +1892,7 @@ static int s_mqtt5_client_subscribe_success_fn(struct aws_allocator *allocator, aws_mqtt5_client_test_init_default_options(&test_options); test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = - s_aws_mqtt5_server_send_suback_on_subscribe; + aws_mqtt5_server_send_suback_on_subscribe; struct aws_mqtt5_client_mock_test_fixture test_context; @@ -2738,7 +2710,7 @@ static int s_aws_mqtt5_mock_server_handle_connect_honor_session_after_success( return aws_mqtt5_mock_server_send_packet(connection, AWS_MQTT5_PT_CONNACK, &connack_view); } -static int s_aws_mqtt5_mock_server_handle_connect_honor_session_unconditional( +int aws_mqtt5_mock_server_handle_connect_honor_session_unconditional( void *packet, struct aws_mqtt5_server_mock_connection_context *connection, void *user_data) { @@ -2780,7 +2752,7 @@ static bool s_received_n_lifecycle_events(void *arg) { return matching_events >= context->expected_event_count; } -static void s_wait_for_n_lifecycle_events( +void aws_wait_for_n_lifecycle_events( struct aws_mqtt5_client_mock_test_fixture *test_fixture, enum aws_mqtt5_client_lifecycle_event_type event_type, size_t expected_event_count) { @@ -2837,7 +2809,7 @@ static int s_do_mqtt5_client_session_resumption_test( test_options.client_options.session_behavior = session_behavior; test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_CONNECT] = - s_aws_mqtt5_mock_server_handle_connect_honor_session_unconditional; + aws_mqtt5_mock_server_handle_connect_honor_session_unconditional; struct aws_mqtt5_client_mqtt5_mock_test_fixture_options test_fixture_options = { .client_options = &test_options.client_options, @@ -2851,7 +2823,7 @@ static int s_do_mqtt5_client_session_resumption_test( for (size_t i = 0; i < SESSION_RESUMPTION_CONNECT_COUNT; ++i) { ASSERT_SUCCESS(aws_mqtt5_client_start(client)); - s_wait_for_n_lifecycle_events(&test_context, AWS_MQTT5_CLET_CONNECTION_SUCCESS, i + 1); + aws_wait_for_n_lifecycle_events(&test_context, AWS_MQTT5_CLET_CONNECTION_SUCCESS, i + 1); /* not technically truly safe to query depending on memory model. Remove if it becomes a problem. */ bool expected_rejoined_session = s_compute_expected_rejoined_session(session_behavior, i); @@ -2860,7 +2832,7 @@ static int s_do_mqtt5_client_session_resumption_test( /* can't use stop as that wipes session state */ aws_channel_shutdown(test_context.server_channel, AWS_ERROR_UNKNOWN); - s_wait_for_n_lifecycle_events(&test_context, AWS_MQTT5_CLET_DISCONNECTION, i + 1); + aws_wait_for_n_lifecycle_events(&test_context, AWS_MQTT5_CLET_DISCONNECTION, i + 1); } struct aws_mqtt5_packet_connect_storage clean_start_connect_storage; @@ -3131,7 +3103,7 @@ int aws_mqtt5_mock_server_handle_unsubscribe_unsuback_success( struct aws_mqtt5_packet_unsuback_view unsuback_view = { .packet_id = unsubscribe_view->packet_id, - .reason_code_count = AWS_ARRAY_SIZE(mqtt5_unsuback_codes), + .reason_code_count = unsubscribe_view->topic_filter_count, .reason_codes = mqtt5_unsuback_codes, }; @@ -3269,7 +3241,7 @@ static int s_do_sub_pub_unsub_test(struct aws_allocator *allocator, enum aws_mqt test_options.client_options.publish_received_handler_user_data = &full_test_context; test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = - s_aws_mqtt5_server_send_suback_on_subscribe; + aws_mqtt5_server_send_suback_on_subscribe; test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_PUBLISH] = aws_mqtt5_mock_server_handle_publish_puback_and_forward; test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_UNSUBSCRIBE] = @@ -4167,7 +4139,7 @@ static int s_mqtt5_client_statistics_subscribe_fn(struct aws_allocator *allocato aws_mqtt5_client_test_init_default_options(&test_options); test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = - s_aws_mqtt5_server_send_suback_on_subscribe; + aws_mqtt5_server_send_suback_on_subscribe; struct aws_mqtt5_client_mock_test_fixture test_context; diff --git a/tests/v5/mqtt5_testing_utils.c b/tests/v5/mqtt5_testing_utils.c index e8e72f3f..9d1163c9 100644 --- a/tests/v5/mqtt5_testing_utils.c +++ b/tests/v5/mqtt5_testing_utils.c @@ -1740,3 +1740,29 @@ size_t aws_mqtt5_linked_list_length(struct aws_linked_list *list) { return length; } + +#define TEST_IO_MESSAGE_LENGTH 4096 + +int aws_mqtt5_mock_server_send_packet( + struct aws_mqtt5_server_mock_connection_context *connection, + enum aws_mqtt5_packet_type packet_type, + void *packet) { + aws_mqtt5_encoder_append_packet_encoding(&connection->encoder, packet_type, packet); + + struct aws_io_message *message = aws_channel_acquire_message_from_pool( + connection->slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, TEST_IO_MESSAGE_LENGTH); + if (message == NULL) { + return AWS_OP_ERR; + } + + enum aws_mqtt5_encoding_result result = + aws_mqtt5_encoder_encode_to_buffer(&connection->encoder, &message->message_data); + AWS_FATAL_ASSERT(result == AWS_MQTT5_ER_FINISHED); + + if (aws_channel_slot_send_message(connection->slot, message, AWS_CHANNEL_DIR_WRITE)) { + aws_mem_release(message->allocator, message); + return AWS_OP_ERR; + } + + return AWS_OP_SUCCESS; +} diff --git a/tests/v5/mqtt5_testing_utils.h b/tests/v5/mqtt5_testing_utils.h index e4fa6de8..b1f016b8 100644 --- a/tests/v5/mqtt5_testing_utils.h +++ b/tests/v5/mqtt5_testing_utils.h @@ -218,6 +218,26 @@ int aws_mqtt5_mock_server_handle_unsubscribe_unsuback_success( struct aws_mqtt5_server_mock_connection_context *connection, void *user_data); +int aws_mqtt5_mock_server_send_packet( + struct aws_mqtt5_server_mock_connection_context *connection, + enum aws_mqtt5_packet_type packet_type, + void *packet); + +int aws_mqtt5_server_send_suback_on_subscribe( + void *packet, + struct aws_mqtt5_server_mock_connection_context *connection, + void *user_data); + +int aws_mqtt5_mock_server_handle_connect_honor_session_unconditional( + void *packet, + struct aws_mqtt5_server_mock_connection_context *connection, + void *user_data); + +void aws_wait_for_n_lifecycle_events( + struct aws_mqtt5_client_mock_test_fixture *test_fixture, + enum aws_mqtt5_client_lifecycle_event_type event_type, + size_t expected_event_count); + extern const struct aws_string *g_default_client_id; #define RECONNECT_TEST_MIN_BACKOFF 500 diff --git a/tests/v5/mqtt5_to_mqtt3_adapter_tests.c b/tests/v5/mqtt5_to_mqtt3_adapter_tests.c index e3de0802..b067267a 100644 --- a/tests/v5/mqtt5_to_mqtt3_adapter_tests.c +++ b/tests/v5/mqtt5_to_mqtt3_adapter_tests.c @@ -9,18 +9,14 @@ #include #include #include -#include #include #include #include -#include #include -#include #include #include -#include enum aws_mqtt3_lifecycle_event_type { AWS_MQTT3_LET_CONNECTION_COMPLETE,