Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson
2025-01-27 11:38:16 -08:00
committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions
@@ -0,0 +1,333 @@
/*
* 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 "services/normal/notifications/alerts_preferences.h"
#include "services/normal/notifications/ancs/ancs_filtering.h"
#include "services/normal/blob_db/ios_notif_pref_db.h"
#include "services/normal/timeline/attributes_actions.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
// Fakes
////////////////////////////////////////////////////////////////
static time_t s_now;
time_t rtc_get_time(void) {
return s_now;
}
RtcTicks rtc_get_ticks(void) {
return 0;
}
bool s_performed_store = false;
AttributeList *s_expected_attributes = NULL;
TimelineItemActionGroup *s_expected_actions = NULL;
static uint8_t app_id_data[] = {
0x00, // id
0x04, 0x00, // length
'P', 'h', 'i', 'l', // Value
};
static ANCSAttribute *s_app_id_attr = (ANCSAttribute *)&app_id_data;
static uint8_t display_name_data[] = {
0x00, // id
0x07, 0x00, // length
'A', 'w', 'e', 's', 'o', 'm', 'e', // Value
};
static ANCSAttribute *s_display_name_attr = (ANCSAttribute *)&display_name_data;
static uint8_t title_data[] = {
0x01, // id
0x0E, 0x00, // length
'A', 'p', 'p', 'l', 'e', ' ', 'P', 'a', 'y', ' ', '=', ' ', ':', '(' // Value
};
static ANCSAttribute *s_title_attr = (ANCSAttribute *)&title_data;
status_t ios_notif_pref_db_store_prefs(const uint8_t *app_id, int length, AttributeList *attr_list,
TimelineItemActionGroup *action_group) {
s_performed_store = true;
if (s_expected_attributes) {
const int buf_size = 256;
uint8_t expected_buf[buf_size];
uint8_t actual_buf[buf_size];
attributes_actions_serialize_payload(s_expected_attributes, s_expected_actions,
expected_buf, buf_size);
attributes_actions_serialize_payload(attr_list, action_group,
actual_buf, buf_size);
cl_assert_equal_m(expected_buf, actual_buf,
attributes_actions_get_serialized_payload_size(s_expected_attributes, s_expected_actions));
}
return S_SUCCESS;
}
void ios_notif_pref_db_free_prefs(iOSNotifPrefs *prefs) {
return;
}
static void prv_compare_notif_prefs(iOSNotifPrefs *prefs1, iOSNotifPrefs *prefs2) {
const int buf_size = 256;
uint8_t buf1[buf_size];
uint8_t buf2[buf_size];
attributes_actions_serialize_payload(&prefs1->attr_list, &prefs1->action_group, buf1, buf_size);
attributes_actions_serialize_payload(&prefs2->attr_list, &prefs2->action_group, buf2, buf_size);
cl_assert_equal_m(buf1, buf2, attributes_actions_get_serialized_payload_size(&prefs2->attr_list, &prefs2->action_group));
}
void test_ancs_filtering__initialize(void) {
s_now = 1;
s_performed_store = false;
s_expected_attributes = NULL;
s_expected_actions = NULL;
}
void test_ancs_filtering__cleanup(void) {
}
void test_ancs_filtering__record_app_no_action_needed(void) {
// We have some existing prefs which includes all the defaults
// We don't need to insert anything
iOSNotifPrefs prefs = {
.attr_list = {
.num_attributes = 5,
.attributes = (Attribute[]) {
{ .id = AttributeIdTitle, .cstring = "Title" },
{ .id = AttributeIdBody, .cstring = "Body" },
{ .id = AttributeIdAppName, .cstring = "Awesome" },
{ .id = AttributeIdLastUpdated, .uint32 = s_now },
{ .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_Always },
},
},
};
iOSNotifPrefs *existing_prefs = &prefs;
ancs_filtering_record_app(&existing_prefs, s_app_id_attr, s_display_name_attr, s_title_attr);
cl_assert(!s_performed_store);
}
void test_ancs_filtering__record_app_no_prefs_yet(void) {
// No existing prefs yet, we should instert all the defaults
iOSNotifPrefs *existing_prefs = NULL;
AttributeList attr_list = {
.num_attributes = 3,
.attributes = (Attribute[]) {
{ .id = AttributeIdAppName, .cstring = "Awesome" },
{ .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None },
{ .id = AttributeIdLastUpdated, .uint32 = s_now },
}
};
s_expected_attributes = &attr_list;
ancs_filtering_record_app(&existing_prefs, s_app_id_attr, s_display_name_attr, s_title_attr);
cl_assert(s_performed_store);
// Make sure the our existing prefs got updated
iOSNotifPrefs expected_prefs = {
.attr_list = attr_list,
};
prv_compare_notif_prefs(existing_prefs, &expected_prefs);
}
void test_ancs_filtering__record_app_existing_mute(void) {
// We already have a mute attribute and 2 unrelated attributes, make sure mute doesn't change
// and the remaining default attributes are added
iOSNotifPrefs prefs = {
.attr_list = {
.num_attributes = 3,
.attributes = (Attribute[]) {
{ .id = AttributeIdTitle, .cstring = "Title" },
{ .id = AttributeIdBody, .cstring = "Body" },
{ .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_Always },
},
},
};
iOSNotifPrefs *existing_prefs = &prefs;
AttributeList expected_attributes = {
.num_attributes = 5,
.attributes = (Attribute[]) {
{ .id = AttributeIdTitle, .cstring = "Title" },
{ .id = AttributeIdBody, .cstring = "Body" },
{ .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_Always },
{ .id = AttributeIdAppName, .cstring = "Awesome" },
{ .id = AttributeIdLastUpdated, .uint32 = s_now },
}
};
s_expected_attributes = &expected_attributes;
ancs_filtering_record_app(&existing_prefs, s_app_id_attr, s_display_name_attr, s_title_attr);
cl_assert(s_performed_store);
// Make sure the our existing prefs got updated
iOSNotifPrefs expected_prefs = {
.attr_list = expected_attributes,
};
prv_compare_notif_prefs(existing_prefs, &expected_prefs);
}
void test_ancs_filtering__record_app_existing_display_name(void) {
// We got a new app name, make sure it gets updated
iOSNotifPrefs prefs = {
.attr_list = {
.num_attributes = 1,
.attributes = (Attribute[]) {
{ .id = AttributeIdAppName, .cstring = "Phil was here" },
},
},
};
iOSNotifPrefs *existing_prefs = &prefs;
AttributeList expected_attributes = {
.num_attributes = 3,
.attributes = (Attribute[]) {
{ .id = AttributeIdAppName, .cstring = "Awesome" },
{ .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None },
{ .id = AttributeIdLastUpdated, .uint32 = s_now },
}
};
s_expected_attributes = &expected_attributes;
ancs_filtering_record_app(&existing_prefs, s_app_id_attr, s_display_name_attr, s_title_attr);
cl_assert(s_performed_store);
// Make sure the our existing prefs got updated
iOSNotifPrefs expected_prefs = {
.attr_list = expected_attributes,
};
prv_compare_notif_prefs(existing_prefs, &expected_prefs);
}
void test_ancs_filtering__record_app_update_timestamp(void) {
// We already have all the default attributes, but it has been a while since we got our last
// notification from this source
iOSNotifPrefs prefs = {
.attr_list = {
.num_attributes = 3,
.attributes = (Attribute[]) {
{ .id = AttributeIdAppName, .cstring = "Awesome" },
{ .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None },
{ .id = AttributeIdLastUpdated, .uint32 = s_now },
},
},
};
iOSNotifPrefs *existing_prefs = &prefs;
s_now += SECONDS_PER_DAY - 1;
ancs_filtering_record_app(&existing_prefs, s_app_id_attr, s_display_name_attr, s_title_attr);
cl_assert(!s_performed_store);
s_now += 2;
AttributeList expected_attributes = {
.num_attributes = 3,
.attributes = (Attribute[]) {
{ .id = AttributeIdAppName, .cstring = "Awesome" },
{ .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None },
{ .id = AttributeIdLastUpdated, .uint32 = s_now },
}
};
s_expected_attributes = &expected_attributes;
ancs_filtering_record_app(&existing_prefs, s_app_id_attr, s_display_name_attr, s_title_attr);
cl_assert(s_performed_store);
// Make sure the our existing prefs got updated
iOSNotifPrefs expected_prefs = {
.attr_list = expected_attributes,
};
prv_compare_notif_prefs(existing_prefs, &expected_prefs);
}
void test_ancs_filtering__should_ignore_because_muted(void) {
iOSNotifPrefs mute_always = {
.attr_list = {
.num_attributes = 1,
.attributes = (Attribute[]) {
{ .id = AttributeIdMuteDayOfWeek, .uint8 = 0x7F },
}
}
};
iOSNotifPrefs mute_weekends = {
.attr_list = {
.num_attributes = 1,
.attributes = (Attribute[]) {
{ .id = AttributeIdMuteDayOfWeek, .uint8 = 0x41 },
}
}
};
iOSNotifPrefs mute_weekdays = {
.attr_list = {
.num_attributes = 1,
.attributes = (Attribute[]) {
{ .id = AttributeIdMuteDayOfWeek, .uint8 = 0x3E },
}
}
};
s_now = 1451606400; // Friday Jan 1, 2016
cl_assert(ancs_filtering_is_muted(&mute_always));
cl_assert(!ancs_filtering_is_muted(&mute_weekends));
cl_assert(ancs_filtering_is_muted(&mute_weekdays));
s_now += SECONDS_PER_DAY; // Saturday Jan 2, 2016
cl_assert(ancs_filtering_is_muted(&mute_always));
cl_assert(ancs_filtering_is_muted(&mute_weekends));
cl_assert(!ancs_filtering_is_muted(&mute_weekdays));
s_now += SECONDS_PER_DAY; // Sunday Jan 3, 2016
cl_assert(ancs_filtering_is_muted(&mute_always));
cl_assert(ancs_filtering_is_muted(&mute_weekends));
cl_assert(!ancs_filtering_is_muted(&mute_weekdays));
s_now += SECONDS_PER_DAY; // Monday Jan 4, 2016
cl_assert(ancs_filtering_is_muted(&mute_always));
cl_assert(!ancs_filtering_is_muted(&mute_weekends));
cl_assert(ancs_filtering_is_muted(&mute_weekdays));
}
void test_ancs_filtering__record_app_no_display_name(void) {
iOSNotifPrefs *existing_prefs = NULL;
// No display name so we expect the app name to be the title
AttributeList expected_attributes = {
.num_attributes = 3,
.attributes = (Attribute[]) {
{ .id = AttributeIdAppName, .cstring = "Apple Pay = :(" },
{ .id = AttributeIdMuteDayOfWeek, .uint8 = MuteBitfield_None },
{ .id = AttributeIdLastUpdated, .uint32 = s_now },
}
};
s_expected_attributes = &expected_attributes;
ancs_filtering_record_app(&existing_prefs, s_app_id_attr, NULL, s_title_attr);
cl_assert(s_performed_store);
// Make sure the our existing prefs got updated
iOSNotifPrefs expected_prefs = {
.attr_list = expected_attributes,
};
prv_compare_notif_prefs(existing_prefs, &expected_prefs);
}
@@ -0,0 +1,180 @@
/*
* 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 "services/normal/notifications/alerts_preferences.h"
#include "services/normal/notifications/ancs/ancs_notifications.h"
#include "services/normal/blob_db/ios_notif_pref_db.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_analytics.h"
#include "stubs_i18n.h"
#include "stubs_layout_layer.h"
#include "stubs_logging.h"
#include "stubs_notifications.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_pin_db.h"
#include "stubs_regular_timer.h"
#include "stubs_reminder_db.h"
#include "stubs_timeline.h"
#include "stubs_uuid.h"
#include "stubs_nexmo.h"
iOSNotifPrefs* ios_notif_pref_db_get_prefs(const uint8_t *app_id, int length) {
return NULL;
}
void ios_notif_pref_db_free_prefs(iOSNotifPrefs *prefs) {
return;
}
void ancs_filtering_record_app(iOSNotifPrefs *app_notif_prefs,
const ANCSAttribute *app_id,
const ANCSAttribute *display_name,
const ANCSAttribute *title) {
}
bool ancs_filtering_is_muted(const iOSNotifPrefs *app_notif_prefs) {
return false;
}
// Fakes
////////////////////////////////////////////////////////////////
#include "fake_events.h"
#include "fake_notification_storage.h"
static time_t s_now;
time_t rtc_get_time(void) {
return s_now;
}
RtcTicks rtc_get_ticks(void) {
return 0;
}
void test_ancs_notifications__initialize(void) {
s_now = 1;
fake_notification_storage_reset();
fake_event_init();
}
void test_ancs_notifications__cleanup(void) {
}
void test_ancs_notifications__handle_phone_call_message(void) {
const uint8_t app_id[] = {
0x00,
21, 0x00,
'c', 'o', 'm', '.', 'a', 'p', 'p', 'l', 'e', '.',
'm', 'o', 'b', 'i', 'l', 'e', 'p', 'h', 'o', 'n', 'e',
};
const uint8_t title[] = {
0x01,
23, 0x00,
// Add some formatting modifiers before and after. iOS 10 seems to be sending phone numbers with them now.
0xe2, 0x80, 0xad, '+', '1', ' ', '(', '5', '1', '0', ')', ' ', '4', '4', '4', '-', '3', '3', '3', '3', 0xe2, 0x80, 0xac,
};
const uint8_t subtitle[] = {
0x02,
8, 0x00,
'P', 'e', 'b', 'b', 'l', 'e', 'H', 'Q',
};
const uint8_t message[] = {
0x03,
13, 0x00,
'I', 'n', 'c', 'o', 'm', 'i', 'n', 'g', ' ', 'C', 'a', 'l', 'l',
};
const uint8_t date[] = {
0x05,
0x00, 0x00,
};
const uint8_t positive_action[] = {
0x06,
6, 0x00,
'A', 'n', 's', 'w', 'e', 'r',
};
const uint8_t negative_action[] = {
0x07,
7, 0x00,
'D', 'e', 'c', 'l', 'i', 'n', 'e',
};
ANCSAttribute *notif_attributes[] = {
[FetchedNotifAttributeIndexAppID] = (ANCSAttribute *)&app_id,
[FetchedNotifAttributeIndexTitle] = (ANCSAttribute *)&title,
[FetchedNotifAttributeIndexSubtitle] = (ANCSAttribute *)&subtitle,
[FetchedNotifAttributeIndexMessage] = (ANCSAttribute *)&message,
[FetchedNotifAttributeIndexDate]= (ANCSAttribute *)&date,
[FetchedNotifAttributeIndexPositiveActionLabel] = (ANCSAttribute *)&positive_action,
[FetchedNotifAttributeIndexNegativeActionLabel] = (ANCSAttribute *)&negative_action,
};
const uint8_t app_display_name[] = {
0x00,
5, 0x0,
'P', 'h', 'o', 'n', 'e',
};
ANCSAttribute *app_attributes[] = {
[FetchedAppAttributeIndexDisplayName] = (ANCSAttribute *)&app_display_name,
};
ANCSProperty properties = ANCSProperty_IncomingCall;
ancs_notifications_handle_message(37, properties, notif_attributes, app_attributes);
// We just processed an incomming phone call event, there better be a phone event scheduled!
PebbleEvent event = fake_event_get_last();
cl_assert_equal_i(event.type, PEBBLE_PHONE_EVENT);
cl_assert_equal_i(event.phone.type, PhoneEventType_Incoming);
cl_assert_equal_i(event.phone.source, PhoneCallSource_ANCS_Legacy);
}
void test_ancs_notifications__handle_phone_call_removed(void) {
const uint32_t uid = 5;
// Make sure we don't send any events when this isn't iOS9
ancs_notifications_handle_notification_removed(uid, ANCSProperty_IncomingCall);
cl_assert_equal_i(fake_event_get_count(), 0);
// Make sure that we trigger an event when this is iOS9
ancs_notifications_handle_notification_removed(uid,
ANCSProperty_IncomingCall | ANCSProperty_iOS9);
PebbleEvent event = fake_event_get_last();
cl_assert_equal_i(fake_event_get_count(), 1);
cl_assert_equal_i(event.type, PEBBLE_PHONE_EVENT);
cl_assert_equal_i(event.phone.type, PhoneEventType_Hide);
cl_assert_equal_i(event.phone.source, PhoneCallSource_ANCS);
cl_assert_equal_i(event.phone.call_identifier, uid);
// Make sure that having a matching notification in notif_db still results in a call hide event
fake_notification_storage_set_existing_ancs_notification(&(Uuid)UUID_SYSTEM, uid);
ancs_notifications_handle_notification_removed(uid,
ANCSProperty_IncomingCall | ANCSProperty_iOS9);
event = fake_event_get_last();
cl_assert_equal_i(fake_event_get_count(), 2);
cl_assert_equal_i(event.type, PEBBLE_PHONE_EVENT);
cl_assert_equal_i(event.phone.type, PhoneEventType_Hide);
cl_assert_equal_i(event.phone.source, PhoneCallSource_ANCS);
cl_assert_equal_i(event.phone.call_identifier, uid);
}
@@ -0,0 +1,91 @@
/*
* 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 "services/normal/notifications/ancs/ancs_notifications_util.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
// Fakes
////////////////////////////////////////////////////////////////
#include "fakes/fake_rtc.h"
// Tests
////////////////////////////////////////////////////////////////
void test_ancs_notifications_util__initialize(void) {
}
void test_ancs_notifications_util__cleanup(void) {
}
static ANCSAttribute *prv_create_ancs_attr(const char *str) {
const size_t len = strlen(str);
ANCSAttribute *attr = malloc(sizeof(ANCSAttribute) + len);
attr->length = len;
memcpy(&attr->value, str, len);
return attr;
}
static void prv_destroy_ancs_attr(ANCSAttribute *attr) {
free(attr);
}
void test_ancs_notifications_util__parse_timestamp(void) {
struct tm apr_3_13_00 = {
.tm_sec = 0,
.tm_min = 0,
.tm_hour = 13,
.tm_mday = 3,
.tm_mon = 3, // Apr
.tm_year = 2015 - 1900,
.tm_isdst = 1,
};
// DST info for US/Canada 2015
TimezoneInfo tz_info = {
.dst_start = 1425780000, // Sun, 08 Mar 2015 02:00
.dst_end = 1446343200 // Sun, 01 Nov 2015 02:00
};
time_util_update_timezone(&tz_info);
time_t actual = mktime(&apr_3_13_00);
rtc_set_time(actual);
ANCSAttribute *valid_date = prv_create_ancs_attr("20150403T130000");
time_t calculated = ancs_notifications_util_parse_timestamp(valid_date);
prv_destroy_ancs_attr(valid_date);
cl_assert_equal_i(calculated, actual);
ANCSAttribute *invalid_date = prv_create_ancs_attr("b4150403T123456");
calculated = ancs_notifications_util_parse_timestamp(invalid_date);
prv_destroy_ancs_attr(invalid_date);
cl_assert_equal_i(calculated, 0);
ANCSAttribute *crazy_bad_date = prv_create_ancs_attr("F");
calculated = ancs_notifications_util_parse_timestamp(crazy_bad_date);
prv_destroy_ancs_attr(crazy_bad_date);
cl_assert_equal_i(calculated, 0);
ANCSAttribute *zero_length_date = prv_create_ancs_attr("");
calculated = ancs_notifications_util_parse_timestamp(zero_length_date);
prv_destroy_ancs_attr(zero_length_date);
cl_assert_equal_i(calculated, 0);
}
@@ -0,0 +1,155 @@
/*
* 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 "services/normal/notifications/ancs/nexmo.h"
#include "services/normal/notifications/ancs/ancs_notifications_util.h"
#include "services/normal/timeline/attributes_actions.h"
// Stubs
////////////////////////////////////////////////////////////////
#include "stubs_logging.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
// Fakes
////////////////////////////////////////////////////////////////
extern const char *NEXMO_REAUTH_STRING;
static AttributeList *s_expected_attributes = NULL;
static TimelineItemActionGroup *s_expected_actions = NULL;
static bool s_performed_store = false;
status_t ios_notif_pref_db_store_prefs(const uint8_t *app_id, int length, AttributeList *attr_list,
TimelineItemActionGroup *action_group) {
const int buf_size = 256;
uint8_t buf1[buf_size];
uint8_t buf2[buf_size];
attributes_actions_serialize_payload(attr_list, action_group, buf1, buf_size);
attributes_actions_serialize_payload(s_expected_attributes, s_expected_actions, buf2, buf_size);
cl_assert_equal_m(buf1, buf2, attributes_actions_get_serialized_payload_size(s_expected_attributes, s_expected_actions));
s_performed_store = true;
return S_SUCCESS;
}
static uint32_t s_expected_uid = 0;
static bool s_performed_dismiss = false;
void ancs_perform_action(uint32_t notification_uid, uint8_t action_id) {
cl_assert_equal_i(notification_uid, s_expected_uid);
cl_assert_equal_i(action_id, ActionIDNegative);
s_performed_dismiss = true;
}
void test_nexmo__initialize(void) {
s_expected_attributes = NULL;
s_expected_actions = NULL;
s_performed_store = false;
s_expected_uid = INVALID_UID;
s_performed_dismiss = false;
}
void test_nexmo__cleanup(void) {
}
void test_nexmo__is_reuath_sms(void) {
uint8_t expected_app_id_buf[128];
ANCSAttribute *expected_app_id = (ANCSAttribute *)&expected_app_id_buf;
expected_app_id->length = strlen(IOS_SMS_APP_ID);
memcpy(expected_app_id->value, IOS_SMS_APP_ID, strlen(IOS_SMS_APP_ID));
char valid_message[128];
strcpy(valid_message, "possible preamble ");
strcat(valid_message, NEXMO_REAUTH_STRING);
strcat(valid_message, " possible postamble");
uint8_t expected_message_buf[128];
ANCSAttribute *expected_message = (ANCSAttribute *)&expected_message_buf;
expected_message->length = strlen(valid_message);
memcpy(expected_message->value, valid_message, strlen(valid_message));
uint8_t bad_app_id_buf[128];
ANCSAttribute *bad_app_id = (ANCSAttribute *)&bad_app_id_buf;
bad_app_id->length = strlen(IOS_MAIL_APP_ID);
memcpy(bad_app_id->value, IOS_MAIL_APP_ID, strlen(IOS_MAIL_APP_ID));
const char *bad_string = "Phil was here";
uint8_t bad_message_buf[128];
ANCSAttribute *bad_message = (ANCSAttribute *)&bad_message_buf;
bad_message->length = strlen(bad_string);
memcpy(bad_message->value, bad_string, strlen(bad_string));
cl_assert(nexmo_is_reauth_sms(expected_app_id, expected_message));
cl_assert(!nexmo_is_reauth_sms(bad_app_id, expected_message));
cl_assert(!nexmo_is_reauth_sms(expected_app_id, bad_message));
cl_assert(!nexmo_is_reauth_sms(bad_app_id, bad_message));
}
void test_nexmo__handle_reuath_sms(void) {
// UID
const uint32_t uid = 42;
s_expected_uid = uid;
// App ID
uint8_t app_id_buf[128];
ANCSAttribute *app_id = (ANCSAttribute *)&app_id_buf;
app_id->length = strlen(IOS_SMS_APP_ID);
memcpy(app_id->value, IOS_SMS_APP_ID, strlen(IOS_SMS_APP_ID));
// Message
char valid_message[128];
strcpy(valid_message, "possible preamble ");
strcat(valid_message, NEXMO_REAUTH_STRING);
strcat(valid_message, " possible postamble");
uint8_t message_buf[128];
ANCSAttribute *message = (ANCSAttribute *)&message_buf;
message->length = strlen(valid_message);
memcpy(message->value, valid_message, strlen(valid_message));
// Existing prefs
iOSNotifPrefs existing_prefs = {
.attr_list = {
.num_attributes = 3,
.attributes = (Attribute[]) {
{ .id = AttributeIdTitle, .cstring = "Title" },
{ .id = AttributeIdBody, .cstring = "Body" },
{ .id = AttributeIdAppName, .cstring = "Awesome" },
},
},
};
// Make sure that the prefs we store are the existing ones + the reauth msg
AttributeList expected_attr_list = {
.num_attributes = 4,
.attributes = (Attribute[]) {
{ .id = AttributeIdTitle, .cstring = "Title" },
{ .id = AttributeIdBody, .cstring = "Body" },
{ .id = AttributeIdAppName, .cstring = "Awesome" },
{ .id = AttributeIdAuthCode, .cstring = valid_message },
},
};
s_expected_attributes = &expected_attr_list;
nexmo_handle_reauth_sms(uid, app_id, message, &existing_prefs);
cl_assert(s_performed_store);
cl_assert(s_performed_dismiss);
}
@@ -0,0 +1,903 @@
/*
* 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 "services/normal/notifications/notification_storage.h"
#include "services/normal/notifications/notification_storage_private.h"
#include "flash_region/flash_region.h"
#include "services/normal/filesystem/pfs.h"
#include "util/size.h"
#include "clar.h"
#include "stdbool.h"
// Stubs
////////////////////////////////////
#include "fake_spi_flash.h"
#include "fake_rtc.h"
#include "stubs_analytics.h"
#include "stubs_hexdump.h"
#include "stubs_layout_layer.h"
#include "stubs_logging.h"
#include "stubs_mutex.h"
#include "stubs_passert.h"
#include "stubs_pbl_malloc.h"
#include "stubs_pebble_tasks.h"
#include "stubs_prompt.h"
#include "stubs_rand_ptr.h"
#include "stubs_serial.h"
#include "stubs_sleep.h"
#include "stubs_task_watchdog.h"
#define TEST_START FLASH_REGION_FILE_TEST_SPACE_BEGIN
#define TEST_SIZE (FLASH_REGION_FILE_TEST_SPACE_END - \
FLASH_REGION_FILE_TEST_SPACE_BEGIN)
extern void notification_storage_reset(void);
typedef void (*SystemTaskEventCallback)(void *data);
static Attribute action1_attributes[] = {
{.id = AttributeIdTitle, .cstring = "Dismiss"},
};
static Attribute action2_attributes[] = {
{.id = AttributeIdTitle, .cstring = "Archive"},
};
static StringList string_list = {
.serialized_byte_length = 3,
.data = "A\0B",
};
static Attribute action3_attributes[] = {
{.id = AttributeIdAncsAction, .int8 = 1},
{.id = AttributeIdCannedResponses, .string_list = &string_list,},
};
static TimelineItemAction actions[] = {
{
.id = 0,
.type = TimelineItemActionTypeResponse,
.attr_list = {
.num_attributes = ARRAY_LENGTH(action1_attributes),
.attributes = action1_attributes
}
},
{
.id = 1, .type = TimelineItemActionTypeResponse,
.attr_list = {
.num_attributes = ARRAY_LENGTH(action2_attributes),
.attributes = action2_attributes
}
},
{
.id = 2,
.type = TimelineItemActionTypeResponse,
.attr_list = {
.num_attributes = ARRAY_LENGTH(action3_attributes),
.attributes = action3_attributes
}
},
};
static Attribute attributes[] = {
{.id = AttributeIdTitle, .cstring = "Sender"},
{.id = AttributeIdBody, .cstring = "Message"},
{.id = AttributeIdSubtitle, .cstring = "Subject"},
};
bool system_task_add_callback(SystemTaskEventCallback cb, void *data) {
return true;
}
PebblePhoneCaller* phone_call_util_create_caller(const char *number, const char *name) {
return NULL;
}
// Setup
////////////////////////////////////
void test_notification_storage__initialize(void) {
fake_spi_flash_init(0, 0x1000000);
pfs_init(false);
pfs_format(false /* write erase headers */);
notification_storage_reset();
}
void test_notification_storage__cleanup(void) {
}
static void compare_attr_list(AttributeList a, AttributeList b) {
cl_assert_equal_i(a.num_attributes, b.num_attributes);
for (int i = 0; i < a.num_attributes; i++) {
cl_assert_equal_i(a.attributes[i].id, b.attributes[i].id);
switch (a.attributes[i].id) {
case AttributeIdTitle:
case AttributeIdSubtitle:
case AttributeIdBody:
cl_assert_equal_s(a.attributes[i].cstring, b.attributes[i].cstring);
break;
case AttributeIdAncsAction:
cl_assert_equal_i(a.attributes[i].int8, b.attributes[i].int8);
break;
case AttributeIdCannedResponses: {
StringList *list_a = a.attributes[i].string_list;
StringList *list_b = b.attributes[i].string_list;
cl_assert_equal_i(list_a->serialized_byte_length, list_b->serialized_byte_length);
cl_assert_equal_i(
string_list_count(list_a),
string_list_count(list_b)
);
uint32_t count = string_list_count(list_a);
for (uint32_t idx = 0; i<count; i++) {
cl_assert_equal_s(
string_list_get_at(list_a, idx),
string_list_get_at(list_b, idx)
);
}
break;
}
default:
cl_assert(false);
break;
}
}
}
static void compare_notifications(TimelineItem *a, TimelineItem *b) {
cl_assert(uuid_equal(&a->header.id, &b->header.id));
cl_assert_equal_i(a->header.ancs_uid, b->header.ancs_uid);
cl_assert_equal_i(a->header.status, b->header.status);
cl_assert_equal_i(a->header.timestamp, b->header.timestamp);
cl_assert_equal_i(a->header.layout, b->header.layout);
compare_attr_list(a->attr_list, b->attr_list);
cl_assert_equal_i(a->action_group.num_actions, b->action_group.num_actions);
for (int i = 0; i < a->action_group.num_actions; i++) {
cl_assert_equal_i(a->action_group.actions[i].id, b->action_group.actions[i].id);
cl_assert_equal_i(a->action_group.actions[i].type, b->action_group.actions[i].type);
compare_attr_list(a->action_group.actions[i].attr_list, b->action_group.actions[i].attr_list);
}
}
// Tests
////////////////////////////////////
void test_notification_storage__basic(void) {
Uuid id = {0x6b, 0xf6, 0x21, 0x5b, 0xc9, 0x7f, 0x40, 0x9e,
0x8c, 0x31, 0x4f, 0x55, 0x65, 0x72, 0x22, 0xb4};
TimelineItem e = {
.header = {
.id = id,
.status = 0,
.layout = LayoutIdGeneric,
.type = TimelineItemTypeNotification,
},
.attr_list = {
.num_attributes = ARRAY_LENGTH(attributes),
.attributes = attributes,
},
.action_group = {
.num_actions = ARRAY_LENGTH(actions),
.actions = actions,
}
};
notification_storage_store(&e);
TimelineItem r;
cl_assert(notification_storage_get(&id, &r));
compare_notifications(&e, &r);
free(r.allocated_buffer);
cl_assert(notification_storage_get(&id, &r));
compare_notifications(&e, &r);
free(r.allocated_buffer);
Uuid invalid_uuid;
uuid_generate(&invalid_uuid);
cl_assert_equal_b(notification_storage_get(&invalid_uuid, &r), false);
}
void test_notification_storage__multiple(void) {
Uuid i1 ;
uuid_generate(&i1);
TimelineItem e1 = {
.header = {
.id = i1,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda5,
},
.attr_list = {
.num_attributes = ARRAY_LENGTH(attributes),
.attributes = attributes,
},
.action_group = {
.num_actions = ARRAY_LENGTH(actions),
.actions = actions,
}
};
Uuid i2;
uuid_generate(&i2);
TimelineItem e2 = {
.header = {
.id = i2,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda6,
},
.attr_list = {
.num_attributes = 2,
.attributes = attributes,
},
.action_group = {
.num_actions = 1,
.actions = &actions[2],
}
};
Uuid i3;
uuid_generate(&i3);
TimelineItem e3 = {
.header = {
.id = i3,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda7,
},
.attr_list = {
.num_attributes = 1,
.attributes = &attributes[2],
},
.action_group = {
.num_actions = 2,
.actions = actions,
}
};
notification_storage_store(&e1);
TimelineItem r;
cl_assert(notification_storage_get(&i1, &r));
compare_notifications(&e1, &r);
free(r.allocated_buffer);
notification_storage_store(&e2);
notification_storage_store(&e3);
cl_assert(notification_storage_get(&i1, &r));
compare_notifications(&e1, &r);
free(r.allocated_buffer);
cl_assert(notification_storage_get(&i2, &r));
compare_notifications(&e2, &r);
free(r.allocated_buffer);
cl_assert(notification_storage_get(&i3, &r));
compare_notifications(&e3, &r);
free(r.allocated_buffer);
cl_assert(notification_storage_get(&i1, &r));
compare_notifications(&e1, &r);
free(r.allocated_buffer);
}
void test_notification_storage__remove_single(void) {
Uuid i;
uuid_generate(&i);
TimelineItem e = {
.header = {
.id = i,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda5,
},
.attr_list = {
.num_attributes = ARRAY_LENGTH(attributes),
.attributes = attributes,
},
.action_group = {
.num_actions = ARRAY_LENGTH(actions),
.actions = actions,
}
};
notification_storage_store(&e);
TimelineItem r;
cl_assert(notification_storage_get(&i, &r));
compare_notifications(&e, &r);
free(r.allocated_buffer);
notification_storage_remove(&i);
cl_assert_equal_b(notification_storage_get(&i, &r), false);
}
void test_notification_storage__set_actioned_flag(void) {
Uuid i;
uuid_generate(&i);
TimelineItem e = {
.header = {
.id = i,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda5,
},
.attr_list = {
.num_attributes = ARRAY_LENGTH(attributes),
.attributes = attributes,
},
.action_group = {
.num_actions = ARRAY_LENGTH(actions),
.actions = actions,
}
};
notification_storage_store(&e);
TimelineItem r;
cl_assert(notification_storage_get(&i, &r));
compare_notifications(&e, &r);
free(r.allocated_buffer);
notification_storage_set_status(&i, TimelineItemStatusActioned);
cl_assert(notification_storage_get(&i, &r));
cl_assert(uuid_equal(&e.header.id, &r.header.id));
cl_assert_equal_i(e.header.ancs_uid, r.header.ancs_uid);
cl_assert_equal_i(TimelineItemStatusActioned, r.header.status);
cl_assert_equal_i(e.header.timestamp, r.header.timestamp);
cl_assert_equal_i(e.header.layout, r.header.layout);
compare_attr_list(e.attr_list, r.attr_list);
}
void test_notification_storage__remove_multiple_first(void) {
Uuid i1;
uuid_generate(&i1);
TimelineItem e1 = {
.header = {
.id = i1,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda5,
},
.attr_list = {
.num_attributes = ARRAY_LENGTH(attributes),
.attributes = attributes,
},
.action_group = {
.num_actions = ARRAY_LENGTH(actions),
.actions = actions,
}
};
Uuid i2;
uuid_generate(&i2);
TimelineItem e2 = {
.header = {
.id = i2,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda6,
},
.attr_list = {
.num_attributes = 2,
.attributes = attributes,
},
.action_group = {
.num_actions = 1,
.actions = &actions[2],
}
};
notification_storage_store(&e1);
notification_storage_store(&e2);
notification_storage_remove(&i1);
TimelineItem r;
cl_assert_equal_b(notification_storage_get(&i1, &r), false);
cl_assert(notification_storage_get(&i2, &r));
compare_notifications(&e2, &r);
free(r.allocated_buffer);
}
void test_notification_storage__remove_add(void) {
Uuid i1;
uuid_generate(&i1);
TimelineItem e1 = {
.header = {
.id = i1,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda5,
},
.attr_list = {
.num_attributes = ARRAY_LENGTH(attributes),
.attributes = attributes,
},
.action_group = {
.num_actions = ARRAY_LENGTH(actions),
.actions = actions,
}
};
Uuid i2;
uuid_generate(&i2);
TimelineItem e2 = {
.header = {
.id = i2,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda6,
},
.attr_list = {
.num_attributes = 2,
.attributes = attributes,
},
.action_group = {
.num_actions = 1,
.actions = &actions[2],
}
};
Uuid i3;
uuid_generate(&i3);
TimelineItem e3 = {
.header = {
.id = i3,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda7,
},
.attr_list = {
.num_attributes = 1,
.attributes = &attributes[2],
},
.action_group = {
.num_actions = 2,
.actions = actions,
}
};
notification_storage_store(&e1);
notification_storage_store(&e2);
notification_storage_remove(&i2);
TimelineItem r;
cl_assert(notification_storage_get(&i1, &r));
compare_notifications(&e1, &r);
free(r.allocated_buffer);
cl_assert_equal_b(notification_storage_get(&i2, &r), false);
notification_storage_store(&e3);
cl_assert(notification_storage_get(&i1, &r));
compare_notifications(&e1, &r);
free(r.allocated_buffer);
cl_assert_equal_b(notification_storage_get(&i2, &r), false);
cl_assert(notification_storage_get(&i3, &r));
compare_notifications(&e3, &r);
free(r.allocated_buffer);
e2.header.timestamp = e3.header.timestamp + 1;
notification_storage_store(&e2);
cl_assert(notification_storage_get(&i1, &r));
compare_notifications(&e1, &r);
free(r.allocated_buffer);
cl_assert(notification_storage_get(&i2, &r));
compare_notifications(&e2, &r);
free(r.allocated_buffer);
cl_assert(notification_storage_get(&i3, &r));
compare_notifications(&e3, &r);
free(r.allocated_buffer);
}
void test_notification_storage__remove_add_compress(void) {
time_t timestamp = 0x10000000;
TimelineItem e = {
.header = {
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda5,
},
.attr_list = {
.num_attributes = ARRAY_LENGTH(attributes),
.attributes = attributes,
},
.action_group = {
.num_actions = ARRAY_LENGTH(actions),
.actions = actions,
}
};
const size_t notif_size = sizeof(SerializedTimelineItemHeader) +
timeline_item_get_serialized_payload_size(&e);
const size_t file_size = NOTIFICATION_STORAGE_FILE_SIZE;
const size_t count = file_size / notif_size;
Uuid uuids[count];
int i;
for (i = 0; i < count; i++) {
uuid_generate(&uuids[i]);
e.header.id = uuids[i];
e.header.timestamp = timestamp + i;
notification_storage_store(&e);
}
TimelineItem r;
cl_assert(notification_storage_get(&uuids[0], &r));
e.header.id = uuids[0];
e.header.timestamp = timestamp;
compare_notifications(&e, &r);
free(r.allocated_buffer);
cl_assert(notification_storage_get(&uuids[i - 1], &r));
e.header.id = uuids[i - 1];
e.header.timestamp = timestamp + i - 1;
compare_notifications(&e, &r);
free(r.allocated_buffer);
// Storage is now full and none have been removed.
// As more space is needed, blocks of 4k are freed up
Uuid uuid;
uuid_generate(&uuid);
e.header.id = uuid;
e.header.timestamp = timestamp + i;
notification_storage_store(&e);
cl_assert(notification_storage_get(&uuid, &r));
compare_notifications(&e, &r);
free(r.allocated_buffer);
const size_t block_size = file_size / 4;
int erase_count = block_size / notif_size + (((block_size % notif_size) > 0) ? 1 : 0);
int j;
for (j = 0; j < erase_count; j++) {
e.header.id = uuids[j];
e.header.timestamp = timestamp + j;
cl_assert_equal_b(notification_storage_get(&uuids[j], &r), false);
}
e.header.id = uuids[j];
e.header.timestamp = timestamp + j;
cl_assert(notification_storage_get(&uuids[j], &r));
compare_notifications(&e, &r);
free(r.allocated_buffer);
// Fill up storage again
for (++i; i < count + erase_count; i++) {
e.header.id = uuids[i % count];
e.header.timestamp = timestamp + i;
notification_storage_store(&e);
cl_assert(notification_storage_get(&uuids[i % count], &r));
compare_notifications(&e, &r);
free(r.allocated_buffer);
}
//Check that no notifications have been deleted
e.header.id = uuids[j];
e.header.timestamp = timestamp + j;
cl_assert(notification_storage_get(&uuids[j], &r));
compare_notifications(&e, &r);
free(r.allocated_buffer);
//Free up enough space for one notification by removing one
notification_storage_remove(&uuids[i/2]);
e.header.id = uuids[i/2];
e.header.timestamp = timestamp + i/2;
cl_assert_equal_b(notification_storage_get(&uuids[i/2], &r), false);
//Add another notification. Compression should take place without freeing up a 4k block
e.header.id = uuids[i];
e.header.timestamp = timestamp + i;
notification_storage_store(&e);
cl_assert(notification_storage_get(&uuids[i], &r));
compare_notifications(&e, &r);
free(r.allocated_buffer);
//Ensure that compression does not remove old notifications
e.header.id = uuids[j];
e.header.timestamp = timestamp + j;
cl_assert(notification_storage_get(&uuids[j], &r));
compare_notifications(&e, &r);
free(r.allocated_buffer);
//Add another notification. Compression should free up another 4k block
e.header.id = uuids[i];
e.header.timestamp = timestamp + i;
notification_storage_store(&e);
cl_assert(notification_storage_get(&uuids[i], &r));
compare_notifications(&e, &r);
free(r.allocated_buffer);
// Check that an expected number of notifications were removed
erase_count += j;
for (; j < erase_count; j++) {
e.header.id = uuids[j];
e.header.timestamp = timestamp + j;
cl_assert_equal_b(notification_storage_get(&uuids[j], &r), false);
}
}
void test_notification_storage__find_ancs_id(void) {
Uuid i1;
uuid_generate(&i1);
TimelineItem e1 = {
.header = {
.id = i1,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda5,
},
.attr_list = {
.num_attributes = ARRAY_LENGTH(attributes),
.attributes = attributes,
},
.action_group = {
.num_actions = ARRAY_LENGTH(actions),
.actions = actions,
}
};
Uuid i2;
uuid_generate(&i2);
TimelineItem e2 = {
.header = {
.id = i2,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 1,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda6,
},
.attr_list = {
.num_attributes = 2,
.attributes = attributes,
},
.action_group = {
.num_actions = 1,
.actions = &actions[2],
}
};
Uuid i3;
uuid_generate(&i3);
TimelineItem e3 = {
.header = {
.id = i3,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 84,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda7,
},
.attr_list = {
.num_attributes = 1,
.attributes = &attributes[2],
},
.action_group = {
.num_actions = 2,
.actions = actions,
}
};
notification_storage_store(&e1);
notification_storage_store(&e2);
notification_storage_store(&e3);
Uuid u;
cl_assert(notification_storage_find_ancs_notification_id(0, &u));
cl_assert(uuid_equal(&u, &e1.header.id));
cl_assert(notification_storage_find_ancs_notification_id(1, &u));
cl_assert(uuid_equal(&u, &e2.header.id));
cl_assert(notification_storage_find_ancs_notification_id(84, &u));
cl_assert(uuid_equal(&u, &e3.header.id));
// Add a new notification with the same ANCS UID as an existing notification to make sure we
// return the new notification instead of the old one
TimelineItem e4 = e3;
uuid_generate(&e4.header.id);
notification_storage_store(&e4);
cl_assert(notification_storage_find_ancs_notification_id(84, &u));
cl_assert(uuid_equal(&u, &e4.header.id));
}
void test_notification_storage__find_by_timestamp(void) {
Uuid i1;
uuid_generate(&i1);
TimelineItem e1 = {
.header = {
.id = i1,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda5,
},
.attr_list = {
.num_attributes = ARRAY_LENGTH(attributes),
.attributes = attributes,
},
.action_group = {
.num_actions = ARRAY_LENGTH(actions),
.actions = actions,
}
};
Uuid i2;
uuid_generate(&i2);
TimelineItem e2 = {
.header = {
.id = i2,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 1,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda6,
},
.attr_list = {
.num_attributes = 2,
.attributes = attributes,
},
.action_group = {
.num_actions = 1,
.actions = &actions[2],
}
};
Uuid i3;
uuid_generate(&i3);
TimelineItem e3 = {
.header = {
.id = i3,
.type = TimelineItemTypeNotification,
.status = 0,
.ancs_uid = 84,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda7,
},
.attr_list = {
.num_attributes = 1,
.attributes = &attributes[2],
},
.action_group = {
.num_actions = 2,
.actions = actions,
}
};
notification_storage_store(&e1);
notification_storage_store(&e2);
notification_storage_store(&e3);
TimelineItem test = e1;
test.header.id = UUID_INVALID;
test.header.ancs_uid = 51;
CommonTimelineItemHeader h;
cl_assert(notification_storage_find_ancs_notification_by_timestamp(&test, &h));
cl_assert_equal_m(&e1.header, &h, sizeof(CommonTimelineItemHeader));
test.action_group.num_actions = 2;
cl_assert_equal_b(notification_storage_find_ancs_notification_by_timestamp(&test, &h),
false);
test = e2;
test.header.id = UUID_INVALID;
cl_assert(notification_storage_find_ancs_notification_by_timestamp(&test, &h));
cl_assert_equal_m(&e2.header, &h, sizeof(CommonTimelineItemHeader));
test = e3;
test.header.id = UUID_INVALID;
cl_assert(notification_storage_find_ancs_notification_by_timestamp(&test, &h));
cl_assert_equal_m(&e3.header, &h, sizeof(CommonTimelineItemHeader));
notification_storage_remove(&i2);
test = e2;
test.header.id = UUID_INVALID;
cl_assert_equal_b(notification_storage_find_ancs_notification_by_timestamp(&test, &h),
false);
test = e3;
test.header.id = UUID_INVALID;
cl_assert(notification_storage_find_ancs_notification_by_timestamp(&test, &h));
cl_assert_equal_m(&e3.header, &h, sizeof(CommonTimelineItemHeader));
}
void test_notification_storage__should_detect_corruption(void) {
Uuid i1;
uuid_generate(&i1);
TimelineItem e1 = {
.header = {
.id = i1,
.type = TimelineItemTypeNotification,
.status = TimelineItemStatusRead,
.ancs_uid = 0,
.layout = LayoutIdGeneric,
.timestamp = 0x53f0dda5,
},
.attr_list = {
.num_attributes = ARRAY_LENGTH(attributes),
.attributes = attributes,
},
.action_group = {
.num_actions = ARRAY_LENGTH(actions),
.actions = actions,
}
};
notification_storage_store(&e1);
TimelineItem r;
cl_assert(notification_storage_get(&i1, &r));
compare_notifications(&r, &e1);
TimelineItem e2 = e1;
Uuid i2;
uuid_generate(&i2);
e2.header.id = i2;
e2.header.status = 0xC0;
notification_storage_store(&e2);
cl_assert_equal_b(notification_storage_get(&i2, &r), false);
TimelineItem e3 = e1;
Uuid i3;
uuid_generate(&i3);
e3.header.id = i3;
e3.header.type = TimelineItemTypeOutOfRange;
notification_storage_store(&e3);
cl_assert_equal_b(notification_storage_get(&i3, &r), false);
Uuid i4;
uuid_generate(&i4);
TimelineItem e4 = e1;
e4.header.id = i4;
e4.header.layout = NumLayoutIds;
notification_storage_store(&e4);
cl_assert_equal_b(notification_storage_get(&i4, &r), false);
}
+73
View File
@@ -0,0 +1,73 @@
from waftools.pebble_test import clar
def build(ctx):
clar(ctx,
sources_ant_glob = "src/fw/services/normal/notifications/notification_storage.c" \
" src/fw/util/rand/rand.c" \
" src/fw/vendor/tinymt32/tinymt32.c" \
" src/fw/util/stringlist.c" \
" src/fw/util/time/time.c" \
" src/fw/services/normal/filesystem/flash_translation.c" \
" src/fw/services/normal/filesystem/pfs.c" \
" src/fw/services/normal/timeline/item.c" \
" src/fw/services/normal/timeline/attributes_actions.c" \
" src/fw/services/normal/timeline/attribute_group.c" \
" src/fw/services/normal/timeline/attribute.c" \
" src/fw/flash_region/flash_region.c" \
" src/fw/flash_region/filesystem_regions.c" \
" src/fw/util/crc8.c" \
" src/fw/util/legacy_checksum.c" \
" tests/fakes/fake_rtc.c" \
" tests/fakes/fake_spi_flash.c",
test_sources_ant_glob = "test_notification_storage.c",
override_includes=['dummy_board'])
clar(ctx,
sources_ant_glob = \
" src/fw/services/normal/notifications/ancs/ancs_notifications.c " \
" src/fw/services/normal/notifications/ancs/ancs_notifications_util.c " \
" src/fw/services/normal/notifications/ancs/ancs_item.c " \
" src/fw/services/normal/notifications/ancs/ancs_phone_call.c " \
" src/fw/services/normal/timeline/attribute.c" \
" src/fw/services/normal/timeline/attributes_actions.c" \
" src/fw/services/normal/timeline/attribute_group.c" \
" src/fw/services/normal/timeline/item.c" \
" src/fw/services/normal/phone_call_util.c" \
" src/fw/applib/fonts/codepoint.c" \
" src/fw/applib/graphics/utf8.c" \
" src/fw/util/pstring.c" \
" src/fw/util/time/time.c" \
" tests/fakes/fake_events.c",
test_sources_ant_glob = "test_ancs_notifications.c")
clar(ctx,
sources_ant_glob = \
" src/fw/services/normal/notifications/ancs/ancs_notifications_util.c " \
" src/fw/util/time/mktime.c " \
" src/fw/util/pstring.c" \
" src/fw/util/time/time.c" \
" tests/fakes/fake_rtc.c",
test_sources_ant_glob = "test_ancs_notifications_util.c")
clar(ctx,
sources_ant_glob = \
" src/fw/services/normal/notifications/ancs/ancs_filtering.c " \
" src/fw/services/normal/timeline/attribute.c" \
" src/fw/services/normal/timeline/attributes_actions.c" \
" src/fw/services/normal/timeline/attribute_group.c" \
" src/fw/util/pstring.c" \
" src/fw/util/time/time.c",
test_sources_ant_glob = "test_ancs_filtering.c")
clar(ctx,
sources_ant_glob = \
" src/fw/services/normal/notifications/ancs/nexmo.c " \
" src/fw/services/normal/notifications/ancs/ancs_notifications_util.c " \
" src/fw/services/normal/timeline/attribute.c" \
" src/fw/services/normal/timeline/attributes_actions.c" \
" src/fw/services/normal/timeline/attribute_group.c" \
" src/fw/util/pstring.c" \
" src/fw/util/time/time.c " \
" tests/fakes/fake_rtc.c ",
test_sources_ant_glob = "test_nexmo.c")
# vim:filetype=python