Files
panopainter/tests/assets/image_metadata_tests.cpp

114 lines
5.3 KiB
C++

#include "assets/image_metadata.h"
#include "test_harness.h"
#include <array>
#include <cstddef>
#include <string_view>
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<std::byte, 33>;
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<std::byte>((width >> 24U) & 0xffU);
bytes[17] = static_cast<std::byte>((width >> 16U) & 0xffU);
bytes[18] = static_cast<std::byte>((width >> 8U) & 0xffU);
bytes[19] = static_cast<std::byte>(width & 0xffU);
bytes[20] = static_cast<std::byte>((height >> 24U) & 0xffU);
bytes[21] = static_cast<std::byte>((height >> 16U) & 0xffU);
bytes[22] = static_cast<std::byte>((height >> 8U) & 0xffU);
bytes[23] = static_cast<std::byte>(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<std::byte, 8> 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();
}