/* * 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 "applib/ui/content_indicator.h" #include "applib/ui/content_indicator_private.h" #include "util/buffer.h" // Fakes //////////////////////////////////// #include "fake_app_timer.h" #include "fake_content_indicator.h" // Stubs //////////////////////////////////// #include "stubs_app_state.h" #include "stubs_compiled_with_legacy2_sdk.h" #include "stubs_gpath.h" #include "stubs_graphics.h" #include "stubs_graphics_context.h" #include "stubs_heap.h" #include "stubs_logging.h" #include "stubs_passert.h" #include "stubs_pbl_malloc.h" #include "stubs_pebble_tasks.h" #include "stubs_ui_window.h" #include "stubs_unobstructed_area.h" extern void prv_content_indicator_update_proc(Layer *layer, GContext *ctx); // Helpers //////////////////////////////////// static Layer s_content_indicator_dummy_layer; static LayerUpdateProc s_content_indicator_dummy_layer_update_proc; ContentIndicatorConfig helper_get_dummy_config(void) { return (ContentIndicatorConfig) { .layer = &s_content_indicator_dummy_layer, .times_out = false, .alignment = GAlignLeft, .colors = { .foreground = GColorGreen, .background = GColorRed } }; } void helper_check_buffer_for_content_indicator(size_t index, ContentIndicator *content_indicator) { ContentIndicatorsBuffer *content_indicators_buffer = content_indicator_get_current_buffer(); Buffer *buffer = &content_indicators_buffer->buffer; ContentIndicator **content_indicators = (ContentIndicator**)buffer->data; cl_assert_equal_p(content_indicators[index], content_indicator); } void helper_check_configs_for_equality(ContentIndicatorConfig a, ContentIndicatorConfig b) { cl_assert_equal_p(a.layer, b.layer); cl_assert_equal_b(a.times_out, b.times_out); cl_assert(a.alignment == b.alignment); cl_assert(a.colors.foreground.argb == b.colors.foreground.argb); cl_assert(a.colors.background.argb == b.colors.background.argb); } // Setup ///////////////////////////////// void test_content_indicator__initialize(void) { // Initialize the static buffer of content indicators content_indicator_init_buffer(content_indicator_get_current_buffer()); // Reset the dummy layer's fields memset(&s_content_indicator_dummy_layer, 0, sizeof(Layer)); } void test_content_indicator__cleanup(void) { } // Tests //////////////////////////////////// void test_content_indicator__create_should_add_to_buffer(void) { ContentIndicator *content_indicator; for (size_t i = 0; i < CONTENT_INDICATOR_BUFFER_SIZE; i++) { content_indicator = content_indicator_create(); cl_assert(content_indicator); helper_check_buffer_for_content_indicator(i, content_indicator); } // Creating more content indicators than the buffer can hold should return NULL cl_assert_equal_p(content_indicator_create(), NULL); } void test_content_indicator__init_should_add_to_buffer(void) { ContentIndicator content_indicator; for (size_t i = 0; i < CONTENT_INDICATOR_BUFFER_SIZE; i++) { content_indicator_init(&content_indicator); helper_check_buffer_for_content_indicator(i, &content_indicator); } // Initializing more content indicators than the buffer can hold should assert cl_assert_passert(content_indicator_init(&content_indicator)); } void test_content_indicator__deinit_should_remove_from_buffer(void) { ContentIndicatorsBuffer *content_indicators_buffer = content_indicator_get_current_buffer(); Buffer *buffer = &content_indicators_buffer->buffer; ContentIndicator content_indicator; size_t bytes_written = 0; for (size_t i = 0; i < CONTENT_INDICATOR_BUFFER_SIZE; i++) { content_indicator_init(&content_indicator); bytes_written += sizeof(ContentIndicator *); cl_assert_equal_i(buffer->bytes_written, bytes_written); } for (size_t i = 0; i < CONTENT_INDICATOR_BUFFER_SIZE; i++) { content_indicator_deinit(&content_indicator); bytes_written -= sizeof(ContentIndicator *); cl_assert_equal_i(buffer->bytes_written, bytes_written); } } void test_content_indicator__configuring_should_configure(void) { ContentIndicator content_indicator; content_indicator_init(&content_indicator); ContentIndicatorDirectionData *direction_data = content_indicator.direction_data; // Test setting a dummy configuration for a direction const ContentIndicatorConfig dummy_config = helper_get_dummy_config(); const ContentIndicatorDirection direction = ContentIndicatorDirectionUp; dummy_config.layer->update_proc = s_content_indicator_dummy_layer_update_proc; cl_assert(content_indicator_configure_direction(&content_indicator, direction, &dummy_config)); helper_check_configs_for_equality(dummy_config, direction_data[direction].config); // Should save a reference to the config layer's update proc cl_assert_equal_p(dummy_config.layer->update_proc, direction_data->original_update_proc); cl_assert_equal_p(direction_data->original_update_proc, s_content_indicator_dummy_layer_update_proc); } void test_content_indicator__configuring_different_directions_with_same_layer_should_fail(void) { ContentIndicator content_indicator; content_indicator_init(&content_indicator); // Setting a dummy configuration for a direction should return true const ContentIndicatorConfig dummy_config = helper_get_dummy_config(); dummy_config.layer->update_proc = s_content_indicator_dummy_layer_update_proc; cl_assert(content_indicator_configure_direction(&content_indicator, ContentIndicatorDirectionUp, &dummy_config)); // Using the same dummy configuration (which has the same layer) to configure a different // direction should fail cl_assert(!content_indicator_configure_direction(&content_indicator, ContentIndicatorDirectionDown, &dummy_config)); } void test_content_indicator__setting_content_available_should_update_layer_update_proc(void) { ContentIndicator content_indicator; content_indicator_init(&content_indicator); ContentIndicatorDirectionData *direction_data = content_indicator.direction_data; const ContentIndicatorConfig dummy_config = helper_get_dummy_config(); const ContentIndicatorDirection direction = ContentIndicatorDirectionUp; dummy_config.layer->update_proc = s_content_indicator_dummy_layer_update_proc; cl_assert(content_indicator_configure_direction(&content_indicator, direction, &dummy_config)); cl_assert_equal_p(dummy_config.layer->update_proc, direction_data->original_update_proc); cl_assert_equal_p(direction_data->original_update_proc, s_content_indicator_dummy_layer_update_proc); // Setting content available should switch the layer's update proc to draw an arrow content_indicator_set_content_available(&content_indicator, direction, true); cl_assert_equal_p(dummy_config.layer->update_proc, prv_content_indicator_update_proc); // Setting content unavailable should revert the layer's update proc content_indicator_set_content_available(&content_indicator, direction, false); cl_assert_equal_p(dummy_config.layer->update_proc, direction_data->original_update_proc); cl_assert_equal_p(direction_data->original_update_proc, s_content_indicator_dummy_layer_update_proc); } void test_content_indicator__creating_for_scroll_layer(void) { ScrollLayer scroll_layer; ContentIndicator *content_indicator = content_indicator_get_or_create_for_scroll_layer( &scroll_layer); cl_assert(content_indicator); // Should save a reference to the scroll layer cl_assert_equal_p(content_indicator->scroll_layer, &scroll_layer); // Should retrieve the same content indicator with the same scroll layer ContentIndicator *content_indicator2 = content_indicator_get_or_create_for_scroll_layer( &scroll_layer); cl_assert(content_indicator2); // Should save a reference to the scroll layer cl_assert_equal_p(content_indicator2->scroll_layer, &scroll_layer); cl_assert_equal_p(content_indicator2, content_indicator); // Should retrieve a different content indicator for a different scroll layer ScrollLayer scroll_layer2; ContentIndicator *content_indicator3 = content_indicator_get_or_create_for_scroll_layer( &scroll_layer2); cl_assert(content_indicator3); // Should save a reference to the scroll layer cl_assert_equal_p(content_indicator3->scroll_layer, &scroll_layer2); cl_assert(content_indicator3 != content_indicator); } void test_content_indicator__should_only_be_created_for_scroll_layer_upon_client_access(void) { ContentIndicatorsBuffer *content_indicators_buffer = content_indicator_get_current_buffer(); Buffer *buffer = &content_indicators_buffer->buffer; // At the start of the test, the buffer should be empty cl_assert(buffer_is_empty(buffer)); ScrollLayer scroll_layer; // Trying to access the ContentIndicator for this ScrollLayer should return NULL because we // haven't tried to access it as the client yet cl_assert_equal_p(content_indicator_get_for_scroll_layer(&scroll_layer), NULL); // And the buffer should still be empty cl_assert(buffer_is_empty(buffer)); // Now we try to access it as the client, which should actually create the ContentIndicator ContentIndicator *content_indicator = content_indicator_get_or_create_for_scroll_layer( &scroll_layer); cl_assert(content_indicator); // The ContentIndicator should have a reference to the ScrollLayer cl_assert_equal_p(content_indicator->scroll_layer, &scroll_layer); // The buffer should now hold the newly created ContentIndicator cl_assert(buffer->bytes_written == sizeof(ContentIndicator *)); // Finally, calling content_indicator_get_for_scroll_layer() again should return the same // ContentIndicator ContentIndicator *content_indicator2 = content_indicator_get_for_scroll_layer(&scroll_layer); cl_assert(content_indicator2); cl_assert_equal_p(content_indicator2, content_indicator); // The buffer should still only hold the single ContentIndicator cl_assert(buffer->bytes_written == sizeof(ContentIndicator *)); } void test_content_indicator__pass_null_config_to_reset_direction_data(void) { ContentIndicator content_indicator; content_indicator_init(&content_indicator); ContentIndicatorDirectionData *direction_data = content_indicator.direction_data; const ContentIndicatorConfig dummy_config = helper_get_dummy_config(); const ContentIndicatorDirection direction = ContentIndicatorDirectionUp; dummy_config.layer->update_proc = s_content_indicator_dummy_layer_update_proc; cl_assert(content_indicator_configure_direction(&content_indicator, direction, &dummy_config)); cl_assert_equal_p(dummy_config.layer->update_proc, direction_data->original_update_proc); cl_assert_equal_p(direction_data->original_update_proc, s_content_indicator_dummy_layer_update_proc); // Setting content available should switch the layer's update proc content_indicator_set_content_available(&content_indicator, direction, true); cl_assert_equal_p(dummy_config.layer->update_proc, prv_content_indicator_update_proc); // Direction data should be emptied and layer's update proc should return to original when NULL // config is passed cl_assert(content_indicator_configure_direction(&content_indicator, direction, NULL)); cl_assert_equal_p(dummy_config.layer->update_proc, s_content_indicator_dummy_layer_update_proc); cl_assert(!direction_data[direction].config.layer); // Setting content available should not change layer update proc without reconfiguring. content_indicator_set_content_available(&content_indicator, direction, true); cl_assert_equal_p(dummy_config.layer->update_proc, s_content_indicator_dummy_layer_update_proc); } void test_content_indicator__re_configure_direction(void) { ContentIndicator content_indicator; content_indicator_init(&content_indicator); const ContentIndicatorConfig dummy_config = helper_get_dummy_config(); const ContentIndicatorDirection up = ContentIndicatorDirectionUp; const ContentIndicatorDirection down = ContentIndicatorDirectionDown; cl_assert(content_indicator_configure_direction(&content_indicator, up, &dummy_config)); // re-configure with the same direction, should be a success cl_assert(content_indicator_configure_direction(&content_indicator, up, &dummy_config)); // re-configure with a different same direction, should fail cl_assert(!content_indicator_configure_direction(&content_indicator, down, &dummy_config)); }