/* * 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/graphics/gtypes.h" #include "applib/graphics/gbitmap_pbi.h" #include "stubs_app_state.h" #include "stubs_graphics_context.h" #include "stubs_logging.h" #include "stubs_process_manager.h" #include "stubs_passert.h" // Stubs /////////////////////// bool gbitmap_init_with_png_data(GBitmap *bitmap, const uint8_t *data, size_t data_size) { return false; } bool gbitmap_png_data_is_png(const uint8_t *data, size_t data_size) { return false; } ResAppNum sys_get_current_resource_num(void) { return 0; } const uint8_t *sys_resource_read_only_bytes(ResAppNum app_num, uint32_t resource_id, size_t *num_bytes_out) { return NULL; } // Fakes /////////////////////// size_t s_resource_size; size_t sys_resource_size(ResAppNum app_num, uint32_t resource_id) { return s_resource_size; } typedef struct { uint16_t row_size_bytes; union { uint16_t info_flags; BitmapInfo info; }; uint16_t width; uint16_t height; } FakeBitmapData; FakeBitmapData s_fake_bitmap_data; size_t sys_resource_load_range( ResAppNum app_num, uint32_t id, uint32_t start_offset, uint8_t *data, size_t num_bytes) { BitmapData *bitmap = (BitmapData*) data; *bitmap = (BitmapData) { .row_size_bytes = s_fake_bitmap_data.row_size_bytes, .info_flags = s_fake_bitmap_data.info_flags, .width = s_fake_bitmap_data.width, .height = s_fake_bitmap_data.height }; return num_bytes; } // Tests /////////////////////// void test_gbitmap_resource_validation__initialize(void) { s_resource_size = 0; s_fake_bitmap_data = (FakeBitmapData) { 0 }; } static uint32_t prv_calculate_size(FakeBitmapData *bitmap) { const uint32_t required_size_bytes = offsetof(BitmapData, data) + // header size (bitmap->row_size_bytes * bitmap->height) + // pixel data gbitmap_get_palette_size(bitmap->info.format); // palette data return required_size_bytes; } void test_gbitmap_resource_validation__total_size(void) { s_fake_bitmap_data = (FakeBitmapData) { .row_size_bytes = 8, .info.format = GBitmapFormat8Bit, .info.version = GBITMAP_VERSION_1, .width = 8, .height = 1 }; // Set the resource size to be valid. s_resource_size = prv_calculate_size(&s_fake_bitmap_data); // We should load it successfully GBitmap bitmap; cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0)); // However, if we corrupt the row_size_bytes field we should fail. s_fake_bitmap_data.row_size_bytes = 12; cl_assert(!gbitmap_init_with_resource_system(&bitmap, 0, 0)); // Corrupt it the other way, so that there's not enough data s_fake_bitmap_data.row_size_bytes = 4; cl_assert(!gbitmap_init_with_resource_system(&bitmap, 0, 0)); // Fix it up again s_fake_bitmap_data.row_size_bytes = 8; cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0)); // But now change the palette format to something that requires more space and watch it fail s_fake_bitmap_data.info.format = GBitmapFormat4BitPalette; cl_assert(!gbitmap_init_with_resource_system(&bitmap, 0, 0)); // But if we have space for the palette, it should pass s_resource_size = prv_calculate_size(&s_fake_bitmap_data); cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0)); } void test_gbitmap_resource_validation__row_size(void) { s_fake_bitmap_data = (FakeBitmapData) { .row_size_bytes = 8, .info.format = GBitmapFormat8Bit, .info.version = GBITMAP_VERSION_1, .width = 8, .height = 1 }; // Set the resource size to be valid. s_resource_size = prv_calculate_size(&s_fake_bitmap_data); // We should load it successfully GBitmap bitmap; cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0)); // Vary the width without changing the height to be too large s_fake_bitmap_data.width = 10, cl_assert(!gbitmap_init_with_resource_system(&bitmap, 0, 0)); // Too small is fine though s_fake_bitmap_data.width = 6, cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0)); // Test with an uneven number of bits and make sure we're rounding correctly s_fake_bitmap_data.info.format = GBitmapFormat1Bit; s_fake_bitmap_data.width = 64; cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0)); s_fake_bitmap_data.info.format = GBitmapFormat1Bit; s_fake_bitmap_data.width = 65; cl_assert(!gbitmap_init_with_resource_system(&bitmap, 0, 0)); s_fake_bitmap_data.info.format = GBitmapFormat1Bit; s_fake_bitmap_data.width = 63; cl_assert(gbitmap_init_with_resource_system(&bitmap, 0, 0)); }