blob: 77ffdf9927df93f6f08828ca44d13a6a939691e2 [file]
// Copyright 2026 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
//
// https://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 "formats/jpeg/box_header.h"
#include <cstdint>
#include <string>
#include "absl/status/status.h"
#include "absl/status/status_matchers.h"
#include "absl/strings/str_format.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "riegeli/bytes/string_reader.h"
namespace credentio {
namespace {
using ::absl_testing::IsOk;
using ::absl_testing::StatusIs;
using ::testing::Range;
using ::testing::TestParamInfo;
using BoxHeaderTest = testing::TestWithParam<uint32_t>;
INSTANTIATE_TEST_SUITE_P(MarkerTests, BoxHeaderTest,
Range(static_cast<uint32_t>(0x0000feff),
static_cast<uint32_t>(0x00010000)),
[](const TestParamInfo<uint32_t>& info) {
return absl::StrFormat(
"%04x", static_cast<uint16_t>(info.param));
});
TEST_P(BoxHeaderTest, ConsumeJpegBoxHeaderTest) {
std::string data("\0\0\0\4\1\1", 6);
data[0] = static_cast<uint8_t>((GetParam() & 0x0000ff00) >> 8);
data[1] = static_cast<uint8_t>((GetParam() & 0x000000ff));
riegeli::StringReader<> input(data);
if (GetParam() < 0xff00 || GetParam() == 0xffff) {
// Invalid marker.
EXPECT_THAT(ConsumeJpegBoxHeader(input),
StatusIs(absl::StatusCode::kInvalidArgument));
return;
}
auto header_or = ConsumeJpegBoxHeader(input);
ASSERT_THAT(header_or, IsOk());
JpegBoxHeader header = *header_or;
EXPECT_EQ(header.offset, 0);
EXPECT_EQ(header.type, GetParam());
if (GetParam() == 0xff01 || (GetParam() >= 0xffd0 && GetParam() <= 0xffd9)) {
EXPECT_EQ(header.size, 2);
} else {
EXPECT_EQ(header.size, 6); // Length + 2 for the marker
}
}
TEST(BoxHeaderTest, ConsumeJpegBoxHeaderEmptyInput) {
std::string data("");
riegeli::StringReader<> input(data);
EXPECT_THAT(ConsumeJpegBoxHeader(input),
StatusIs(absl::StatusCode::kDataLoss,
::testing::HasSubstr("Failed to read type")));
}
TEST(BoxHeaderTest, ConsumeJpegBoxHeaderShortInputForSize) {
std::string data("\xff\xe1\x00", 3);
riegeli::StringReader<> input(data);
EXPECT_THAT(ConsumeJpegBoxHeader(input),
StatusIs(absl::StatusCode::kDataLoss,
::testing::HasSubstr("Failed to read size")));
}
TEST_P(BoxHeaderTest, ConvertMarkerToLabelTest) {
uint16_t marker = static_cast<uint16_t>(GetParam() & 0x0000ffff);
JpegBoxHeader header = {.type = marker};
switch (marker) {
case 0xfeff:
EXPECT_EQ(header.label(), "feff");
return;
case 0xff01:
EXPECT_EQ(header.label(), "TEM");
return;
case 0xffc4:
EXPECT_EQ(header.label(), "DHT");
return;
case 0xffc8:
EXPECT_EQ(header.label(), "JPG");
return;
case 0xffcc:
EXPECT_EQ(header.label(), "DAC");
return;
case 0xffd8:
EXPECT_EQ(header.label(), "SOI");
return;
case 0xffd9:
EXPECT_EQ(header.label(), "EOI");
return;
case 0xffda:
EXPECT_EQ(header.label(), "SOS");
return;
case 0xffdb:
EXPECT_EQ(header.label(), "DQT");
return;
case 0xffdc:
EXPECT_EQ(header.label(), "DNL");
return;
case 0xffdd:
EXPECT_EQ(header.label(), "DRI");
return;
case 0xffde:
EXPECT_EQ(header.label(), "DHP");
return;
case 0xffdf:
EXPECT_EQ(header.label(), "EXP");
return;
case 0xfffe:
EXPECT_EQ(header.label(), "COM");
return;
case 0xffff:
EXPECT_EQ(header.label(), "ffff");
return;
}
if ((marker & 0x00f0) == 0x00c0) {
EXPECT_EQ(header.label(), absl::StrFormat("SOF%d", (marker & 0x000f)));
return;
}
if (marker >= 0xffd0 && marker <= 0xffd7) {
EXPECT_EQ(header.label(), absl::StrFormat("RST%d", (marker & 0x000f)));
return;
}
if ((marker & 0x00f0) == 0x00e0) {
EXPECT_EQ(header.label(), absl::StrFormat("APP%d", (marker & 0x000f)));
return;
}
if (marker >= 0xfff0 && marker <= 0xfffd) {
EXPECT_EQ(header.label(), absl::StrFormat("JPG%d", (marker & 0x000f)));
return;
}
EXPECT_EQ(header.label(), "RES");
}
TEST(BoxHeaderTest, PrintToJpegBoxHeader) {
JpegBoxHeader header = {.type = 0xffe1, .offset = 10, .size = 20};
EXPECT_EQ(testing::PrintToString(header),
"{type: 65505, offset: 10, size: 20, label: \"APP1\"}");
}
} // namespace
} // namespace credentio