#include "assets/ppi_header.h" #include "test_harness.h" #include #include #include #include using pp::assets::parse_ppi_header; using pp::assets::parse_ppi_project_layout; using pp::assets::ppi_header_size; using pp::assets::ppi_thumbnail_byte_size; using pp::foundation::StatusCode; namespace { void append_u32(std::vector& bytes, std::uint32_t value) { bytes.push_back(static_cast(value & 0xffU)); bytes.push_back(static_cast((value >> 8U) & 0xffU)); bytes.push_back(static_cast((value >> 16U) & 0xffU)); bytes.push_back(static_cast((value >> 24U) & 0xffU)); } std::vector valid_header() { std::vector bytes { std::byte { 'P' }, std::byte { 'P' }, std::byte { 'I' }, std::byte { 0 }, }; append_u32(bytes, 0); append_u32(bytes, 4); append_u32(bytes, 0); append_u32(bytes, 2); append_u32(bytes, 3); append_u32(bytes, 1024); append_u32(bytes, 128); append_u32(bytes, 128); append_u32(bytes, 4); return bytes; } void parses_legacy_ppi_header(pp::tests::Harness& h) { const auto bytes = valid_header(); const auto header = parse_ppi_header(bytes); PP_EXPECT(h, bytes.size() == ppi_header_size); PP_EXPECT(h, header.ok()); PP_EXPECT(h, header.value().document_version.major == 0U); PP_EXPECT(h, header.value().document_version.minor == 4U); PP_EXPECT(h, header.value().software_version.fix == 3U); PP_EXPECT(h, header.value().software_version.build == 1024U); PP_EXPECT(h, header.value().thumbnail.width == 128U); PP_EXPECT(h, header.value().thumbnail.height == 128U); PP_EXPECT(h, header.value().thumbnail.components == 4U); } void rejects_truncated_invalid_magic_and_bad_thumbnail(pp::tests::Harness& h) { auto truncated = valid_header(); truncated.pop_back(); auto bad_magic = valid_header(); bad_magic[0] = std::byte { 'X' }; auto bad_thumb = valid_header(); bad_thumb[32] = std::byte { 64 }; const auto truncated_result = parse_ppi_header(truncated); const auto magic_result = parse_ppi_header(bad_magic); const auto thumb_result = parse_ppi_header(bad_thumb); PP_EXPECT(h, !truncated_result.ok()); PP_EXPECT(h, truncated_result.status().code == StatusCode::out_of_range); PP_EXPECT(h, !magic_result.ok()); PP_EXPECT(h, magic_result.status().code == StatusCode::invalid_argument); PP_EXPECT(h, !thumb_result.ok()); PP_EXPECT(h, thumb_result.status().code == StatusCode::invalid_argument); } void rejects_unsupported_document_versions(pp::tests::Harness& h) { auto bad_major = valid_header(); bad_major[4] = std::byte { 1 }; auto bad_minor = valid_header(); bad_minor[8] = std::byte { 0 }; const auto major_result = parse_ppi_header(bad_major); const auto minor_result = parse_ppi_header(bad_minor); PP_EXPECT(h, !major_result.ok()); PP_EXPECT(h, major_result.status().code == StatusCode::invalid_argument); PP_EXPECT(h, !minor_result.ok()); PP_EXPECT(h, minor_result.status().code == StatusCode::invalid_argument); } void parses_project_layout_with_thumbnail_and_body(pp::tests::Harness& h) { auto bytes = valid_header(); const auto thumbnail_size = ppi_thumbnail_byte_size(parse_ppi_header(bytes).value().thumbnail); PP_EXPECT(h, thumbnail_size.ok()); bytes.resize(ppi_header_size + thumbnail_size.value() + 7U, std::byte { 0xaa }); const auto layout = parse_ppi_project_layout(bytes); PP_EXPECT(h, layout.ok()); PP_EXPECT(h, layout.value().thumbnail_offset == ppi_header_size); PP_EXPECT(h, layout.value().thumbnail_bytes == 128U * 128U * 4U); PP_EXPECT(h, layout.value().body_offset == ppi_header_size + (128U * 128U * 4U)); PP_EXPECT(h, layout.value().body_bytes == 7U); } void rejects_project_layout_with_truncated_thumbnail(pp::tests::Harness& h) { auto bytes = valid_header(); bytes.resize(ppi_header_size + (128U * 128U * 4U) - 1U, std::byte { 0 }); const auto layout = parse_ppi_project_layout(bytes); PP_EXPECT(h, !layout.ok()); PP_EXPECT(h, layout.status().code == StatusCode::out_of_range); } } int main() { pp::tests::Harness harness; harness.run("parses_legacy_ppi_header", parses_legacy_ppi_header); harness.run("rejects_truncated_invalid_magic_and_bad_thumbnail", rejects_truncated_invalid_magic_and_bad_thumbnail); harness.run("rejects_unsupported_document_versions", rejects_unsupported_document_versions); harness.run("parses_project_layout_with_thumbnail_and_body", parses_project_layout_with_thumbnail_and_body); harness.run("rejects_project_layout_with_truncated_thumbnail", rejects_project_layout_with_truncated_thumbnail); return harness.finish(); }