/* * 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 "pebble_asserts.h" #include "applib/unobstructed_area_service.h" // Stubs ///////////////////// #include "stubs_app.h" #include "stubs_app_manager.h" #include "stubs_app_state.h" #include "stubs_events.h" #include "stubs_framebuffer.h" #include "stubs_graphics.h" #include "stubs_logging.h" #include "stubs_passert.h" #include "stubs_pbl_malloc.h" #include "stubs_pebble_tasks.h" #include "stubs_process_manager.h" #include "stubs_ui_window.h" // Fakes ///////////////////// #include "fake_event_service.h" // Statics ///////////////////// void *s_context_target; typedef struct UnobstructedAreaTestData { void *context; int num_will_change_calls; int num_change_calls; int num_did_change_calls; GRect last_will_change_final_area; AnimationProgress last_change_progress; } UnobstructedAreaTestData; UnobstructedAreaTestData s_data; static void prv_will_change(GRect final_area, void *context) { s_data.last_will_change_final_area = final_area; cl_assert_equal_p(context, s_data.context); s_data.num_will_change_calls++; } static void prv_change(AnimationProgress progress, void *context) { s_data.last_change_progress = progress; cl_assert_equal_p(context, s_data.context); s_data.num_change_calls++; } static void prv_did_change(void *context) { cl_assert_equal_p(context, s_data.context); s_data.num_did_change_calls++; } // Test boilerplate ///////////////////// void test_unobstructed_area_service__initialize(void) { s_data = (UnobstructedAreaTestData) { .context = &s_context_target, .last_change_progress = -1, }; fake_event_service_init(); s_app_state_framebuffer = &(FrameBuffer) { .size = DISP_FRAME.size }; unobstructed_area_service_init(app_state_get_unobstructed_area_state(), DISP_ROWS); } void test_unobstructed_area_service__cleanup(void) { app_unobstructed_area_service_unsubscribe(); unobstructed_area_service_deinit(app_state_get_unobstructed_area_state()); } // Tests ////////////////////// void test_unobstructed_area_service__subscribe(void) { // Unsubscribing first should not crash app_unobstructed_area_service_unsubscribe(); cl_assert(!app_state_get_unobstructed_area_state()->handlers.will_change); cl_assert(!app_state_get_unobstructed_area_state()->handlers.change); cl_assert(!app_state_get_unobstructed_area_state()->handlers.did_change); // Subscribing should use the event service UnobstructedAreaHandlers handlers = { .will_change = prv_will_change, .change = prv_change, .did_change = prv_did_change, }; app_unobstructed_area_service_subscribe(handlers, s_data.context); cl_assert(fake_event_service_get_info(PEBBLE_UNOBSTRUCTED_AREA_EVENT)->handler); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.will_change, prv_will_change); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.change, prv_change); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.did_change, prv_did_change); // Unsubscribing after subscription should cancel subscriptions app_unobstructed_area_service_unsubscribe(); cl_assert(!app_state_get_unobstructed_area_state()->handlers.will_change); cl_assert(!app_state_get_unobstructed_area_state()->handlers.change); cl_assert(!app_state_get_unobstructed_area_state()->handlers.did_change); // Unsubscribing again should not crash app_unobstructed_area_service_unsubscribe(); cl_assert(!app_state_get_unobstructed_area_state()->handlers.will_change); cl_assert(!app_state_get_unobstructed_area_state()->handlers.change); cl_assert(!app_state_get_unobstructed_area_state()->handlers.did_change); } void test_unobstructed_area_service__will_change(void) { UnobstructedAreaHandlers handlers = { .will_change = prv_will_change, }; app_unobstructed_area_service_subscribe(handlers, s_data.context); cl_assert(fake_event_service_get_info(PEBBLE_UNOBSTRUCTED_AREA_EVENT)->handler); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.will_change, prv_will_change); const GRect from_area = GRect(0, 0, DISP_COLS, 400); const GRect to_area = GRect(0, 0, DISP_COLS, 200); unobstructed_area_service_will_change(from_area.size.h, to_area.size.h); fake_event_service_handle_last(); cl_assert_equal_i(s_data.num_will_change_calls, 1); const GRect to_area_expected = GRect(0, 0, DISP_COLS, MIN(200, DISP_ROWS)); cl_assert_equal_grect(s_data.last_will_change_final_area, to_area_expected); } void test_unobstructed_area_service__will_change_twice(void) { UnobstructedAreaHandlers handlers = { .will_change = prv_will_change, }; app_unobstructed_area_service_subscribe(handlers, s_data.context); cl_assert(fake_event_service_get_info(PEBBLE_UNOBSTRUCTED_AREA_EVENT)->handler); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.will_change, prv_will_change); const GRect from_area = GRect(0, 0, DISP_COLS, 400); const GRect to_area = GRect(0, 0, DISP_COLS, 200); unobstructed_area_service_will_change(from_area.size.h, to_area.size.h); fake_event_service_handle_last(); unobstructed_area_service_will_change(from_area.size.h, to_area.size.h); cl_assert_passert(fake_event_service_handle_last()); } void test_unobstructed_area_service__change(void) { UnobstructedAreaHandlers handlers = { .change = prv_change, }; app_unobstructed_area_service_subscribe(handlers, s_data.context); cl_assert(fake_event_service_get_info(PEBBLE_UNOBSTRUCTED_AREA_EVENT)->handler); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.change, prv_change); const GRect from_area = GRect(0, 0, DISP_COLS, 400); const GRect to_area = GRect(0, 0, DISP_COLS, 200); unobstructed_area_service_will_change(from_area.size.h, to_area.size.h); fake_event_service_handle_last(); const GRect area = GRect(0, 0, DISP_COLS, 200); const AnimationProgress progress = ANIMATION_NORMALIZED_MAX / 2; unobstructed_area_service_change(area.size.h, to_area.size.h, progress); fake_event_service_handle_last(); cl_assert_equal_i(s_data.num_change_calls, 1); cl_assert_equal_i(s_data.last_change_progress, progress); } void test_unobstructed_area_service__change_after_subscribe(void) { const GRect from_area = GRect(0, 0, DISP_COLS, 400); const GRect to_area = GRect(0, 0, DISP_COLS, 200); unobstructed_area_service_will_change(from_area.size.h, to_area.size.h); UnobstructedAreaHandlers handlers = { .will_change = prv_will_change, .change = prv_change, }; app_unobstructed_area_service_subscribe(handlers, s_data.context); cl_assert(fake_event_service_get_info(PEBBLE_UNOBSTRUCTED_AREA_EVENT)->handler); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.change, prv_change); const GRect area = GRect(0, 0, DISP_COLS, 200); const AnimationProgress progress = ANIMATION_NORMALIZED_MAX / 2; unobstructed_area_service_change(area.size.h, to_area.size.h, progress); fake_event_service_handle_last(); cl_assert_equal_i(s_data.num_will_change_calls, 1); cl_assert_equal_i(s_data.num_change_calls, 1); cl_assert_equal_i(s_data.last_change_progress, progress); } void test_unobstructed_area_service__change_no_will(void) { UnobstructedAreaHandlers handlers = { .will_change = prv_will_change, .change = prv_change, }; app_unobstructed_area_service_subscribe(handlers, s_data.context); cl_assert(fake_event_service_get_info(PEBBLE_UNOBSTRUCTED_AREA_EVENT)->handler); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.change, prv_change); const GRect to_area = GRect(0, 0, DISP_COLS, 200); const GRect area = GRect(0, 0, DISP_COLS, 200); const AnimationProgress progress = ANIMATION_NORMALIZED_MAX / 2; unobstructed_area_service_change(area.size.h, to_area.size.h, progress); fake_event_service_handle_last(); cl_assert_equal_i(s_data.num_will_change_calls, 1); cl_assert_equal_i(s_data.num_change_calls, 1); cl_assert_equal_i(s_data.last_change_progress, progress); } void test_unobstructed_area_service__did_change(void) { UnobstructedAreaHandlers handlers = { .did_change = prv_did_change, }; app_unobstructed_area_service_subscribe(handlers, s_data.context); cl_assert(fake_event_service_get_info(PEBBLE_UNOBSTRUCTED_AREA_EVENT)->handler); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.did_change, prv_did_change); const GRect from_area = GRect(0, 0, DISP_COLS, 400); const GRect to_area = GRect(0, 0, DISP_COLS, 200); unobstructed_area_service_will_change(from_area.size.h, to_area.size.h); fake_event_service_handle_last(); unobstructed_area_service_did_change(to_area.size.h); fake_event_service_handle_last(); cl_assert_equal_i(s_data.num_did_change_calls, 1); } void test_unobstructed_area_service__did_change_after_subscribe(void) { const GRect from_area = GRect(0, 0, DISP_COLS, 400); const GRect to_area = GRect(0, 0, DISP_COLS, 200); unobstructed_area_service_will_change(from_area.size.h, to_area.size.h); UnobstructedAreaHandlers handlers = { .will_change = prv_will_change, .did_change = prv_did_change, }; app_unobstructed_area_service_subscribe(handlers, s_data.context); cl_assert(fake_event_service_get_info(PEBBLE_UNOBSTRUCTED_AREA_EVENT)->handler); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.did_change, prv_did_change); unobstructed_area_service_did_change(to_area.size.h); fake_event_service_handle_last(); cl_assert_equal_i(s_data.num_will_change_calls, 1); cl_assert_equal_i(s_data.num_did_change_calls, 1); } void test_unobstructed_area_service__did_change_no_will(void) { UnobstructedAreaHandlers handlers = { .will_change = prv_will_change, .did_change = prv_did_change, }; app_unobstructed_area_service_subscribe(handlers, s_data.context); cl_assert(fake_event_service_get_info(PEBBLE_UNOBSTRUCTED_AREA_EVENT)->handler); cl_assert_equal_p(app_state_get_unobstructed_area_state()->handlers.did_change, prv_did_change); const GRect to_area = GRect(0, 0, DISP_COLS, 200); unobstructed_area_service_did_change(to_area.size.h); fake_event_service_handle_last(); cl_assert_equal_i(s_data.num_will_change_calls, 1); cl_assert_equal_i(s_data.num_did_change_calls, 1); } void test_unobstructed_area_service__layer_no_clip(void) { app_state_get_unobstructed_area_state()->area = GRect(0, 0, 400, 400); Layer root_layer = { .bounds = GRect(100, 100, 200, 200), }; GRect unobstructed_bounds; layer_get_unobstructed_bounds(&root_layer, &unobstructed_bounds); cl_assert_equal_grect(unobstructed_bounds, root_layer.bounds); } void test_unobstructed_area_service__layer_clip_x_y(void) { app_state_get_unobstructed_area_state()->area = GRect(0, 0, 400, 400); Layer root_layer = { .bounds = GRect(210, 220, 300, 300), }; GRect unobstructed_bounds; layer_get_unobstructed_bounds(&root_layer, &unobstructed_bounds); cl_assert_equal_grect(unobstructed_bounds, GRect(210, 220, 190, 180)); } void test_unobstructed_area_service__layer_clip_nx_ny(void) { app_state_get_unobstructed_area_state()->area = GRect(0, 0, 400, 400); Layer root_layer = { .bounds = GRect(-110, -120, 300, 300), }; GRect unobstructed_bounds; layer_get_unobstructed_bounds(&root_layer, &unobstructed_bounds); cl_assert_equal_grect(unobstructed_bounds, GRect(0, 0, 190, 180)); } void test_unobstructed_area_service__nested_layer_no_clip(void) { app_state_get_unobstructed_area_state()->area = GRect(0, 0, 400, 400); Layer root_layer = { .bounds = GRect(30, 30, 30, 30), }; Layer layer = { .bounds = GRect(20, 20, 20, 20), }; layer_add_child(&root_layer, &layer); GRect unobstructed_bounds; layer_get_unobstructed_bounds(&layer, &unobstructed_bounds); cl_assert_equal_grect(unobstructed_bounds, layer.bounds); } void test_unobstructed_area_service__nested_layer_clip_x_y(void) { app_state_get_unobstructed_area_state()->area = GRect(0, 0, 400, 400); Layer root_layer = { .bounds = GRect(150, 120, 10, 10), // The size of the parent layer has no affect }; Layer layer = { .bounds = GRect(110, 130, 300, 200), }; layer_add_child(&root_layer, &layer); GRect unobstructed_bounds; layer_get_unobstructed_bounds(&layer, &unobstructed_bounds); cl_assert_equal_grect(unobstructed_bounds, GRect(110, 130, 140, 150)); } void test_unobstructed_area_service__nested_layer_clip_nx_ny(void) { app_state_get_unobstructed_area_state()->area = GRect(0, 0, 400, 400); Layer root_layer = { .bounds = GRect(-150, -120, 10, 10), // The size of the parent layer has no affect }; Layer layer = { .bounds = GRect(-110, -130, 300, 290), }; layer_add_child(&root_layer, &layer); GRect unobstructed_bounds; layer_get_unobstructed_bounds(&layer, &unobstructed_bounds); cl_assert_equal_grect(unobstructed_bounds, GRect(150, 120, 40, 40)); }