pebble/tests/fw/applib/test_app_message.c
2025-01-27 11:38:16 -08:00

802 lines
26 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "clar.h"
#include "applib/app_message/app_message_internal.h"
#include "kernel/events.h"
#include "system/logging.h"
#include "util/attributes.h"
#include "util/math.h"
#include <stddef.h>
#include <limits.h>
extern AppTimer *app_message_outbox_get_ack_nack_timer(void);
// Stubs
////////////////////////////////////
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_rand_ptr.h"
#include "fake_pbl_malloc.h"
// Fakes
////////////////////////////////////
#include "fake_app_timer.h"
#include "fake_pebble_tasks.h"
// Structures and Externs
////////////////////////////////////
typedef struct PACKED {
AppMessageCmd command:8;
uint8_t transaction_id;
union PACKED {
struct PACKED {
Uuid uuid;
Dictionary dictionary; //!< Variable length!
} push; //!< valid for CMD_PUSH only
struct PACKED {} ack;
} payload[];
} AppMessage;
extern AppTimer *app_message_ack_timer_id(void);
extern bool app_message_is_accepting_inbound(void);
extern bool app_message_is_accepting_outbound(void);
extern bool app_message_is_closed_inbound(void);
extern bool app_message_is_closed_outbound(void);
extern void app_message_monitor_reset(void);
// Globals
////////////////////////////////////
static const uint16_t ENDPOINT_ID = 0x30;
static const uint16_t MAX_SIZE_INBOUND = 32;
static const uint16_t MAX_SIZE_OUTBOUND = 32;
static const char *TEST_DATA = "01234567890123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789";
static const uint32_t TEST_KEY = 0xbeefbabe;
static const uint8_t TEST_TRANSACTION_ID_1 = 0x11; // msgs with this ID are asserted to be ack'd
static const uint8_t TEST_TRANSACTION_ID_2 = 0x22; // msgs with this ID are asserted to be nack'd
static const uint16_t MAX_DATA_SIZE = MAX_SIZE_OUTBOUND - sizeof(Dictionary) - sizeof(Tuple);
static int s_context;
static DictionaryIterator s_expected_iter;
uint8_t s_expected_buffer[MAX_SIZE_OUTBOUND];
static int s_out_sent_call_count = 0;
static int s_out_failed_call_count = 0;
static AppMessageResult s_failure_result = APP_MSG_OK;
static bool s_ack_sent_is_called = false;
static bool s_nack_sent_is_called = false;
static bool s_in_received_is_called = false;
static bool s_in_dropped_is_called = false;
static bool s_ack_received_for_id_1 = false;
static bool s_nack_received_for_id_2 = false;
static AppMessageResult s_dropped_reason = APP_MSG_OK;
static AppMessageCtx s_app_message_ctx;
typedef void (*RemoteReceiveHandler)(uint16_t endpoint_id,
const uint8_t* data, unsigned int length);
static RemoteReceiveHandler s_remote_receive_handler;
// UUID: 6bf6215b-c97f-409e-8c31-4f55657222b4
static Uuid simplicity_uuid = (Uuid){ 0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4 };
static CommSession *s_fake_app_comm_session = (CommSession *) 0xaabbccdd;
static bool s_is_connected;
static bool s_is_app_message_receiver_open;
static Uuid s_app_uuid;
static Uuid s_remote_app_uuid;
static bool s_app_receiver_oom;
// Utils
////////////////////////////////////
static void prv_set_app_uuid(Uuid uuid) {
s_app_uuid = uuid;
}
static void prv_set_remote_app_uuid(Uuid uuid) {
s_remote_app_uuid = uuid;
}
//! @note Assumes same order of tuples in both dictionaries!
static void prv_assert_dict_equal(DictionaryIterator *a, DictionaryIterator *b) {
Tuple *a_tuple = dict_read_first(a);
Tuple *b_tuple = dict_read_first(b);
while (b_tuple && a_tuple) {
cl_assert_equal_i(a_tuple->key, b_tuple->key);
cl_assert_equal_i(a_tuple->length, b_tuple->length);
cl_assert_equal_i(a_tuple->type, b_tuple->type);
cl_assert_equal_m(a_tuple->value, b_tuple->value, a_tuple->length);
a_tuple = dict_read_next(a);
b_tuple = dict_read_next(b);
}
if (b_tuple) {
cl_fail("Dictionary `B` contained more tuples than dictionary `A`.");
} else if (a_tuple) {
cl_fail("Dictionary `A` contained more tuples than dictionary `B`.");
}
}
// Callbacks
////////////////////////////////////
static void prv_out_sent_callback(DictionaryIterator *sent, void *context) {
s_out_sent_call_count++;
cl_assert_equal_p(context, &s_context);
prv_assert_dict_equal(sent, &s_expected_iter);
// When the outbox sent callback is called, the outbox should be in the
// ACCEPTING state again.
cl_assert_equal_b(app_message_is_accepting_outbound(), true);
}
static void prv_out_failed_callback(DictionaryIterator *failed,
AppMessageResult reason, void *context) {
s_out_failed_call_count++;
cl_assert_equal_p(context, &s_context);
prv_assert_dict_equal(failed, &s_expected_iter);
s_failure_result = reason;
// When the outbox failed callback is called, the outbox should be in the
// ACCEPTING state again.
cl_assert_equal_b(app_message_is_accepting_outbound(), true);
}
static void prv_in_received_callback(DictionaryIterator *received, void *context) {
cl_assert_equal_p(context, &s_context);
prv_assert_dict_equal(received, &s_expected_iter);
s_in_received_is_called = true;
}
static void prv_in_dropped_callback(AppMessageResult reason, void *context) {
cl_assert_equal_p(context, &s_context);
cl_assert_equal_b(s_in_dropped_is_called, false);
s_in_dropped_is_called = true;
s_dropped_reason = reason;
}
static void prv_send_ack_nack(uint16_t endpoint_id, const uint8_t* data,
unsigned int length, bool nack) {
const int o = offsetof(AppMessage, payload[0].push.dictionary);
cl_assert_equal_i(length, o + dict_calc_buffer_size(1, MAX_DATA_SIZE));
CommSession *session = s_fake_app_comm_session;
AppMessage *message = (AppMessage*)data;
AppMessage ack = {
.command = nack ? CMD_NACK : CMD_ACK,
.transaction_id = message->transaction_id,
};
if (endpoint_id == ENDPOINT_ID) {
app_message_app_protocol_msg_callback(session, (const uint8_t*)&ack, sizeof(AppMessage), NULL);
} else {
cl_fail("Unhandled endpoint");
}
}
static void prv_nack_sent_callback(uint16_t endpoint_id, const uint8_t* data, unsigned int length) {
s_nack_sent_is_called = true;
prv_send_ack_nack(endpoint_id, data, length, true);
}
static void prv_ack_sent_callback(uint16_t endpoint_id, const uint8_t* data, unsigned int length) {
s_ack_sent_is_called = true;
prv_send_ack_nack(endpoint_id, data, length, false);
}
static void prv_receive_test_data(uint8_t transaction_id, const bool oversized) {
const uint16_t dict_length = dict_calc_buffer_size(1, MAX_DATA_SIZE);
const uint16_t message_length = offsetof(AppMessage, payload[0].push.dictionary) +
+ dict_length + (oversized ? 20 : 0);
uint8_t buffer[message_length];
AppMessage *message = (AppMessage*)buffer;
message->command = CMD_PUSH;
message->transaction_id = transaction_id;
message->payload->push.uuid = s_remote_app_uuid;
memcpy(&message->payload->push.dictionary, s_expected_buffer, dict_length);
PBL_LOG(LOG_LEVEL_DEBUG, "message->transaction_id = %"PRIu32, message->transaction_id);
CommSession *session = s_fake_app_comm_session;
app_message_app_protocol_msg_callback(session, buffer, message_length, NULL);
}
static void prv_receive_ack_nack_callback(uint16_t endpoint_id,
const uint8_t* data, unsigned int length) {
AppMessage *message = (AppMessage*)data;
cl_assert(length == sizeof(AppMessage));
PBL_LOG(LOG_LEVEL_DEBUG, "message %"PRIu32", id1 %"PRIu32", id2 %"PRIu32, message->transaction_id,
TEST_TRANSACTION_ID_1, TEST_TRANSACTION_ID_2);
if (message->transaction_id == TEST_TRANSACTION_ID_1) {
cl_assert_equal_b(s_ack_received_for_id_1, false);
s_ack_received_for_id_1 = true;
cl_assert_equal_i(message->command, CMD_ACK);
} else if (message->transaction_id == TEST_TRANSACTION_ID_2) {
cl_assert_equal_b(s_nack_received_for_id_2, false);
s_nack_received_for_id_2 = true;
cl_assert_equal_i(message->command, CMD_NACK);
} else {
cl_assert(false);
}
}
static void prv_no_reply_callback(uint16_t endpoint_id,
const uint8_t* data, unsigned int length) {
}
// Overrides
///////////////////////////////////
bool sys_app_pp_has_capability(CommSessionCapability capability) {
return true;
}
static int s_sys_psleep_last_millis;
void sys_psleep(int millis) {
s_sys_psleep_last_millis = millis;
}
AppMessageCtx *app_state_get_app_message_ctx(void) {
return &s_app_message_ctx;
}
bool app_message_receiver_open(size_t buffer_size) {
if (s_app_receiver_oom) {
return false;
}
s_is_app_message_receiver_open = true;
return true;
}
void app_message_receiver_close(void) {
s_is_app_message_receiver_open = false;
}
size_t sys_app_pp_app_message_inbox_size_maximum(void) {
return 600;
}
void sys_app_pp_app_message_analytics_count_drop(void) {
}
bool sys_get_current_app_is_js_allowed(void) {
return false;
}
Version sys_get_current_app_sdk_version(void) {
return (Version) {};
}
static uint16_t s_sent_endpoint_id;
static uint8_t *s_sent_data;
static uint16_t s_sent_data_length;
void prv_send_data(uint16_t endpoint_id, const uint8_t* data, uint16_t length) {
const size_t header_size =
(uintptr_t)(((AppMessage *)0)->payload[0].push.dictionary.head[0].value->data);
const uint16_t max_length = (header_size + MAX_DATA_SIZE);
if (length > max_length) {
// Using cl_assert_equal_i for the nicer printing.
// when getting at this point, it will always trip:
cl_assert_equal_i(length, max_length);
}
cl_assert_equal_p(s_sent_data, NULL);
s_sent_data = kernel_malloc(length);
cl_assert(s_sent_data);
memcpy(s_sent_data, data, length);
s_sent_data_length = length;
s_sent_endpoint_id = endpoint_id;
}
bool sys_app_pp_send_data(CommSession *session, uint16_t endpoint_id,
const uint8_t* data, uint16_t length) {
if (!s_is_connected) {
return false;
}
prv_send_data(endpoint_id, data, length);
return true;
}
static AppOutboxSentHandler s_app_outbox_sent_handler;
static void *s_app_outbox_ctx;
static void prv_call_outbox_sent(int status) {
cl_assert(s_app_outbox_sent_handler);
s_app_outbox_sent_handler(status, s_app_outbox_ctx);
}
void app_outbox_send(const uint8_t *data, size_t length,
AppOutboxSentHandler sent_handler, void *cb_ctx) {
if (!s_is_connected) {
sent_handler(AppOutboxStatusConsumerDoesNotExist, cb_ctx);
return;
}
s_app_outbox_sent_handler = sent_handler;
s_app_outbox_ctx = cb_ctx;
AppMessageAppOutboxData *outbox_data = (AppMessageAppOutboxData *)data;
prv_send_data(outbox_data->endpoint_id,
outbox_data->payload, length - sizeof(AppMessageAppOutboxData));
}
static void prv_process_sent_data(void) {
if (!s_sent_data) {
return;
}
if (!s_is_connected) {
return;
}
if (!s_is_app_message_receiver_open) {
return;
}
cl_assert(s_remote_receive_handler);
s_remote_receive_handler(s_sent_endpoint_id, s_sent_data, s_sent_data_length);
kernel_free(s_sent_data);
s_sent_data = NULL;
}
void sys_get_app_uuid(Uuid *uuid) {
cl_assert(uuid);
*uuid = s_app_uuid;
}
static void (*s_process_manager_callback)(void *data);
static void *s_process_manager_callback_data;
void sys_current_process_schedule_callback(CallbackEventCallback async_cb, void *ctx) {
// Expecting the stub to be called only once durning a test:
cl_assert_equal_p(s_process_manager_callback, NULL);
cl_assert_equal_p(s_process_manager_callback_data, NULL);
s_process_manager_callback = async_cb;
s_process_manager_callback_data = ctx;
}
static int s_app_inbox_consume_call_count;
void app_inbox_consume(AppInboxConsumerInfo *consumer_info) {
++s_app_inbox_consume_call_count;
}
// Setup
////////////////////////////////////
void test_app_message__initialize(void) {
prv_set_app_uuid(simplicity_uuid);
prv_set_remote_app_uuid(simplicity_uuid);
fake_app_timer_init();
s_app_receiver_oom = false;
s_sys_psleep_last_millis = 0;
s_app_inbox_consume_call_count = 0;
app_message_init();
app_message_set_context(&s_context);
cl_assert_equal_i(app_message_open(MAX_SIZE_INBOUND, MAX_SIZE_OUTBOUND), APP_MSG_OK);
cl_assert_equal_p(app_message_register_outbox_sent(prv_out_sent_callback), NULL);
cl_assert_equal_p(app_message_register_outbox_failed(prv_out_failed_callback), NULL);
cl_assert_equal_p(app_message_register_inbox_dropped(prv_in_dropped_callback), NULL);
cl_assert_equal_p(app_message_register_inbox_received(prv_in_received_callback), NULL);
s_out_sent_call_count = 0;
s_out_failed_call_count = 0;
s_ack_sent_is_called = false;
s_nack_sent_is_called = false;
s_in_received_is_called = false;
s_in_dropped_is_called = false;
s_ack_received_for_id_1 = false;
s_nack_received_for_id_2 = false;
s_remote_receive_handler = NULL;
s_dropped_reason = APP_MSG_OK;
s_failure_result = APP_MSG_OK;
s_process_manager_callback = NULL;
s_process_manager_callback_data = NULL;
s_is_connected = true;
// Create the dictionary that is used to compare with what has been received:
dict_write_begin(&s_expected_iter, s_expected_buffer, MAX_SIZE_OUTBOUND);
cl_assert_equal_i(DICT_OK, dict_write_data(&s_expected_iter, TEST_KEY,
(const uint8_t*)TEST_DATA, MAX_DATA_SIZE));
dict_write_end(&s_expected_iter);
}
void test_app_message__cleanup(void) {
app_message_close();
cl_assert_equal_b(app_message_is_closed_inbound(), true);
cl_assert_equal_b(app_message_is_closed_outbound(), true);
fake_app_timer_deinit();
kernel_free(s_sent_data);
s_sent_data = NULL;
}
// Test OUTBOUND (watch->phone):
////////////////////////////////////
static void prv_send_test_data_expecting_result(AppMessageResult result) {
DictionaryIterator *iter;
cl_assert_equal_i(app_message_outbox_begin(&iter), APP_MSG_OK);
cl_assert_equal_i(dict_write_data(iter, TEST_KEY, (const uint8_t*)TEST_DATA, MAX_DATA_SIZE),
DICT_OK);
cl_assert_equal_i(app_message_outbox_send(), result);
}
static void prv_send_test_data(void) {
prv_send_test_data_expecting_result(APP_MSG_OK);
}
static void prv_set_remote_receive_handler(RemoteReceiveHandler handler) {
s_remote_receive_handler = handler;
}
void test_app_message__send_happy_case_outbox_sent_then_ack(void) {
prv_set_remote_receive_handler(prv_ack_sent_callback);
prv_send_test_data();
prv_call_outbox_sent(AppOutboxStatusSuccess);
prv_process_sent_data();
// After the ACK has been received, we should have been called
cl_assert_equal_b(s_ack_sent_is_called, true);
// Since that callback schedules another callback, we have to invoke
// system tasks again to get th actual callback to trigger.
cl_assert_equal_i(s_out_sent_call_count, 1);
// Check that the state is reset properly after everything
cl_assert_equal_b(app_message_is_accepting_outbound(), true);
}
void test_app_message__send_happy_case_ack_then_outbox_sent(void) {
prv_set_remote_receive_handler(prv_ack_sent_callback);
prv_send_test_data();
prv_process_sent_data();
// With certain PP transports (i.e. PPoGATT), the 'consuming' of the outbound data / outbox sent
// callback can fire after the AppMessage (N)ACK has been received.
cl_assert_equal_b(app_message_is_accepting_outbound(), false);
prv_call_outbox_sent(AppOutboxStatusSuccess);
// After the ACK has been received, we should have been called
cl_assert_equal_b(s_ack_sent_is_called, true);
// Since that callback schedules another callback, we have to invoke
// system tasks again to get th actual callback to trigger.
cl_assert_equal_i(s_out_sent_call_count, 1);
// Check that the state is reset properly after everything
cl_assert_equal_b(app_message_is_accepting_outbound(), true);
}
void test_app_message__cancel_timer(void) {
prv_set_remote_receive_handler(prv_ack_sent_callback);
prv_send_test_data();
prv_call_outbox_sent(AppOutboxStatusSuccess);
prv_process_sent_data();
// After the ACK has been received, we should have been called
cl_assert_equal_b(s_ack_sent_is_called, true);
// Check that we were called
cl_assert_equal_i(s_out_sent_call_count, 1);
// Timer should be invalid
cl_assert_equal_b(!fake_app_timer_is_scheduled(app_message_ack_timer_id()), true);
// Check the state is reset properly
cl_assert_equal_b(app_message_is_accepting_outbound(), true);
}
void test_app_message__send_ack_timeout(void) {
// We'll send the ack right after the timeout
prv_set_remote_receive_handler(prv_ack_sent_callback);
prv_send_test_data();
prv_call_outbox_sent(AppOutboxStatusSuccess);
// Fire the timeout and send the data
app_timer_trigger(app_message_ack_timer_id());
prv_process_sent_data();
cl_assert_equal_i(s_out_sent_call_count, 0);
cl_assert_equal_i(s_out_failed_call_count, 1);
cl_assert_equal_i(s_failure_result, APP_MSG_SEND_TIMEOUT);
// Check the state is reset properly
cl_assert_equal_b(app_message_is_accepting_outbound(), true);
}
void test_app_message__send_rejected(void) {
// Sending ack on timeout, but reject the send
prv_set_remote_receive_handler(prv_nack_sent_callback);
prv_send_test_data();
prv_call_outbox_sent(AppOutboxStatusSuccess);
prv_process_sent_data();
// Fire the ack timeout after receiving the nack
app_timer_trigger(app_message_ack_timer_id());
cl_assert_equal_b(s_nack_sent_is_called, true);
cl_assert_equal_i(s_out_sent_call_count, 0);
cl_assert_equal_i(s_out_failed_call_count, 1);
cl_assert_equal_i(s_failure_result, APP_MSG_SEND_REJECTED);
// Check the state is reset properly
cl_assert_equal_b(app_message_is_accepting_outbound(), true);
}
void test_app_message__nack_then_outbox_sent(void) {
// Sending ack on timeout, but reject the send
prv_set_remote_receive_handler(prv_nack_sent_callback);
prv_send_test_data();
prv_process_sent_data();
cl_assert_equal_b(app_message_is_accepting_outbound(), false);
prv_call_outbox_sent(AppOutboxStatusSuccess);
cl_assert_equal_b(s_nack_sent_is_called, true);
cl_assert_equal_i(s_out_sent_call_count, 0);
cl_assert_equal_i(s_out_failed_call_count, 1);
cl_assert_equal_i(s_failure_result, APP_MSG_SEND_REJECTED);
// Check the state is reset properly
cl_assert_equal_b(app_message_is_accepting_outbound(), true);
}
void test_app_message__busy(void) {
DictionaryIterator *iter;
prv_set_remote_receive_handler(prv_no_reply_callback);
prv_send_test_data();
prv_process_sent_data();
// Can't get or send again if still sending
cl_assert_equal_i(app_message_outbox_begin(&iter), APP_MSG_BUSY);
cl_assert_equal_i(app_message_outbox_send(), APP_MSG_BUSY);
// Can't get or send again if waiting on the ACK
cl_assert_equal_i(app_message_outbox_begin(&iter), APP_MSG_BUSY);
cl_assert_equal_i(app_message_outbox_send(), APP_MSG_BUSY);
}
void test_app_message__send_disconnected(void) {
prv_set_remote_receive_handler(prv_nack_sent_callback);
// Disconnect the comm session
s_is_connected = false;
// The return value should be APP_MSG_OK, even though we already know it's going to fail.
// The failure should be delivered after returning from app_message_outbox_send(), because
// some apps call .._send() again from the failed_callback.
prv_send_test_data_expecting_result(APP_MSG_OK);
// Make fake remote send any outstanding data (none expected)
prv_process_sent_data();
cl_assert_equal_i(s_out_sent_call_count, 0);
// failed_callback not called yet:
cl_assert_equal_i(s_out_failed_call_count, 0);
// Now process the scheduled callback event:
cl_assert(s_process_manager_callback);
s_process_manager_callback(s_process_manager_callback_data);
// Check that the ack/nack timer is removed:
cl_assert_equal_p(app_message_outbox_get_ack_nack_timer(), NULL);
cl_assert_equal_i(1, s_out_failed_call_count);
cl_assert_equal_i(s_failure_result, APP_MSG_NOT_CONNECTED);
cl_assert_equal_b(s_nack_sent_is_called, false);
// Check the state is reset properly
cl_assert_equal_b(app_message_is_accepting_outbound(), true);
}
void test_app_message__send_while_closing_and_while_being_disconnected(void) {
prv_set_remote_receive_handler(prv_nack_sent_callback);
prv_send_test_data();
// Disconnect the comm session and remove the
// app message context
s_is_connected = false;
app_message_close();
// Make fake remote send any outstanding data (none expected)
prv_process_sent_data();
// No app_message callbacks are expected to be called, as we closed the context
cl_assert_equal_i(s_out_sent_call_count, 0);
cl_assert_equal_b(s_nack_sent_is_called, false);
cl_assert_equal_i(s_out_failed_call_count, 0);
cl_assert_equal_b(app_message_is_closed_outbound(), true);
}
void test_app_message__send_while_closing(void) {
prv_set_remote_receive_handler(prv_ack_sent_callback);
prv_send_test_data();
// Close the AppMessage context
app_message_close();
// Make fake remote send the ack if something has been sent (not expected)
prv_process_sent_data();
// Test that timer has been invalidated
cl_assert_equal_b(!fake_app_timer_is_scheduled(app_message_ack_timer_id()), true);
cl_assert_equal_b(s_ack_sent_is_called, false);
cl_assert_equal_i(s_out_sent_call_count, 0);
cl_assert_equal_i(s_out_failed_call_count, 0);
cl_assert_equal_b(app_message_is_closed_outbound(), true);
cl_assert_equal_b(app_message_is_closed_inbound(), true);
}
void test_app_message__throttle_repeated_outbox_begin_calls(void) {
prv_set_remote_receive_handler(prv_no_reply_callback);
prv_send_test_data();
// Expect exponential back-off:
for (int i = 1; i <= 128; i *= 2) {
DictionaryIterator *iter;
cl_assert_equal_i(app_message_outbox_begin(&iter), APP_MSG_BUSY);
cl_assert_equal_i(MIN(i, 100), s_sys_psleep_last_millis);
}
}
// Test INBOUND (phone->watch):
////////////////////////////////////
static void check_in_accepting_again(void) {
cl_assert(app_message_is_accepting_inbound() == true);
}
void test_app_message__receive_happy_case(void) {
prv_set_remote_receive_handler(prv_receive_ack_nack_callback);
prv_receive_test_data(TEST_TRANSACTION_ID_1, false);
cl_assert_equal_i(s_app_inbox_consume_call_count, 1);
prv_process_sent_data();
// First Message
cl_assert(s_in_received_is_called == true);
// ACK the messages
// Check that state was reset properly
check_in_accepting_again();
}
void test_app_message__receive_dropped_because_buffer_too_small(void) {
// FIXME:
// https://pebbletechnology.atlassian.net/browse/PBL-22925
return;
prv_set_remote_receive_handler(prv_receive_ack_nack_callback);
prv_receive_test_data(TEST_TRANSACTION_ID_2, true);
// Message should be dropped due to buffer overflow
cl_assert_equal_b(s_in_dropped_is_called, true);
cl_assert_equal_b(s_in_received_is_called, false);
cl_assert_equal_i(s_dropped_reason, APP_MSG_BUFFER_OVERFLOW);
cl_assert_equal_b(s_nack_received_for_id_2, true);
// Check that the state was reset
check_in_accepting_again();
}
void test_app_message__receive_app_not_running(void) {
// FIXME:
// https://pebbletechnology.atlassian.net/browse/PBL-22925
return;
prv_set_remote_receive_handler(prv_receive_ack_nack_callback);
prv_receive_test_data(TEST_TRANSACTION_ID_2, false);
cl_assert_equal_b(s_in_received_is_called, false);
cl_assert_equal_b(s_in_dropped_is_called, false);
cl_assert_equal_b(s_nack_received_for_id_2, true);
// Check that the state is reset
check_in_accepting_again();
}
void test_app_message__receive_app_uuid_mismatch(void) {
// Change the current app uuid
prv_set_app_uuid(UuidMake(0xF6, 0x2C, 0xB7, 0xBA, 0x1B, 0x8D, 0x46, 0x10,
0xBE, 0xC5, 0xDE, 0xC6, 0x5A, 0xD3, 0x18, 0x29));
prv_set_remote_receive_handler(prv_receive_ack_nack_callback);
prv_receive_test_data(TEST_TRANSACTION_ID_2, false);
prv_process_sent_data();
cl_assert_equal_b(s_in_received_is_called, false);
cl_assert_equal_b(s_in_dropped_is_called, false);
cl_assert_equal_b(s_nack_received_for_id_2, true);
// Check that the state is reset
check_in_accepting_again();
}
void test_app_message__get_context(void) {
cl_assert_equal_p(app_message_get_context(), &s_context);
}
void test_app_message__open_while_already_open(void) {
cl_assert_equal_i(app_message_open(MAX_SIZE_INBOUND, MAX_SIZE_OUTBOUND), APP_MSG_INVALID_STATE);
}
void test_app_message__begin_while_already_begun(void) {
DictionaryIterator *iterator;
cl_assert_equal_i(app_message_outbox_begin(&iterator), APP_MSG_OK);
cl_assert_equal_i(app_message_outbox_begin(&iterator), APP_MSG_INVALID_STATE);
}
void test_app_message__begin_null_iterator(void) {
cl_assert_equal_i(app_message_outbox_begin(NULL), APP_MSG_INVALID_ARGS);
}
void test_app_message__send_while_not_begun(void) {
cl_assert_equal_i(app_message_outbox_send(), APP_MSG_INVALID_STATE);
}
void test_app_message__zero_inbox(void) {
app_message_close();
cl_assert_equal_i(app_message_open(0, MAX_SIZE_OUTBOUND), APP_MSG_OK);
cl_assert_equal_b(app_message_is_closed_inbound(), true);
cl_assert_equal_b(app_message_is_closed_outbound(), false);
}
void test_app_message__zero_outbox(void) {
app_message_close();
cl_assert_equal_i(app_message_open(MAX_SIZE_INBOUND, 0), APP_MSG_OK);
cl_assert_equal_b(app_message_is_closed_inbound(), false);
cl_assert_equal_b(app_message_is_closed_outbound(), true);
DictionaryIterator *iterator;
cl_assert_equal_i(app_message_outbox_begin(&iterator), APP_MSG_INVALID_STATE);
}
void test_app_message__oom(void) {
s_app_receiver_oom = true;
app_message_close();
cl_assert_equal_i(app_message_open(MAX_SIZE_INBOUND, MAX_SIZE_OUTBOUND), APP_MSG_OUT_OF_MEMORY);
cl_assert_equal_b(app_message_is_closed_inbound(), true);
cl_assert_equal_b(app_message_is_closed_outbound(), true);
}
void test_app_message__kernel_nack_handler(void) {
prv_set_remote_receive_handler(prv_receive_ack_nack_callback);
const AppMessagePush push = {
.header = {
.command = CMD_PUSH,
.transaction_id = TEST_TRANSACTION_ID_2,
},
};
app_message_app_protocol_system_nack_callback(s_fake_app_comm_session,
(const uint8_t *)&push, sizeof(push));
prv_process_sent_data();
cl_assert_equal_b(s_nack_received_for_id_2, true);
}