pebble/tests/fw/services/notifications/test_notification_storage.c
2025-01-27 11:38:16 -08:00

903 lines
24 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 "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);
}