mirror of
https://github.com/google/pebble.git
synced 2025-03-20 19:01:21 +00:00
802 lines
26 KiB
C
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);
|
|
}
|