/* * 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 "apps/system_apps/timeline/text_node.h" #include "clar.h" #include "pebble_asserts.h" // Stubs ///////////////////// #include "stubs_app_state.h" #include "stubs_heap.h" #include "stubs_passert.h" #include "stubs_pbl_malloc.h" #include "stubs_process_manager.h" // TODO: PBL-22271 Complete timeline text node unit tests // Fakes ///////////////////// typedef struct GTextNodeTestData { GContext gcontext; GRect clip_box; GTextNode *text_node; GSize max_used_size; } GTextNodeTestData; GTextNodeTestData s_data; #define TEST_TEXT "DUMMY TEXT" #define TEST_FONT ((void *)0xf0a7f0a7) #define TEST_TEXT_SIZE GSize(50, 18) #define TEST_TEXT_BOX GRect(10, 10, 140, 200) void graphics_draw_text(GContext *ctx, const char *text, GFont const font, const GRect box, const GTextOverflowMode overflow_mode, const GTextAlignment alignment, const GTextLayoutCacheRef layout_ref) { GTextNodeText *text_node = (GTextNodeText *)s_data.text_node; cl_assert(text_node); cl_assert_equal_s(text_node->text, text); cl_assert_equal_p(text_node->font, font); cl_assert_equal_p(text_node->overflow, overflow_mode); cl_assert_equal_p(text_node->alignment, alignment); TextLayoutExtended *layout = (TextLayoutExtended *)layout_ref; cl_assert_equal_p(text_node->line_spacing_delta, layout->line_spacing_delta); layout->max_used_size = s_data.max_used_size; } GSize graphics_text_layout_get_max_used_size(GContext *ctx, const char *text, GFont const font, const GRect box, const GTextOverflowMode overflow_mode, const GTextAlignment alignment, GTextLayoutCacheRef layout_ref) { GTextNodeText *text_node = (GTextNodeText *)s_data.text_node; cl_assert(text_node); cl_assert_equal_s(text_node->text, text); cl_assert_equal_p(text_node->font, font); cl_assert_equal_p(text_node->overflow, overflow_mode); cl_assert_equal_p(text_node->alignment, alignment); TextLayoutExtended *layout = (TextLayoutExtended *)layout_ref; cl_assert_equal_p(text_node->line_spacing_delta, layout->line_spacing_delta); layout->max_used_size = s_data.max_used_size; return layout->max_used_size; } // Setup and Teardown //////////////////////////////////// void test_graphics_text_node__initialize(void) { s_data = (GTextNodeTestData) {}; } void test_graphics_text_node__cleanup(void) { } // Tests //////////////////////////////////// void test_graphics_text_node__text_draw(void) { GContext *ctx = &s_data.gcontext; GTextNodeText text_node = {}; text_node.text = (char *)TEST_TEXT; text_node.font = TEST_FONT; text_node.overflow = GTextOverflowModeTrailingEllipsis; text_node.alignment = GTextAlignmentCenter; s_data.text_node = &text_node.node; s_data.max_used_size = TEST_TEXT_SIZE; GSize size; graphics_text_node_draw(&text_node.node, ctx, &TEST_TEXT_BOX, NULL, &size); cl_assert(size.w > 0); cl_assert(size.h > 0); cl_assert_equal_i(size.w, TEST_TEXT_SIZE.w); cl_assert_equal_i(size.h, TEST_TEXT_SIZE.h); } void test_graphics_text_node__text_size(void) { GContext *ctx = &s_data.gcontext; GTextNodeText text_node = {}; text_node.text = (char *)TEST_TEXT; text_node.font = TEST_FONT; text_node.overflow = GTextOverflowModeTrailingEllipsis; text_node.alignment = GTextAlignmentCenter; s_data.text_node = &text_node.node; s_data.max_used_size = TEST_TEXT_SIZE; GSize size; graphics_text_node_get_size(&text_node.node, ctx, &TEST_TEXT_BOX, NULL, &size); cl_assert(size.w > 0); cl_assert(size.h > 0); cl_assert_equal_i(size.w, TEST_TEXT_SIZE.w); cl_assert_equal_i(size.h, TEST_TEXT_SIZE.h); } static int s_num_draw_custom_calls = 0; static void prv_draw_custom(GContext *ctx, const GRect *box, const GTextNodeDrawConfig *config, bool render, GSize *size_out, void *user_data) { s_num_draw_custom_calls++; *size_out = TEST_TEXT_SIZE; } void test_graphics_text_node__custom_cached_size(void) { GContext *ctx = &s_data.gcontext; GTextNodeCustom custom_node = {}; custom_node.node.type = GTextNodeType_Custom; custom_node.callback = prv_draw_custom; GSize size; graphics_text_node_get_size(&custom_node.node, ctx, &TEST_TEXT_BOX, NULL, &size); cl_assert(size.w > 0); cl_assert(size.h > 0); cl_assert_equal_i(size.w, TEST_TEXT_SIZE.w); cl_assert_equal_i(size.h, TEST_TEXT_SIZE.h); cl_assert_equal_i(size.w, custom_node.node.cached_size.w); cl_assert_equal_i(size.h, custom_node.node.cached_size.h); cl_assert_equal_i(s_num_draw_custom_calls, 1); graphics_text_node_get_size(&custom_node.node, ctx, &TEST_TEXT_BOX, NULL, &size); cl_assert(size.w > 0); cl_assert(size.h > 0); cl_assert_equal_i(size.w, TEST_TEXT_SIZE.w); cl_assert_equal_i(size.h, TEST_TEXT_SIZE.h); cl_assert_equal_i(size.w, custom_node.node.cached_size.w); cl_assert_equal_i(size.h, custom_node.node.cached_size.h); cl_assert_equal_i(s_num_draw_custom_calls, 1); } void test_graphics_text_node__create_container_nodes_buffer(void) { GTextNodeHorizontal *h_empty = graphics_text_node_create_horizontal(0); cl_assert_equal_i(h_empty->container.max_nodes, 0); cl_assert_equal_p(NULL, h_empty->container.nodes); GTextNodeHorizontal *h_nodes = graphics_text_node_create_horizontal(3); cl_assert_equal_i(h_nodes->container.max_nodes, 3); cl_assert_equal_p(h_nodes + 1, (void *)h_nodes->container.nodes); GTextNodeVertical *v_empty = graphics_text_node_create_vertical(0); cl_assert_equal_i(v_empty->container.max_nodes, 0); cl_assert_equal_p(NULL, v_empty->container.nodes); GTextNodeVertical *v_nodes = graphics_text_node_create_vertical(3); cl_assert_equal_i(v_nodes->container.max_nodes, 3); cl_assert_equal_p(v_nodes + 1, (void *)v_nodes->container.nodes); } void test_graphics_text_node__destroy(void) { const char *str_a = "A"; GTextNodeText *text_a = graphics_text_node_create_text(strlen(str_a) + 1); cl_assert(text_a->node.free_on_destroy); strcpy((char *)text_a->text, str_a); const char *str_b = task_strdup("B"); GTextNodeText *text_b = graphics_text_node_create_text(0); cl_assert(text_b->node.free_on_destroy); text_b->text = (char *)str_b; const char *str_c = "C"; GTextNodeText text_c = { .text = (char *)str_c, }; cl_assert(!text_c.node.free_on_destroy); GTextNodeCustom *custom_a = graphics_text_node_create_custom(NULL, NULL); cl_assert(custom_a->node.free_on_destroy); GTextNodeHorizontal *horizontal_a = graphics_text_node_create_horizontal(3); cl_assert(horizontal_a->container.node.free_on_destroy); cl_assert_equal_i(horizontal_a->container.max_nodes, 3); cl_assert_equal_i(horizontal_a->container.num_nodes, 0); cl_assert(graphics_text_node_container_add_child(&horizontal_a->container, &text_a->node)); cl_assert(graphics_text_node_container_add_child(&horizontal_a->container, &text_b->node)); cl_assert(graphics_text_node_container_add_child(&horizontal_a->container, &text_c.node)); cl_assert(!graphics_text_node_container_add_child(&horizontal_a->container, &custom_a->node)); cl_assert_equal_i(horizontal_a->container.num_nodes, 3); GTextNodeVertical *vertical_a = graphics_text_node_create_vertical(2); cl_assert(vertical_a->container.node.free_on_destroy); cl_assert_equal_i(vertical_a->container.max_nodes, 2); cl_assert_equal_i(vertical_a->container.num_nodes, 0); cl_assert(graphics_text_node_container_add_child(&vertical_a->container, &horizontal_a->container.node)); cl_assert(graphics_text_node_container_add_child(&vertical_a->container, &custom_a->node)); cl_assert(!graphics_text_node_container_add_child(&vertical_a->container, &text_c.node)); cl_assert_equal_i(vertical_a->container.num_nodes, 2); graphics_text_node_destroy(&vertical_a->container.node); task_free((char *)str_b); } static void prv_draw_custom_clip(GContext *ctx, const GRect *box, const GTextNodeDrawConfig *config, bool render, GSize *size_out, void *user_data) { cl_assert_equal_grect(ctx->draw_state.clip_box, s_data.clip_box); } #define TEST_CLIP_BOX GRect(10, 20, 30, 40) void test_graphics_text_node__clip(void) { GContext *ctx = &s_data.gcontext; GTextNodeCustom custom_node = {}; custom_node.node.type = GTextNodeType_Custom; custom_node.callback = prv_draw_custom_clip; // Clipping off s_data.gcontext.draw_state.clip_box = DISP_FRAME; s_data.clip_box = DISP_FRAME; graphics_text_node_draw(&custom_node.node, ctx, &TEST_CLIP_BOX, NULL, NULL); cl_assert_equal_grect(ctx->draw_state.clip_box, DISP_FRAME); // Clipping on custom_node.node.clip = true; s_data.gcontext.draw_state.clip_box = DISP_FRAME; s_data.clip_box = TEST_CLIP_BOX; graphics_text_node_draw(&custom_node.node, ctx, &TEST_CLIP_BOX, NULL, NULL); cl_assert_equal_grect(ctx->draw_state.clip_box, DISP_FRAME); }