pebble/tests/fw/applib/test_unobstructed_area_service.c
2025-01-27 11:38:16 -08:00

352 lines
13 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "clar.h"
#include "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));
}