blob: 2c61c559d764b03d3ae13ef06959b6c2db2c49ec [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <array>
#include <gtest/gtest.h>
#include "src/lib/chunked-compression/chunked-decompressor.h"
#include "src/storage/blobfs/delivery_blob.h"
#include "src/storage/blobfs/delivery_blob_private.h"
#include "src/storage/tools/blobfs-compression/blobfs-compression.h"
namespace blobfs_compress {
using namespace blobfs;
TEST(OfflineCompressionTest, Type1EmptyBlob) {
const zx::result<fbl::Array<uint8_t>> delivery_blob =
GenerateDeliveryBlob({}, DeliveryBlobType::kType1);
ASSERT_TRUE(delivery_blob.is_ok());
// Data should only contain headers and no payload.
ASSERT_EQ(delivery_blob->size(), sizeof(DeliveryBlobHeader) + sizeof(MetadataType1));
// Decode headers/metadata and validate.
const cpp20::span<const uint8_t> buffer(delivery_blob->data(), delivery_blob->size());
const zx::result<DeliveryBlobHeader> header = DeliveryBlobHeader::FromBuffer(buffer);
ASSERT_TRUE(header.is_ok());
ASSERT_TRUE(header->IsValid());
ASSERT_EQ(header->type, DeliveryBlobType::kType1);
EXPECT_EQ(header->header_length, sizeof(DeliveryBlobHeader) + sizeof(MetadataType1));
const zx::result<MetadataType1> metadata =
MetadataType1::FromBuffer(buffer.subspan(sizeof(DeliveryBlobHeader)), *header);
ASSERT_TRUE(metadata.is_ok());
ASSERT_TRUE(metadata->IsValid(*header));
EXPECT_EQ(metadata->payload_length, 0u);
EXPECT_EQ(metadata->flags & ~MetadataType1::kValidFlagsMask, 0u);
// There should be no payload to decompress, so the IsCompressed bit should be zero.
EXPECT_FALSE(metadata->flags & MetadataType1::kIsCompressed);
}
TEST(OfflineCompressionTest, Type1ShouldNotCompress) {
// If we have less data to compress than the zstd-chunked header, we shouldn't use compression
// as the result would always be larger than the original.
constexpr size_t kSmallPayloadSize = 4u;
const std::vector<uint8_t> blob_data(kSmallPayloadSize);
const zx::result<fbl::Array<uint8_t>> delivery_blob =
GenerateDeliveryBlob({blob_data.data(), blob_data.size()}, DeliveryBlobType::kType1);
ASSERT_TRUE(delivery_blob.is_ok());
ASSERT_EQ(delivery_blob->size(),
sizeof(DeliveryBlobHeader) + sizeof(MetadataType1) + blob_data.size());
// Decode headers/metadata and validate.
const cpp20::span buffer(delivery_blob->data(), delivery_blob->size());
const zx::result<DeliveryBlobHeader> header = DeliveryBlobHeader::FromBuffer(buffer);
ASSERT_TRUE(header.is_ok());
ASSERT_TRUE(header->IsValid());
ASSERT_EQ(header->type, DeliveryBlobType::kType1);
EXPECT_EQ(header->header_length, sizeof(DeliveryBlobHeader) + sizeof(MetadataType1));
const cpp20::span metadata_buffer = buffer.subspan(sizeof(DeliveryBlobHeader));
const zx::result<MetadataType1> metadata = MetadataType1::FromBuffer(metadata_buffer, *header);
ASSERT_TRUE(metadata.is_ok());
ASSERT_TRUE(metadata->IsValid(*header));
EXPECT_EQ(metadata->payload_length, blob_data.size());
// Validate payload itself.
const cpp20::span payload_buffer = metadata_buffer.subspan(sizeof(MetadataType1));
ASSERT_EQ(payload_buffer.size(), blob_data.size());
ASSERT_TRUE(std::equal(payload_buffer.begin(), payload_buffer.end(), blob_data.cbegin()));
}
TEST(OfflineCompressionTest, Type1ShouldCompress) {
constexpr size_t kLargePayloadSize = 1ul << 16;
const std::vector<uint8_t> blob_data(kLargePayloadSize);
const zx::result<fbl::Array<uint8_t>> delivery_blob =
GenerateDeliveryBlob({blob_data.data(), blob_data.size()}, DeliveryBlobType::kType1);
ASSERT_TRUE(delivery_blob.is_ok());
ASSERT_LE(delivery_blob->size(),
sizeof(DeliveryBlobHeader) + sizeof(MetadataType1) + blob_data.size());
// Decode headers/metadata and validate.
const cpp20::span buffer(delivery_blob->data(), delivery_blob->size());
const zx::result<DeliveryBlobHeader> header = DeliveryBlobHeader::FromBuffer(buffer);
ASSERT_TRUE(header.is_ok());
ASSERT_TRUE(header->IsValid());
ASSERT_EQ(header->type, DeliveryBlobType::kType1);
EXPECT_EQ(header->header_length, sizeof(DeliveryBlobHeader) + sizeof(MetadataType1));
const cpp20::span metadata_buffer = buffer.subspan(sizeof(DeliveryBlobHeader));
const zx::result<MetadataType1> metadata = MetadataType1::FromBuffer(metadata_buffer, *header);
ASSERT_TRUE(metadata.is_ok());
ASSERT_TRUE(metadata->IsValid(*header));
// The payload length should be less than the actual data as it should be compressed.
EXPECT_LE(metadata->payload_length, blob_data.size());
EXPECT_TRUE((metadata->flags & MetadataType1::kValidFlagsMask) != 0u);
// Ensure that we can correctly decompress the payload.
const cpp20::span payload_buffer = metadata_buffer.subspan(sizeof(MetadataType1));
fbl::Array<uint8_t> decompressed_result;
size_t unused;
ASSERT_EQ(chunked_compression::ChunkedDecompressor::DecompressBytes(
payload_buffer.data(), payload_buffer.size(), &decompressed_result, &unused),
chunked_compression::kStatusOk);
// Verify the decompressed result.
ASSERT_EQ(decompressed_result.size(), blob_data.size());
ASSERT_TRUE(
std::equal(decompressed_result.begin(), decompressed_result.end(), blob_data.cbegin()));
}
} // namespace blobfs_compress