blob: eec5a94e4a957f3466ac0510ca613765d2ab2f06 [file] [log] [blame]
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <executorch/schema/extended_header.h>
#include <gtest/gtest.h>
#include <executorch/runtime/core/result.h>
#include <executorch/runtime/platform/runtime.h>
using namespace ::testing;
using torch::executor::Error;
using torch::executor::ExtendedHeader;
using torch::executor::Result;
class ExtendedHeaderTest : public ::testing::Test {
protected:
void SetUp() override {
// Since these tests cause ET_LOG to be called, the PAL must be initialized
// first.
torch::executor::runtime_init();
}
};
/**
* An example, valid extended header.
*
* This data is intentionally fragile. If the header layout or magic changes,
* this test data must change too. The layout of the header is a contract, not
* an implementation detail.
*/
// clang-format off
constexpr char kExampleHeaderData[] = {
// Magic bytes
'e', 'h', '0', '0',
// uint32_t header size (little endian)
0x18, 0x00, 0x00, 0x00,
// uint64_t program size
0x71, 0x61, 0x51, 0x41, 0x31, 0x21, 0x11, 0x01,
// uint64_t segment base offset
0x72, 0x62, 0x52, 0x42, 0x32, 0x22, 0x12, 0x02,
};
// clang-format on
/// The program_size field encoded in kExampleHeaderData. Each byte is unique
/// within the header data.
constexpr uint64_t kExampleProgramSize = 0x0111213141516171;
/// The segment_base_offset field encoded in kExampleHeaderData. Each byte is
/// unique within the header data.
constexpr uint64_t kExampleSegmentBaseOffset = 0x0212223242526272;
/// The offset to the header's length field, which is in the 4 bytes after the
/// magic.
constexpr size_t kHeaderLengthOffset =
ExtendedHeader::kHeaderOffset + ExtendedHeader::kMagicSize;
/**
* Returns fake serialized Program head data that contains kExampleHeaderData at
* the expected offset.
*/
std::vector<uint8_t> CreateExampleProgramHead() {
// Allocate memory representing the head of the serialized Program.
std::vector<uint8_t> ret(ExtendedHeader::kNumHeadBytes);
// Write non-zeros into it to make it more obvious if we read outside the
// header.
memset(ret.data(), 0x55, ret.size());
// Copy the example header into the right offset.
memcpy(
ret.data() + ExtendedHeader::kHeaderOffset,
kExampleHeaderData,
sizeof(kExampleHeaderData));
return ret;
}
TEST_F(ExtendedHeaderTest, ValidHeaderParsesCorrectly) {
std::vector<uint8_t> program = CreateExampleProgramHead();
Result<ExtendedHeader> header =
ExtendedHeader::Parse(program.data(), program.size());
// The header should be present.
ASSERT_EQ(header.error(), Error::Ok);
// Since each byte of these fields is unique, success demonstrates that the
// endian-to-int conversion is correct and looks at the expected bytes of the
// header.
EXPECT_EQ(header->program_size, kExampleProgramSize);
EXPECT_EQ(header->segment_base_offset, kExampleSegmentBaseOffset);
}
TEST_F(ExtendedHeaderTest, ShortDataFails) {
std::vector<uint8_t> program = CreateExampleProgramHead();
// Try parsing a smaller-than-required part of the data.
ASSERT_GE(program.size(), ExtendedHeader::kNumHeadBytes);
Result<ExtendedHeader> header =
ExtendedHeader::Parse(program.data(), ExtendedHeader::kNumHeadBytes - 1);
// Should have been rejected.
EXPECT_EQ(header.error(), Error::InvalidArgument);
}
TEST_F(ExtendedHeaderTest, MissingHeaderNotFound) {
// Program head data without the extended header magic bytes.
std::vector<uint8_t> program(ExtendedHeader::kNumHeadBytes);
memset(program.data(), 0x55, program.size());
// The header should not be found.
Result<ExtendedHeader> header =
ExtendedHeader::Parse(program.data(), program.size());
EXPECT_EQ(header.error(), Error::NotFound);
}
TEST_F(ExtendedHeaderTest, BadMagicTreatedAsMissing) {
// Get a valid header.
std::vector<uint8_t> program = CreateExampleProgramHead();
// Should be present.
{
Result<ExtendedHeader> header =
ExtendedHeader::Parse(program.data(), program.size());
ASSERT_EQ(header.error(), Error::Ok);
}
// Change a character in the magic.
program[ExtendedHeader::kHeaderOffset] = 'x';
// No longer present.
{
Result<ExtendedHeader> header =
ExtendedHeader::Parse(program.data(), program.size());
EXPECT_EQ(header.error(), Error::NotFound);
}
}
TEST_F(ExtendedHeaderTest, ShorterHeaderLengthFails) {
// Get a valid header.
std::vector<uint8_t> program = CreateExampleProgramHead();
// Should be present.
{
Result<ExtendedHeader> header =
ExtendedHeader::Parse(program.data(), program.size());
ASSERT_EQ(header.error(), Error::Ok);
}
// Make the header length smaller.
// First demonstrate that we're looking in the right place.
EXPECT_EQ(program[kHeaderLengthOffset], 0x18);
program[kHeaderLengthOffset] = 0x10;
// Program now considered invalid.
{
Result<ExtendedHeader> header =
ExtendedHeader::Parse(program.data(), program.size());
EXPECT_EQ(header.error(), Error::InvalidProgram);
}
}
TEST_F(ExtendedHeaderTest, LongerHeaderLengthSucceeds) {
// Get a valid header.
std::vector<uint8_t> program = CreateExampleProgramHead();
// Make the header length larger.
// First demonstrate that we're looking in the right place.
EXPECT_EQ(program[kHeaderLengthOffset], 0x18);
program[kHeaderLengthOffset] = 0x20;
// Should still be present and contain the expected values.
{
Result<ExtendedHeader> header =
ExtendedHeader::Parse(program.data(), program.size());
ASSERT_EQ(header.error(), Error::Ok);
EXPECT_EQ(header->program_size, kExampleProgramSize);
EXPECT_EQ(header->segment_base_offset, kExampleSegmentBaseOffset);
}
}