mirror of
https://github.com/google/pebble.git
synced 2025-03-19 18:41:21 +00:00
1439 lines
47 KiB
C
1439 lines
47 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 "applib/ui/app_window_stack.h"
|
|
#include "applib/ui/window.h"
|
|
#include "applib/ui/window_manager.h"
|
|
#include "applib/ui/window_stack.h"
|
|
#include "applib/ui/window_stack_private.h"
|
|
#include "kernel/ui/modals/modal_manager.h"
|
|
|
|
#include "applib/connection_service_private.h"
|
|
#include "applib/battery_state_service_private.h"
|
|
#include "applib/tick_timer_service_private.h"
|
|
|
|
#include "clar.h"
|
|
|
|
// Stubs
|
|
////////////////////////////////////
|
|
|
|
#include "stubs_accel_service.h"
|
|
#include "stubs_app_state.h"
|
|
#include "stubs_app_timer.h"
|
|
#include "stubs_ble_app_support.h"
|
|
#include "stubs_event_service_client.h"
|
|
#include "stubs_fonts.h"
|
|
#include "stubs_freertos.h"
|
|
#include "stubs_gbitmap.h"
|
|
#include "stubs_graphics.h"
|
|
#include "stubs_graphics_context.h"
|
|
#include "stubs_heap.h"
|
|
#include "stubs_logging.h"
|
|
#include "stubs_passert.h"
|
|
#include "stubs_persist.h"
|
|
#include "stubs_plugin_service.h"
|
|
#include "stubs_print.h"
|
|
#include "stubs_process_manager.h"
|
|
#include "stubs_prompt.h"
|
|
#include "stubs_queue.h"
|
|
#include "stubs_resources.h"
|
|
#include "stubs_syscalls.h"
|
|
#include "stubs_unobstructed_area.h"
|
|
|
|
// Fakes
|
|
////////////////////////////////////
|
|
|
|
#include "fake_events.h"
|
|
#include "fake_pbl_malloc.h"
|
|
#include "fake_pebble_tasks.h"
|
|
#include "fake_animation.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
// Static Variables
|
|
////////////////////////////////////
|
|
|
|
static int16_t s_load_count = 0;
|
|
static int16_t s_unload_count = 0;
|
|
static int16_t s_appear_count = 0;
|
|
static int16_t s_disappear_count = 0;
|
|
|
|
static Window *s_last_click_configured_window;
|
|
|
|
static bool s_app_idle = false;
|
|
|
|
// Overrides
|
|
////////////////////////////////////
|
|
|
|
void battery_state_service_state_init(BatteryStateServiceState *state) {
|
|
return;
|
|
}
|
|
|
|
void connection_service_state_init(ConnectionServiceState *state) {
|
|
}
|
|
|
|
void tick_timer_service_state_init(TickTimerServiceState *state) {
|
|
return;
|
|
}
|
|
|
|
void framebuffer_clear(FrameBuffer* f) {
|
|
return;
|
|
}
|
|
|
|
void launcher_task_add_callback(void (*callback)(void *data), void *data) {
|
|
callback(data);
|
|
}
|
|
|
|
void app_idle_timeout_pause(void) {
|
|
s_app_idle = true;
|
|
}
|
|
|
|
void app_idle_timeout_resume(void) {
|
|
s_app_idle = false;
|
|
}
|
|
|
|
bool app_install_id_from_app_db(AppInstallId id) {
|
|
return false;
|
|
}
|
|
|
|
void framebuffer_dirty_all(FrameBuffer *f) {
|
|
return;
|
|
}
|
|
|
|
void framebuffer_mark_dirty_rect(FrameBuffer *f, GRect rect) {
|
|
return;
|
|
}
|
|
|
|
bool layer_is_status_bar_layer(Layer *layer) {
|
|
return false;
|
|
}
|
|
|
|
void status_bar_layer_render(GContext *ctx, const GRect *bounds, void *config) {
|
|
return;
|
|
}
|
|
|
|
GDrawState graphics_context_get_drawing_state(GContext* ctx) {
|
|
GDrawState state;
|
|
memset(&state, 0, sizeof(GDrawState));
|
|
return state;
|
|
}
|
|
|
|
void graphics_context_set_drawing_state(GContext* ctx, GDrawState draw_state) {
|
|
return;
|
|
}
|
|
|
|
bool compositor_is_animating(void) {
|
|
return false;
|
|
}
|
|
|
|
void *compositor_modal_transition_to_modal_get(bool dest) {
|
|
return NULL;
|
|
}
|
|
|
|
void compositor_modal_render_ready(void) {
|
|
}
|
|
|
|
void compositor_transition_cancel(void) {
|
|
}
|
|
|
|
bool sys_app_is_watchface(void) {
|
|
return false;
|
|
}
|
|
|
|
void click_manager_init(ClickManager *click_manager) {
|
|
return;
|
|
}
|
|
|
|
void click_manager_clear(ClickManager *click_manager) {
|
|
return;
|
|
}
|
|
|
|
void click_manager_reset(ClickManager *click_manager) {
|
|
return;
|
|
}
|
|
|
|
void watchface_reset_click_manager(void) {
|
|
return;
|
|
}
|
|
|
|
Animation *window_transition_default_pop_create_animation(WindowTransitioningContext *context) {
|
|
window_transition_context_disappear(context);
|
|
window_transition_context_appear(context);
|
|
return animation_create();
|
|
}
|
|
|
|
const WindowTransitionImplementation window_transition_default_pop_implementation = {
|
|
.create_animation = window_transition_default_pop_create_animation,
|
|
};
|
|
|
|
const WindowTransitionImplementation *window_transition_get_default_pop_implementation() {
|
|
return &window_transition_default_pop_implementation;
|
|
}
|
|
|
|
Animation *window_transition_default_push_create_animation(WindowTransitioningContext *context) {
|
|
window_transition_context_disappear(context);
|
|
window_transition_context_appear(context);
|
|
return animation_create();
|
|
}
|
|
|
|
const WindowTransitionImplementation window_transition_default_push_implementation = {
|
|
.create_animation = window_transition_default_push_create_animation,
|
|
};
|
|
|
|
const WindowTransitionImplementation *window_transition_get_default_push_implementation() {
|
|
return &window_transition_default_push_implementation;
|
|
}
|
|
|
|
Animation *window_transition_none_create_animation(WindowTransitioningContext *context) {
|
|
window_transition_context_disappear(context);
|
|
window_transition_context_appear(context);
|
|
return animation_create();
|
|
}
|
|
|
|
const WindowTransitionImplementation g_window_transition_none_implementation = {
|
|
.create_animation = window_transition_none_create_animation,
|
|
};
|
|
|
|
void compositor_transition(const CompositorTransition *type) {
|
|
Window *window = modal_manager_get_top_window();
|
|
if (window) {
|
|
GContext ctx;
|
|
memset(&ctx, 0, sizeof(GContext));
|
|
modal_manager_render(&ctx);
|
|
}
|
|
}
|
|
|
|
void app_click_config_setup_with_window(ClickManager *click_manager, struct Window *window) {
|
|
s_last_click_configured_window = window;
|
|
}
|
|
|
|
// Helpers
|
|
////////////////////////////////////
|
|
static int16_t prv_get_load_unload_count(void) {
|
|
return s_load_count - s_unload_count;
|
|
}
|
|
|
|
static int16_t prv_get_appear_disappear_count(void) {
|
|
return s_appear_count - s_disappear_count;
|
|
}
|
|
|
|
static void prv_reset_counts(void) {
|
|
s_load_count = 0;
|
|
s_unload_count = 0;
|
|
s_appear_count = 0;
|
|
s_disappear_count = 0;
|
|
}
|
|
|
|
static void prv_click_config_provider(void *context) {
|
|
return;
|
|
}
|
|
|
|
static void prv_window_appear(Window *window) {
|
|
cl_check(window);
|
|
cl_assert_equal_i(window->on_screen, true);
|
|
s_appear_count++;
|
|
cl_check(s_appear_count >= 1);
|
|
}
|
|
|
|
static void prv_window_disappear(Window *window) {
|
|
cl_check(window);
|
|
cl_assert_equal_i(window->on_screen, false);
|
|
s_disappear_count++;
|
|
cl_check(s_appear_count >= 0);
|
|
}
|
|
|
|
static void prv_window_load(Window *window) {
|
|
cl_check(window);
|
|
cl_assert_equal_i(window->on_screen, true);
|
|
s_load_count++;
|
|
}
|
|
|
|
static void prv_window_unload(Window *window) {
|
|
cl_check(window);
|
|
cl_assert_equal_i(window->on_screen, false);
|
|
s_unload_count++;
|
|
window_destroy(window);
|
|
}
|
|
|
|
static void prv_push_window_load(Window *window) {
|
|
prv_window_load(window);
|
|
Window *new_window = window_create();
|
|
window_set_window_handlers(new_window, &(WindowHandlers){
|
|
.load = prv_window_load,
|
|
.unload = prv_window_unload
|
|
});
|
|
|
|
cl_check(window->parent_window_stack);
|
|
cl_assert_equal_i(window->on_screen, true);
|
|
cl_assert_equal_i(window->is_loaded, false);
|
|
|
|
window_stack_push(window->parent_window_stack, new_window, true);
|
|
}
|
|
|
|
static void prv_pop_window_load(Window *window) {
|
|
prv_window_load(window);
|
|
|
|
cl_check(window->parent_window_stack);
|
|
cl_assert_equal_i(window->on_screen, true);
|
|
cl_assert_equal_i(window->is_loaded, false);
|
|
|
|
window_stack_pop(window->parent_window_stack, true);
|
|
}
|
|
|
|
static void prv_push_window_unload(Window *window) {
|
|
WindowStack *stack = window->parent_window_stack;
|
|
|
|
prv_window_unload(window);
|
|
|
|
Window *new_window = window_create();
|
|
window_set_window_handlers(new_window, &(WindowHandlers){
|
|
.load = prv_window_load,
|
|
.unload = prv_window_unload,
|
|
.appear = prv_window_appear
|
|
});
|
|
|
|
cl_check(stack);
|
|
cl_check(new_window);
|
|
|
|
window_stack_push(stack, new_window, true);
|
|
}
|
|
|
|
static void prv_pop_window_unload(Window *window) {
|
|
window_stack_remove(window, true);
|
|
prv_window_unload(window);
|
|
}
|
|
|
|
// Setup and Teardown
|
|
////////////////////////////////////
|
|
|
|
void test_window_stack__initialize(void) {
|
|
s_last_click_configured_window = NULL;
|
|
|
|
WindowStack *stack = app_state_get_window_stack();
|
|
*stack = (WindowStack) {};
|
|
|
|
modal_manager_reset();
|
|
|
|
prv_reset_counts();
|
|
|
|
stub_pebble_tasks_set_current(PebbleTask_KernelMain);
|
|
}
|
|
|
|
void test_window_stack__cleanup(void) {
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
app_window_stack_pop_all(false);
|
|
|
|
stub_pebble_tasks_set_current(PebbleTask_KernelMain);
|
|
|
|
modal_manager_pop_all();
|
|
|
|
fake_animation_cleanup();
|
|
|
|
cl_assert_equal_i(fake_pbl_malloc_num_net_allocs(), 0);
|
|
}
|
|
|
|
// Tests
|
|
////////////////////////////////////
|
|
|
|
void test_window_stack__basic_app_push(void) {
|
|
Window *window = window_create();
|
|
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
cl_check(app_state_get_window_stack());
|
|
cl_check(!app_state_get_window_stack()->list_head);
|
|
|
|
app_window_stack_push(window, true);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 1);
|
|
|
|
app_window_stack_pop(true);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 0);
|
|
|
|
window_destroy(window);
|
|
}
|
|
|
|
void test_window_stack__basic_modal_push(void) {
|
|
Window *window = window_create();
|
|
WindowStack *window_stack =
|
|
modal_manager_get_window_stack(ModalPriorityGeneric);
|
|
|
|
cl_check(window_stack);
|
|
cl_check(!window_stack->list_head);
|
|
|
|
window_stack_push(window_stack, window, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(window_stack), 1);
|
|
|
|
window_stack_pop(window_stack, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(window_stack), 0);
|
|
|
|
window_destroy(window);
|
|
}
|
|
|
|
void test_window_stack__basic_window_pop(void) {
|
|
Window *window1 = window_create();
|
|
Window *window2 = window_create();
|
|
|
|
// Switch to app state to push windows to the Application window stack
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
cl_check(app_state_get_window_stack());
|
|
cl_check(!app_state_get_window_stack()->list_head);
|
|
|
|
app_window_stack_push(window1, true);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 1);
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
|
|
app_window_stack_push(window2, true);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 2);
|
|
cl_assert_equal_i(window1->on_screen, false);
|
|
cl_assert_equal_i(window2->on_screen, true);
|
|
|
|
app_window_stack_pop(true);
|
|
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
cl_assert_equal_i(window2->on_screen, false);
|
|
|
|
app_window_stack_pop(true);
|
|
|
|
cl_assert_equal_i(window1->on_screen, false);
|
|
|
|
window_destroy(window1);
|
|
window_destroy(window2);
|
|
}
|
|
|
|
void test_window_stack__basic_window_pop_under(void) {
|
|
Window *window1 = window_create();
|
|
Window *window2 = window_create();
|
|
|
|
// Switch to app state to push windows to the Application window stack
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
cl_check(app_state_get_window_stack());
|
|
cl_check(!app_state_get_window_stack()->list_head);
|
|
|
|
app_window_stack_push(window1, true);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 1);
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
|
|
app_window_stack_push(window2, true);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 2);
|
|
cl_assert_equal_i(window1->on_screen, false);
|
|
cl_assert_equal_i(window2->on_screen, true);
|
|
|
|
app_window_stack_remove(window1, false);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 1);
|
|
cl_assert_equal_i(window2->on_screen, true);
|
|
cl_assert_equal_i(window1->on_screen, false);
|
|
|
|
app_window_stack_remove(window2, false);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 0);
|
|
cl_assert_equal_i(window2->on_screen, false);
|
|
cl_assert_equal_i(window1->on_screen, false);
|
|
|
|
window_destroy(window1);
|
|
window_destroy(window2);
|
|
}
|
|
|
|
void test_window_stack__pop_all(void) {
|
|
WindowStack *stack = modal_manager_get_window_stack(ModalPriorityGeneric);
|
|
Window *windows[3];
|
|
|
|
for (uint8_t idx = 0; idx < 3; idx++) {
|
|
windows[idx] = window_create();
|
|
}
|
|
|
|
window_stack_push(stack, windows[0], true);
|
|
cl_assert_equal_i(window_stack_count(stack), 1);
|
|
cl_assert_equal_i(windows[0]->on_screen, true);
|
|
|
|
window_stack_push(stack, windows[1], true);
|
|
cl_assert_equal_i(window_stack_count(stack), 2);
|
|
cl_assert_equal_i(windows[0]->on_screen, false);
|
|
cl_assert_equal_i(windows[1]->on_screen, true);
|
|
|
|
window_stack_push(stack, windows[2], true);
|
|
cl_assert_equal_i(window_stack_count(stack), 3);
|
|
cl_assert_equal_i(windows[0]->on_screen, false);
|
|
cl_assert_equal_i(windows[1]->on_screen, false);
|
|
cl_assert_equal_i(windows[2]->on_screen, true);
|
|
|
|
window_stack_pop_all(stack, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(stack), 0);
|
|
cl_assert_equal_i(windows[0]->on_screen, false);
|
|
cl_assert_equal_i(windows[1]->on_screen, false);
|
|
cl_assert_equal_i(windows[2]->on_screen, false);
|
|
|
|
for (uint8_t idx = 0; idx < 3; idx++) {
|
|
window_destroy(windows[idx]);
|
|
}
|
|
}
|
|
|
|
void test_window_stack__insert_next(void) {
|
|
Window *window1 = window_create();
|
|
Window *window2 = window_create();
|
|
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
cl_check(app_state_get_window_stack());
|
|
cl_check(!app_state_get_window_stack()->list_head);
|
|
|
|
app_window_stack_push(window1, true);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 1);
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
|
|
app_window_stack_insert_next(window2);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 2);
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
cl_assert_equal_i(window2->on_screen, false);
|
|
|
|
app_window_stack_pop(true);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 1);
|
|
cl_assert_equal_i(window1->on_screen, false);
|
|
cl_assert_equal_i(window2->on_screen, true);
|
|
|
|
app_window_stack_pop(true);
|
|
|
|
cl_assert_equal_i(app_window_stack_count(), 0);
|
|
cl_assert_equal_i(window2->on_screen, false);
|
|
|
|
window_destroy(window1);
|
|
window_destroy(window2);
|
|
}
|
|
|
|
// Description:
|
|
// During the push of a window, we push another window in the load handler of
|
|
// the window being pushed. This causes the loading window to disappaer from
|
|
// the screen (before it even appeared) and become subverted by the new window.
|
|
void test_window_stack__push_during_window_load(void) {
|
|
Window *window = window_create();
|
|
window_set_window_handlers(window, &(WindowHandlers){
|
|
.load = prv_push_window_load,
|
|
.unload = prv_window_unload,
|
|
.appear = prv_window_appear,
|
|
.disappear = prv_window_disappear
|
|
});
|
|
|
|
WindowStack *stack = app_state_get_window_stack();
|
|
|
|
// Switch to the app state to push a window
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
cl_check(stack);
|
|
cl_assert_equal_i(window_stack_count(stack), 0);
|
|
|
|
window_stack_push(stack, window, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(stack), 2);
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 2);
|
|
cl_assert_equal_i(prv_get_appear_disappear_count(), 0);
|
|
|
|
cl_check(((WindowStackItem *)stack->list_head)->window != window);
|
|
|
|
window_stack_pop_all(stack, false);
|
|
|
|
cl_assert_equal_i(window_stack_count(stack), 0);
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 0);
|
|
cl_assert_equal_i(prv_get_appear_disappear_count(), 0);
|
|
}
|
|
|
|
// Description:
|
|
// This test ensures that when we push windows onto modal window stacks, that
|
|
// only the appropriate window is visible at a given time.
|
|
void test_window_stack__modal_priority(void) {
|
|
Window *windows[NumModalPriorities];
|
|
WindowStack *window_stacks[NumModalPriorities];
|
|
ModalPriority idx = ModalPriorityInvalid;
|
|
|
|
do {
|
|
++idx;
|
|
windows[idx] = window_create();
|
|
window_set_window_handlers(windows[idx], &(WindowHandlers) {
|
|
.unload = prv_window_unload
|
|
});
|
|
window_stacks[idx] = modal_manager_get_window_stack(idx);
|
|
} while (idx < NumModalPriorities - 1);
|
|
|
|
idx = ModalPriorityInvalid;
|
|
|
|
do {
|
|
++idx;
|
|
window_stack_push(window_stacks[idx], windows[idx], false);
|
|
|
|
cl_assert_equal_i(window_stack_count(window_stacks[idx]), 1);
|
|
cl_assert_equal_i(windows[idx]->on_screen, true);
|
|
|
|
// All windows below the current priority should now not be on the screen
|
|
// as the modal has subverted them.
|
|
ModalPriority sub_idx = idx;
|
|
do {
|
|
sub_idx--;
|
|
if (sub_idx == ModalPriorityInvalid) {
|
|
break;
|
|
}
|
|
cl_assert_equal_i(window_stack_count(window_stacks[sub_idx]), 1);
|
|
cl_assert_equal_i(windows[sub_idx]->on_screen, false);
|
|
} while (true);
|
|
} while (idx < NumModalPriorities - 1);
|
|
}
|
|
|
|
void test_window_stack__modal_properties_transparent(void) {
|
|
const int num_windows_per_stack = 2;
|
|
Window *windows[NumModalPriorities][num_windows_per_stack];
|
|
WindowStack *window_stacks[NumModalPriorities];
|
|
|
|
for (ModalPriority idx = ModalPriorityMin; idx < NumModalPriorities; idx++) {
|
|
window_stacks[idx] = modal_manager_get_window_stack(idx);
|
|
for (int i = 0; i < num_windows_per_stack; i++) {
|
|
windows[idx][i] = window_create();
|
|
}
|
|
}
|
|
|
|
// The following checks use integer priorities to clearly indicate stack order
|
|
// We check to make sure the first occurrence of integer values are less than NumModalPriorities
|
|
|
|
// Test: No top window does not result in Exists
|
|
// Test: No top window results in Transparent and Unfocused
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Transparent | ModalProperty_Unfocused);
|
|
|
|
// Add priority 0 (discreet) opaque window 0
|
|
cl_assert(NumModalPriorities > 0);
|
|
window_stack_push(window_stacks[0], windows[0][0], false);
|
|
|
|
// A discreet window just went on-screen
|
|
// Test: Discreet windows have no compositor transition
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_RenderRequested);
|
|
|
|
window_stack_remove(windows[0][0], false);
|
|
|
|
// Add priority 2 opaque window 0
|
|
cl_assert(NumModalPriorities > 2);
|
|
window_stack_push(window_stacks[2], windows[2][0], false);
|
|
|
|
// An opaque window just went on-screen
|
|
// Test: A top window results in Exists
|
|
// Test: One opaque top window removes Transparent and Unfocused
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_CompositorTransitions |
|
|
ModalProperty_RenderRequested);
|
|
cl_assert_equal_i(windows[2][0]->on_screen, true);
|
|
cl_assert_equal_i(windows[2][0]->is_click_configured, true);
|
|
cl_assert_equal_p(s_last_click_configured_window, windows[2][0]);
|
|
|
|
// Add priority 2 transparent window 1
|
|
window_set_transparent(windows[2][1], true);
|
|
window_stack_push(window_stacks[2], windows[2][1], false);
|
|
|
|
// A transparent window is now the top window
|
|
// Test: Opaque windows that are not the top window have no affect on transparency
|
|
// Test: One transparent top window results in Transparent
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_CompositorTransitions |
|
|
ModalProperty_RenderRequested | ModalProperty_Transparent);
|
|
// Checks are listed from top to bottom
|
|
cl_assert_equal_i(windows[2][1]->on_screen, true);
|
|
cl_assert_equal_i(windows[2][1]->is_click_configured, true);
|
|
cl_assert_equal_i(windows[2][0]->on_screen, false);
|
|
cl_assert_equal_i(windows[2][0]->is_click_configured, false);
|
|
cl_assert_equal_p(s_last_click_configured_window, windows[2][1]);
|
|
|
|
// Add priority 3 opaque window 0
|
|
cl_assert(NumModalPriorities > 3);
|
|
window_stack_push(window_stacks[3], windows[3][0], false);
|
|
|
|
// An opaque top window of a different stack is now obstructing the transparent top window
|
|
// Top here throughout means that it is the top window of the window stack it is in
|
|
// Test: An opaque top window above a transparent top window removes Transparent
|
|
// i.e. A transparent top window below an opaque top window does not result in Transparent
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_CompositorTransitions |
|
|
ModalProperty_RenderRequested);
|
|
cl_assert_equal_i(windows[3][0]->on_screen, true);
|
|
cl_assert_equal_i(windows[3][0]->is_click_configured, true);
|
|
cl_assert_equal_i(windows[2][1]->on_screen, false);
|
|
cl_assert_equal_i(windows[2][1]->is_click_configured, false);
|
|
cl_assert_equal_p(s_last_click_configured_window, windows[3][0]);
|
|
|
|
// Add priority 3 transparent window 1
|
|
window_set_transparent(windows[3][1], true);
|
|
window_stack_push(window_stacks[3], windows[3][1], false);
|
|
|
|
// A transparent window is now the top window, and there is another transparent window below
|
|
// Test: Multiple transparent top windows result in Transparent
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_CompositorTransitions |
|
|
ModalProperty_RenderRequested | ModalProperty_Transparent);
|
|
cl_assert_equal_i(windows[3][1]->on_screen, true);
|
|
cl_assert_equal_i(windows[3][1]->is_click_configured, true);
|
|
cl_assert_equal_i(windows[3][0]->on_screen, false);
|
|
cl_assert_equal_i(windows[3][0]->is_click_configured, false);
|
|
cl_assert_equal_i(windows[2][1]->on_screen, true);
|
|
cl_assert_equal_i(windows[2][1]->is_click_configured, false);
|
|
cl_assert_equal_p(s_last_click_configured_window, windows[3][1]);
|
|
|
|
// Add priority 1 opaque window 0
|
|
cl_assert(NumModalPriorities > 1);
|
|
window_stack_push(window_stacks[1], windows[1][0], false);
|
|
|
|
// An opaque top window is now below two transparent top windows
|
|
// Test: An opaque top window below a transparent top window removes Transparent
|
|
// i.e. A transparent top window above an opaque top window does not result in Transparent
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_CompositorTransitions |
|
|
ModalProperty_RenderRequested);
|
|
cl_assert_equal_i(windows[3][1]->on_screen, true);
|
|
cl_assert_equal_i(windows[3][1]->is_click_configured, true);
|
|
cl_assert_equal_i(windows[2][1]->on_screen, true);
|
|
cl_assert_equal_i(windows[2][1]->is_click_configured, false);
|
|
cl_assert_equal_i(windows[1][0]->on_screen, true);
|
|
cl_assert_equal_i(windows[1][0]->is_click_configured, false);
|
|
cl_assert_equal_p(s_last_click_configured_window, windows[3][1]);
|
|
|
|
for (ModalPriority idx = ModalPriorityMin; idx < NumModalPriorities; idx++) {
|
|
for (int i = 0; i < num_windows_per_stack; i++) {
|
|
window_stack_remove(windows[idx][i], false);
|
|
window_destroy(windows[idx][i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void test_window_stack__modal_properties_unfocused(void) {
|
|
const int num_windows_per_stack = 2;
|
|
Window *windows[NumModalPriorities][num_windows_per_stack];
|
|
WindowStack *window_stacks[NumModalPriorities];
|
|
|
|
for (ModalPriority idx = ModalPriorityMin; idx < NumModalPriorities; idx++) {
|
|
window_stacks[idx] = modal_manager_get_window_stack(idx);
|
|
for (int i = 0; i < num_windows_per_stack; i++) {
|
|
windows[idx][i] = window_create();
|
|
}
|
|
}
|
|
|
|
// The following checks use integer priorities to clearly indicate stack order
|
|
// We check to make sure the first occurrence of integer values are less than NumModalPriorities
|
|
|
|
// Test: No top window does not result in Exists
|
|
// Test: No top window results in Transparent and Unfocused
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Transparent | ModalProperty_Unfocused);
|
|
|
|
// Add priority 2 opaque window 0
|
|
cl_assert(NumModalPriorities > 2);
|
|
window_stack_push(window_stacks[2], windows[2][0], false);
|
|
|
|
// Add priority 2 unfocusable window 1
|
|
window_set_focusable(windows[2][1], false);
|
|
window_stack_push(window_stacks[2], windows[2][1], false);
|
|
|
|
// An unfocusable window is now the top window
|
|
// Test: Opaque windows that are not the top window have no affect on unfocusable
|
|
// Test: One unfocusable top window results in Unfocused
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_CompositorTransitions |
|
|
ModalProperty_RenderRequested | ModalProperty_Unfocused);
|
|
// Checks are listed from top to bottom
|
|
cl_assert_equal_i(windows[2][1]->on_screen, true);
|
|
cl_assert_equal_i(windows[2][1]->is_click_configured, false);
|
|
cl_assert_equal_i(windows[2][0]->on_screen, false);
|
|
cl_assert_equal_i(windows[2][0]->is_click_configured, false);
|
|
cl_assert_equal_p(s_last_click_configured_window, windows[2][0]);
|
|
|
|
// Add priority 3 opaque window 0
|
|
cl_assert(NumModalPriorities > 3);
|
|
window_stack_push(window_stacks[3], windows[3][0], false);
|
|
|
|
// An opaque top window of a different stack is now obstructing the unfocusable top window
|
|
// Top here throughout means that it is the top window of the window stack it is in
|
|
// Test: An opaque top window above a unfocusable top window removes Unfocusable
|
|
// i.e. A unfocusable top window below an opaque top window does not result in Unfocusable
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_CompositorTransitions |
|
|
ModalProperty_RenderRequested);
|
|
cl_assert_equal_i(windows[3][0]->on_screen, true);
|
|
cl_assert_equal_i(windows[3][0]->is_click_configured, true);
|
|
cl_assert_equal_i(windows[2][1]->on_screen, false);
|
|
cl_assert_equal_i(windows[2][1]->is_click_configured, false);
|
|
cl_assert_equal_p(s_last_click_configured_window, windows[3][0]);
|
|
|
|
// Add priority 3 unfocusable window 1
|
|
window_set_focusable(windows[3][1], false);
|
|
window_stack_push(window_stacks[3], windows[3][1], false);
|
|
|
|
// A unfocusable window is now the top window, and there is another unfocusable window below
|
|
// Test: Multiple unfocusable top windows result in Unfocusable
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_CompositorTransitions |
|
|
ModalProperty_RenderRequested | ModalProperty_Unfocused);
|
|
cl_assert_equal_i(windows[3][1]->on_screen, true);
|
|
cl_assert_equal_i(windows[3][1]->is_click_configured, false);
|
|
cl_assert_equal_i(windows[3][0]->on_screen, false);
|
|
cl_assert_equal_i(windows[3][0]->is_click_configured, false);
|
|
cl_assert_equal_i(windows[2][1]->on_screen, false);
|
|
cl_assert_equal_i(windows[2][1]->is_click_configured, false);
|
|
cl_assert_equal_p(s_last_click_configured_window, windows[3][0]);
|
|
|
|
// Add priority 1 opaque window 0
|
|
cl_assert(NumModalPriorities > 1);
|
|
window_stack_push(window_stacks[1], windows[1][0], false);
|
|
|
|
// An opaque top window is now below two unfocusable top windows
|
|
// Test: An opaque top window below a unfocusable top window removes Unfocusable
|
|
// i.e. A unfocusable top window above an opaque top window does not result in Unfocusable
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_CompositorTransitions |
|
|
ModalProperty_RenderRequested);
|
|
cl_assert_equal_i(windows[3][1]->on_screen, true);
|
|
cl_assert_equal_i(windows[3][1]->is_click_configured, false);
|
|
cl_assert_equal_i(windows[2][1]->on_screen, false);
|
|
cl_assert_equal_i(windows[2][1]->is_click_configured, false);
|
|
cl_assert_equal_i(windows[1][0]->on_screen, false);
|
|
cl_assert_equal_i(windows[1][0]->is_click_configured, true);
|
|
cl_assert_equal_p(s_last_click_configured_window, windows[1][0]);
|
|
|
|
for (ModalPriority idx = ModalPriorityMin; idx < NumModalPriorities; idx++) {
|
|
for (int i = 0; i < num_windows_per_stack; i++) {
|
|
window_stack_remove(windows[idx][i], false);
|
|
window_destroy(windows[idx][i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void test_window_stack__modal_properties_enable_disable(void) {
|
|
// Enable all modals
|
|
modal_manager_set_min_priority(ModalPriorityMin);
|
|
|
|
Window *window1 = window_create();
|
|
modal_window_push(window1, ModalPriorityGeneric, false);
|
|
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_CompositorTransitions |
|
|
ModalProperty_RenderRequested);
|
|
|
|
// Disable all modals
|
|
modal_manager_set_min_priority(ModalPriorityMax);
|
|
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Transparent | ModalProperty_Unfocused);
|
|
|
|
// Re-enable all modals
|
|
modal_manager_set_min_priority(ModalPriorityMin);
|
|
|
|
modal_manager_event_loop_upkeep();
|
|
cl_assert_equal_i(modal_manager_get_properties(),
|
|
ModalProperty_Exists | ModalProperty_CompositorTransitions);
|
|
}
|
|
|
|
// Description:
|
|
// This test ensures that when we push a window onto the modal window stack, then
|
|
// we push another window onto the modal window stack at a lower priority, then
|
|
// pushing the first at a lower priority than the second will bring the second onto
|
|
// the screen and subvert the first.
|
|
void test_window_stack__modal_reprioritize(void) {
|
|
Window *window1 = window_create();
|
|
Window *window2 = window_create();
|
|
uint8_t base_priority = ModalPriorityDiscreet + 3;
|
|
|
|
window_set_click_config_provider(window1, prv_click_config_provider);
|
|
|
|
window_set_click_config_provider(window2, prv_click_config_provider);
|
|
|
|
modal_window_push(window1, base_priority, false);
|
|
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
|
|
modal_window_push(window2, base_priority - 1, false);
|
|
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
cl_assert_equal_i(window2->on_screen, false);
|
|
|
|
modal_window_push(window1, base_priority - 2, false);
|
|
|
|
modal_manager_event_loop_upkeep();
|
|
|
|
cl_assert_equal_i(window1->on_screen, false);
|
|
cl_assert_equal_i(window2->on_screen, true);
|
|
|
|
window_stack_remove(window2, false);
|
|
|
|
modal_manager_event_loop_upkeep();
|
|
|
|
cl_assert_equal_i(window2->on_screen, false);
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
|
|
window_stack_remove(window1, true);
|
|
|
|
cl_assert_equal_i(window1->on_screen, false);
|
|
|
|
window_destroy(window1);
|
|
window_destroy(window2);
|
|
}
|
|
|
|
// Description:
|
|
// This test ensures that we are able to work with both the modal window stacks
|
|
// and the application stack at the same time.
|
|
void test_window_stack__modal_and_app(void) {
|
|
Window *window1 = window_create();
|
|
Window *window2 = window_create();
|
|
|
|
WindowStack *app_stack = app_state_get_window_stack();
|
|
WindowStack *modal_stack = modal_manager_get_window_stack(ModalPriorityGeneric);
|
|
|
|
cl_check(app_stack);
|
|
cl_check(modal_stack);
|
|
|
|
cl_check(!app_stack->list_head);
|
|
cl_check(!modal_stack->list_head);
|
|
|
|
// Switch to the app state to push a window
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
window_stack_push(app_stack, window1, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(app_stack), 1);
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
cl_assert_equal_p(s_last_click_configured_window, window1);
|
|
|
|
// Switch to the kernel to push a modal window
|
|
stub_pebble_tasks_set_current(PebbleTask_KernelMain);
|
|
|
|
window_stack_push(modal_stack, window2, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(modal_stack), 1);
|
|
cl_assert_equal_i(window2->on_screen, true);
|
|
cl_assert_equal_p(s_last_click_configured_window, window2);
|
|
|
|
// Switch to modal happens via the compositor
|
|
compositor_transition(NULL);
|
|
// Call the upkeep function so the change in state is handled
|
|
modal_manager_event_loop_upkeep();
|
|
|
|
// The app is now obstructed by an opaque modal, it should be idle
|
|
cl_assert_equal_b(s_app_idle, true);
|
|
|
|
// Assert that the window pushed onto the app stack has lost focus
|
|
// We do this by checking the last event, which should have been a focus
|
|
// lost event.
|
|
PebbleEvent event = fake_event_get_last();
|
|
cl_assert_equal_i(event.type, PEBBLE_APP_WILL_CHANGE_FOCUS_EVENT);
|
|
cl_assert_equal_i(event.app_focus.in_focus, false);
|
|
|
|
cl_assert_equal_i(window_stack_count(app_stack), 1);
|
|
|
|
// Pop the modal window off the stack
|
|
window_stack_remove(window2, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(modal_stack), 0);
|
|
cl_assert_equal_i(window2->on_screen, false);
|
|
|
|
// Switch to app happens via the compositor
|
|
compositor_transition(NULL);
|
|
// Call the upkeep function so the change in state is handled
|
|
modal_manager_event_loop_upkeep();
|
|
|
|
// The app is unobstructed, it should not be idle
|
|
cl_assert_equal_b(s_app_idle, false);
|
|
|
|
// Assert that the window pushed onto the app stack has regained focus,
|
|
// this is also done by checking the last event.
|
|
event = fake_event_get_last();
|
|
|
|
cl_assert_equal_i(event.type, PEBBLE_APP_WILL_CHANGE_FOCUS_EVENT);
|
|
cl_assert_equal_i(event.app_focus.in_focus, true);
|
|
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
|
|
window_stack_remove(window1, true);
|
|
|
|
cl_assert_equal_i(window1->on_screen, false);
|
|
cl_assert_equal_i(window_stack_count(app_stack), 0);
|
|
|
|
window_destroy(window1);
|
|
window_destroy(window2);
|
|
}
|
|
|
|
// Tests modal and app transitions with a transparent modal window
|
|
void test_window_stack__transparent_modal_and_app(void) {
|
|
Window *window1 = window_create();
|
|
Window *window2 = window_create();
|
|
cl_assert_equal_b(window_is_transparent(window2), false);
|
|
window_set_transparent(window2, true);
|
|
cl_assert_equal_b(window_is_transparent(window2), true);
|
|
|
|
WindowStack *app_stack = app_state_get_window_stack();
|
|
WindowStack *modal_stack = modal_manager_get_window_stack(ModalPriorityGeneric);
|
|
|
|
cl_check(app_stack);
|
|
cl_check(modal_stack);
|
|
|
|
cl_check(!app_stack->list_head);
|
|
cl_check(!modal_stack->list_head);
|
|
|
|
// Switch to the app state to push a window
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
window_stack_push(app_stack, window1, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(app_stack), 1);
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
cl_assert_equal_p(s_last_click_configured_window, window1);
|
|
|
|
// Switch to the kernel to push a modal window
|
|
stub_pebble_tasks_set_current(PebbleTask_KernelMain);
|
|
|
|
window_stack_push(modal_stack, window2, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(modal_stack), 1);
|
|
cl_assert_equal_i(window2->on_screen, true);
|
|
cl_assert_equal_p(s_last_click_configured_window, window2);
|
|
|
|
// Switch to modal happens via the compositor
|
|
compositor_transition(NULL);
|
|
// Call the upkeep function so the change in state is handled
|
|
modal_manager_event_loop_upkeep();
|
|
|
|
// The app is now obstructed by a transparent modal, it should remain active
|
|
cl_assert_equal_b(s_app_idle, false);
|
|
|
|
// Assert that the window pushed onto the app stack has lost focus
|
|
// We do this by checking the last event, which should have been a focus
|
|
// lost event.
|
|
PebbleEvent event = fake_event_get_last();
|
|
cl_assert_equal_i(event.type, PEBBLE_APP_WILL_CHANGE_FOCUS_EVENT);
|
|
cl_assert_equal_i(event.app_focus.in_focus, false);
|
|
|
|
cl_assert_equal_i(window_stack_count(app_stack), 1);
|
|
|
|
// Pop the modal window off the stack
|
|
window_stack_remove(window2, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(modal_stack), 0);
|
|
cl_assert_equal_i(window2->on_screen, false);
|
|
|
|
// Switch to app happens via the compositor
|
|
compositor_transition(NULL);
|
|
// Call the upkeep function so the change in state is handled
|
|
modal_manager_event_loop_upkeep();
|
|
|
|
// The app is unobstructed, it should remain active
|
|
cl_assert_equal_b(s_app_idle, false);
|
|
|
|
// Assert that the window pushed onto the app stack has regained focus,
|
|
// this is also done by checking the last event.
|
|
event = fake_event_get_last();
|
|
|
|
cl_assert_equal_i(event.type, PEBBLE_APP_WILL_CHANGE_FOCUS_EVENT);
|
|
cl_assert_equal_i(event.app_focus.in_focus, true);
|
|
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
|
|
window_stack_remove(window1, true);
|
|
|
|
cl_assert_equal_i(window1->on_screen, false);
|
|
cl_assert_equal_i(window_stack_count(app_stack), 0);
|
|
|
|
window_destroy(window1);
|
|
window_destroy(window2);
|
|
}
|
|
|
|
// Tests modal and app transitions with an unfocusable modal window
|
|
void test_window_stack__unfocusable_modal_and_app(void) {
|
|
Window *window1 = window_create();
|
|
Window *window2 = window_create();
|
|
cl_assert_equal_b(window_is_focusable(window2), true);
|
|
window_set_focusable(window2, false);
|
|
cl_assert_equal_b(window_is_focusable(window2), false);
|
|
|
|
WindowStack *app_stack = app_state_get_window_stack();
|
|
WindowStack *modal_stack = modal_manager_get_window_stack(ModalPriorityGeneric);
|
|
|
|
cl_check(app_stack);
|
|
cl_check(modal_stack);
|
|
|
|
cl_check(!app_stack->list_head);
|
|
cl_check(!modal_stack->list_head);
|
|
|
|
// Switch to the app state to push a window
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
window_stack_push(app_stack, window1, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(app_stack), 1);
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
cl_assert_equal_p(s_last_click_configured_window, window1);
|
|
|
|
// Switch to the kernel to push a modal window
|
|
stub_pebble_tasks_set_current(PebbleTask_KernelMain);
|
|
|
|
window_stack_push(modal_stack, window2, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(modal_stack), 1);
|
|
cl_assert_equal_i(window2->on_screen, true);
|
|
cl_assert_equal_p(s_last_click_configured_window, window1);
|
|
|
|
// Switch to modal happens via the compositor
|
|
compositor_transition(NULL);
|
|
// Call the upkeep function so the change in state is handled
|
|
modal_manager_event_loop_upkeep();
|
|
|
|
// The app is now obstructed by a unfocusable modal, it should remain active
|
|
cl_assert_equal_b(s_app_idle, false);
|
|
|
|
// The app should retain focus
|
|
PebbleEvent event = fake_event_get_last();
|
|
cl_assert_equal_i(event.type, PEBBLE_APP_WILL_CHANGE_FOCUS_EVENT);
|
|
cl_assert_equal_i(event.app_focus.in_focus, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(app_stack), 1);
|
|
|
|
// Pop the modal window off the stack
|
|
window_stack_remove(window2, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(modal_stack), 0);
|
|
cl_assert_equal_i(window2->on_screen, false);
|
|
|
|
// Switch to app happens via the compositor
|
|
compositor_transition(NULL);
|
|
// Call the upkeep function so the change in state is handled
|
|
modal_manager_event_loop_upkeep();
|
|
|
|
// The app is unobstructed, it should remain active
|
|
cl_assert_equal_b(s_app_idle, false);
|
|
|
|
// Assert that the window pushed onto the app stack has remained focused
|
|
event = fake_event_get_last();
|
|
|
|
cl_assert_equal_i(event.type, PEBBLE_APP_WILL_CHANGE_FOCUS_EVENT);
|
|
cl_assert_equal_i(event.app_focus.in_focus, true);
|
|
|
|
cl_assert_equal_i(window1->on_screen, true);
|
|
|
|
window_stack_remove(window1, true);
|
|
|
|
cl_assert_equal_i(window1->on_screen, false);
|
|
cl_assert_equal_i(window_stack_count(app_stack), 0);
|
|
|
|
window_destroy(window1);
|
|
window_destroy(window2);
|
|
}
|
|
|
|
// Description:
|
|
// This test ensures that the flow of adding a window to the window stack is followed
|
|
// correctly. That is, we add the window to the window stack, its load handler is
|
|
// called, it calls to set the click config, and the click config is set properly.
|
|
void test_window_stack__window_flow(void) {
|
|
Window *window = window_create();
|
|
|
|
window_set_window_handlers(window, &(WindowHandlers){
|
|
.load = prv_window_load,
|
|
.unload = prv_window_unload,
|
|
.appear = prv_window_appear,
|
|
.disappear = prv_window_disappear
|
|
});
|
|
|
|
window_set_click_config_provider(window, prv_click_config_provider);
|
|
|
|
cl_check(window->is_waiting_for_click_config);
|
|
|
|
WindowStack *stack = app_state_get_window_stack();
|
|
|
|
cl_check(stack);
|
|
cl_check(!stack->list_head);
|
|
|
|
// Switch to the app state to push the window
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
window_stack_push(stack, window, true);
|
|
|
|
cl_assert_equal_i(window->on_screen, 1);
|
|
cl_assert_equal_i(window_stack_count(stack), 1);
|
|
|
|
// Ensure the load handler was called
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 1);
|
|
|
|
// Ensure the appear handler was called
|
|
cl_assert_equal_i(prv_get_appear_disappear_count(), 1);
|
|
|
|
// Ensure the click config handler was called
|
|
cl_assert_equal_i(window->is_waiting_for_click_config, false);
|
|
|
|
window_stack_pop(stack, false);
|
|
|
|
cl_assert_equal_i(window_stack_count(stack), 0);
|
|
|
|
// Ensure the disappear handler was called
|
|
cl_assert_equal_i(prv_get_appear_disappear_count(), 0);
|
|
|
|
// Ensure the unload handler was called
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 0);
|
|
}
|
|
|
|
void test_window_stack__dump(void) {
|
|
WindowStack *stack = app_state_get_window_stack();
|
|
Window *window1 = window_create();
|
|
Window *window2 = window_create();
|
|
Window *window3 = window_create();
|
|
window1->debug_name = "Window1";
|
|
window2->debug_name = "Window2";
|
|
window3->debug_name = "Window3";
|
|
|
|
window_stack_push(stack, window1, true);
|
|
window_stack_push(stack, window2, true);
|
|
window_stack_push(stack, window3, true);
|
|
|
|
WindowStackDump *dump;
|
|
size_t stack_depth = window_stack_dump(stack, &dump);
|
|
cl_assert_equal_i(stack_depth, 3);
|
|
cl_assert_equal_p(dump[0].addr, window3);
|
|
cl_assert_equal_s(dump[0].name, "Window3");
|
|
cl_assert_equal_p(dump[1].addr, window2);
|
|
cl_assert_equal_s(dump[1].name, "Window2");
|
|
cl_assert_equal_p(dump[2].addr, window1);
|
|
cl_assert_equal_s(dump[2].name, "Window1");
|
|
kernel_free(dump);
|
|
}
|
|
|
|
void test_window_stack__pop_all_modals(void) {
|
|
Window *windows[NumModalPriorities];
|
|
WindowStack *window_stacks[NumModalPriorities];
|
|
|
|
for (ModalPriority idx = ModalPriorityMin; idx < NumModalPriorities; idx++) {
|
|
window_stacks[idx] = modal_manager_get_window_stack(idx);
|
|
windows[idx] = window_create();
|
|
window_stack_push(window_stacks[idx], windows[idx], true);
|
|
// Modals are visible as we push from lowest priority to top most
|
|
cl_assert_equal_b(windows[idx]->on_screen, true);
|
|
}
|
|
|
|
// Only the top modal is visible
|
|
modal_manager_event_loop_upkeep();
|
|
for (ModalPriority idx = ModalPriorityMin; idx < NumModalPriorities; idx++) {
|
|
cl_assert_equal_b(windows[idx]->on_screen, (idx == NumModalPriorities - 1));
|
|
}
|
|
|
|
// Pop all modals
|
|
modal_manager_pop_all();
|
|
modal_manager_event_loop_upkeep();
|
|
|
|
_Static_assert(ModalPriorityMin == ModalPriorityDiscreet,
|
|
"Update the test to handle priorities below discreet.");
|
|
// Discreet should not be popped
|
|
cl_assert_equal_b(windows[ModalPriorityDiscreet]->on_screen, true);
|
|
|
|
// All other modals should be popped
|
|
for (ModalPriority idx = ModalPriorityDiscreet + 1; idx < NumModalPriorities; idx++) {
|
|
cl_assert_equal_b(windows[idx]->on_screen, false);
|
|
}
|
|
|
|
}
|
|
|
|
// Edge Case Tests
|
|
////////////////////////////////////
|
|
|
|
// Description:
|
|
// During the load handler of a window, we pop it.
|
|
void test_window_stack__pop_during_window_load(void) {
|
|
Window *window = window_create();
|
|
window_set_window_handlers(window, &(WindowHandlers){
|
|
.load = prv_pop_window_load,
|
|
.unload = prv_window_unload
|
|
});
|
|
|
|
WindowStack *stack = app_state_get_window_stack();
|
|
|
|
// Switch to the app state to push a window
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
cl_check(stack);
|
|
cl_assert_equal_i(window_stack_count(stack), 0);
|
|
|
|
window_stack_push(stack, window, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(stack), 0);
|
|
|
|
// We popped the window off the screen, but the unload handler
|
|
// should not have been called for it, as it hasn't finished
|
|
// unloading.
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 1);
|
|
}
|
|
|
|
// Description:
|
|
// In this test, we push a window during the unload handler of a window.
|
|
void test_window_stack__push_during_window_unload(void) {
|
|
Window *window = window_create();
|
|
|
|
window_set_window_handlers(window, &(WindowHandlers){
|
|
.load = prv_window_load,
|
|
.unload = prv_push_window_unload
|
|
});
|
|
|
|
WindowStack *stack = app_state_get_window_stack();
|
|
|
|
// Switch to the app state to push a window
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
cl_check(stack);
|
|
cl_assert_equal_i(window_stack_count(stack), 0);
|
|
|
|
window_stack_push(stack, window, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(stack), 1);
|
|
cl_assert_equal_i(window->on_screen, true);
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 1);
|
|
|
|
window_stack_pop(stack, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(stack), 1);
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 1);
|
|
}
|
|
|
|
// Description:
|
|
// In this test we push two windows that push windows during their unload handlers.
|
|
// We want to verify that those two windows stay on the stack after calling
|
|
// `window_stack_pop_all`.
|
|
void test_window_stack__push_during_window_unload_multiple(void) {
|
|
Window *window1 = window_create();
|
|
Window *window2 = window_create();
|
|
|
|
window_set_window_handlers(window1, &(WindowHandlers){
|
|
.load = prv_window_load,
|
|
.unload = prv_push_window_unload,
|
|
.appear = prv_window_appear,
|
|
.disappear = prv_window_disappear
|
|
});
|
|
|
|
window_set_window_handlers(window2, &(WindowHandlers){
|
|
.load = prv_window_load,
|
|
.unload = prv_push_window_unload,
|
|
.appear = prv_window_appear,
|
|
.disappear = prv_window_disappear
|
|
});
|
|
|
|
WindowStack *stack = app_state_get_window_stack();
|
|
|
|
// Switch to the app state to push a window
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
cl_check(stack);
|
|
cl_assert_equal_i(window_stack_count(stack), 0);
|
|
|
|
window_stack_push(stack, window1, true);
|
|
window_stack_push(stack, window2, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(stack), 2);
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 2);
|
|
cl_assert_equal_i(prv_get_appear_disappear_count(), 1);
|
|
|
|
window_stack_pop_all(stack, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(stack), 2);
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 2);
|
|
cl_assert_equal_i(prv_get_appear_disappear_count(), 2);
|
|
cl_assert_equal_i(s_disappear_count, 2);
|
|
cl_assert_equal_i(s_appear_count, 4);
|
|
|
|
window_stack_pop_all(stack, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(stack), 0);
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 0);
|
|
}
|
|
|
|
// Edge Case
|
|
// Description:
|
|
// During the unload handler of a window, we try to pop it.
|
|
void test_window_stack__pop_during_window_unload(void) {
|
|
Window *window = window_create();
|
|
|
|
window_set_window_handlers(window, &(WindowHandlers){
|
|
.load = prv_window_load,
|
|
.unload = prv_pop_window_unload,
|
|
.appear = prv_window_appear,
|
|
.disappear = prv_window_disappear
|
|
});
|
|
|
|
WindowStack *stack = app_state_get_window_stack();
|
|
|
|
// Switch to the app state to push a window
|
|
stub_pebble_tasks_set_current(PebbleTask_App);
|
|
|
|
cl_check(stack);
|
|
cl_assert_equal_i(window_stack_count(stack), 0);
|
|
|
|
window_stack_push(stack, window, true);
|
|
|
|
cl_assert_equal_b(animation_is_scheduled(fake_animation_get_first_animation()), true);
|
|
cl_assert_equal_i(window_stack_count(stack), 1);
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 1);
|
|
cl_assert_equal_i(prv_get_appear_disappear_count(), 1);
|
|
|
|
window_stack_remove(window, true);
|
|
|
|
cl_assert_equal_i(window_stack_count(stack), 0);
|
|
cl_assert_equal_i(prv_get_load_unload_count(), 0);
|
|
cl_assert_equal_i(prv_get_appear_disappear_count(), 0);
|
|
// FIXME: PBL-25460
|
|
// cl_assert_equal_b(animation_is_scheduled(fake_animation_get_first_animation()), false);
|
|
}
|
|
|
|
// Push two windows back to back, before the first transition completes. This should cancel the
|
|
// first transition and instead run the second transition.
|
|
void test_window_stack__double_animated_push(void) {
|
|
Window *window1 = window_create();
|
|
Window *window2 = window_create();
|
|
|
|
WindowStack *stack = app_state_get_window_stack();
|
|
|
|
window_stack_push(stack, window1, true);
|
|
Animation *first = fake_animation_get_first_animation();
|
|
|
|
cl_assert(animation_is_scheduled(first));
|
|
|
|
window_stack_push(stack, window2, true);
|
|
Animation *second = fake_animation_get_next_animation(first);
|
|
cl_assert(!animation_is_scheduled(first));
|
|
cl_assert(animation_is_scheduled(second));
|
|
}
|