#include "assets/image_metadata.h" #include "test_harness.h" #include #include #include using pp::assets::ImageColorType; using pp::assets::image_color_type_name; using pp::assets::max_image_dimension; using pp::assets::parse_png_metadata; using pp::foundation::StatusCode; namespace { using PngHeader = std::array; PngHeader make_png_header(std::uint32_t width, std::uint32_t height, std::byte bit_depth, std::byte color_type) { PngHeader bytes { std::byte { 0x89 }, std::byte { 0x50 }, std::byte { 0x4e }, std::byte { 0x47 }, std::byte { 0x0d }, std::byte { 0x0a }, std::byte { 0x1a }, std::byte { 0x0a }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x0d }, std::byte { 'I' }, std::byte { 'H' }, std::byte { 'D' }, std::byte { 'R' }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, bit_depth, color_type, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, }; bytes[16] = static_cast((width >> 24U) & 0xffU); bytes[17] = static_cast((width >> 16U) & 0xffU); bytes[18] = static_cast((width >> 8U) & 0xffU); bytes[19] = static_cast(width & 0xffU); bytes[20] = static_cast((height >> 24U) & 0xffU); bytes[21] = static_cast((height >> 16U) & 0xffU); bytes[22] = static_cast((height >> 8U) & 0xffU); bytes[23] = static_cast(height & 0xffU); return bytes; } void parses_png_ihdr_metadata(pp::tests::Harness& h) { const auto rgba = make_png_header(320, 240, std::byte { 8 }, std::byte { 6 }); const auto rgb = make_png_header(17, 9, std::byte { 8 }, std::byte { 2 }); const auto rgba_result = parse_png_metadata(rgba); const auto rgb_result = parse_png_metadata(rgb); PP_EXPECT(h, rgba_result.ok()); PP_EXPECT(h, rgba_result.value().width == 320U); PP_EXPECT(h, rgba_result.value().height == 240U); PP_EXPECT(h, rgba_result.value().bit_depth == 8U); PP_EXPECT(h, rgba_result.value().components == 4U); PP_EXPECT(h, rgba_result.value().color_type == ImageColorType::rgba); PP_EXPECT(h, image_color_type_name(rgba_result.value().color_type) == std::string_view("rgba")); PP_EXPECT(h, rgb_result.ok()); PP_EXPECT(h, rgb_result.value().components == 3U); PP_EXPECT(h, rgb_result.value().color_type == ImageColorType::rgb); } void maps_png_color_type_components(pp::tests::Harness& h) { const auto grayscale = parse_png_metadata(make_png_header(1, 1, std::byte { 8 }, std::byte { 0 })); const auto indexed = parse_png_metadata(make_png_header(1, 1, std::byte { 8 }, std::byte { 3 })); const auto gray_alpha = parse_png_metadata(make_png_header(1, 1, std::byte { 8 }, std::byte { 4 })); PP_EXPECT(h, grayscale.ok()); PP_EXPECT(h, grayscale.value().components == 1U); PP_EXPECT(h, grayscale.value().color_type == ImageColorType::grayscale); PP_EXPECT(h, indexed.ok()); PP_EXPECT(h, indexed.value().components == 1U); PP_EXPECT(h, indexed.value().color_type == ImageColorType::indexed); PP_EXPECT(h, gray_alpha.ok()); PP_EXPECT(h, gray_alpha.value().components == 2U); PP_EXPECT(h, gray_alpha.value().color_type == ImageColorType::grayscale_alpha); } void rejects_corrupt_or_extreme_png_metadata(pp::tests::Harness& h) { const std::array truncated { std::byte { 0x89 }, std::byte { 0x50 }, std::byte { 0x4e }, std::byte { 0x47 }, std::byte { 0x0d }, std::byte { 0x0a }, std::byte { 0x1a }, std::byte { 0x0a }, }; auto bad_signature = make_png_header(1, 1, std::byte { 8 }, std::byte { 6 }); bad_signature[0] = std::byte { 0x00 }; auto bad_ihdr = make_png_header(1, 1, std::byte { 8 }, std::byte { 6 }); bad_ihdr[15] = std::byte { 'X' }; const auto zero_width = make_png_header(0, 1, std::byte { 8 }, std::byte { 6 }); const auto too_large = make_png_header(max_image_dimension + 1U, 1, std::byte { 8 }, std::byte { 6 }); const auto bad_depth = make_png_header(1, 1, std::byte { 0 }, std::byte { 6 }); const auto bad_color = make_png_header(1, 1, std::byte { 8 }, std::byte { 5 }); PP_EXPECT(h, parse_png_metadata(truncated).status().code == StatusCode::out_of_range); PP_EXPECT(h, parse_png_metadata(bad_signature).status().code == StatusCode::invalid_argument); PP_EXPECT(h, parse_png_metadata(bad_ihdr).status().code == StatusCode::invalid_argument); PP_EXPECT(h, parse_png_metadata(zero_width).status().code == StatusCode::out_of_range); PP_EXPECT(h, parse_png_metadata(too_large).status().code == StatusCode::out_of_range); PP_EXPECT(h, parse_png_metadata(bad_depth).status().code == StatusCode::invalid_argument); PP_EXPECT(h, parse_png_metadata(bad_color).status().code == StatusCode::invalid_argument); } } int main() { pp::tests::Harness harness; harness.run("parses_png_ihdr_metadata", parses_png_ihdr_metadata); harness.run("maps_png_color_type_components", maps_png_color_type_components); harness.run("rejects_corrupt_or_extreme_png_metadata", rejects_corrupt_or_extreme_png_metadata); return harness.finish(); }