blob: 500f0c502f2cf02e929bd5bddcfc7e7f4db04271 [file] [log] [blame]
// Copyright 2020 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 <fbl/array.h>
#include <src/lib/chunked-compression/chunked-archive.h>
#include <src/lib/chunked-compression/chunked-compressor.h>
#include <src/lib/chunked-compression/chunked-decompressor.h>
#include <src/lib/chunked-compression/status.h>
#include <zxtest/zxtest.h>
namespace chunked_compression {
namespace {
void RandomFill(uint8_t* data, size_t len) {
size_t off = 0;
size_t rounded_len = fbl::round_down(len, sizeof(int));
for (off = 0; off < rounded_len; off += sizeof(int)) {
*reinterpret_cast<int*>(data + off) = rand();
}
ZX_ASSERT(off == rounded_len);
for (; off < len; ++off) {
data[off] = static_cast<uint8_t>(rand());
}
}
} // namespace
TEST(ChunkedDecompressorTest, Decompress_EmptyArchive) {
fbl::Array<uint8_t> buf(new uint8_t[16], 16);
HeaderWriter writer(buf.get(), buf.size(), 0);
ASSERT_EQ(writer.Finalize(), kStatusOk);
fbl::Array<uint8_t> out_buf;
size_t decompressed_size;
ASSERT_EQ(
ChunkedDecompressor::DecompressBytes(buf.get(), buf.size(), &out_buf, &decompressed_size),
kStatusOk);
EXPECT_EQ(decompressed_size, 0ul);
}
TEST(ChunkedDecompressorTest, Decompress_SingleFrame_Zeroes) {
size_t len = 8192ul;
fbl::Array<uint8_t> data(new uint8_t[len], len);
memset(data.get(), 0x00, len);
fbl::Array<uint8_t> compressed_data;
size_t compressed_len;
ASSERT_EQ(ChunkedCompressor::CompressBytes(data.get(), len, &compressed_data, &compressed_len),
kStatusOk);
ASSERT_GE(compressed_data.size(), compressed_len);
fbl::Array<uint8_t> out_buf;
size_t decompressed_size;
ASSERT_EQ(ChunkedDecompressor::DecompressBytes(compressed_data.get(), compressed_len, &out_buf,
&decompressed_size),
kStatusOk);
EXPECT_EQ(decompressed_size, 8192l);
EXPECT_BYTES_EQ(data.get(), out_buf.get(), len);
}
TEST(ChunkedDecompressorTest, Decompress_SingleFrame_Random) {
size_t len = 8192ul;
fbl::Array<uint8_t> data(new uint8_t[len], len);
RandomFill(data.get(), len);
fbl::Array<uint8_t> compressed_data;
size_t compressed_len;
ASSERT_EQ(ChunkedCompressor::CompressBytes(data.get(), len, &compressed_data, &compressed_len),
kStatusOk);
ASSERT_GE(compressed_data.size(), compressed_len);
fbl::Array<uint8_t> out_buf;
size_t decompressed_size;
ASSERT_EQ(ChunkedDecompressor::DecompressBytes(compressed_data.get(), compressed_len, &out_buf,
&decompressed_size),
kStatusOk);
EXPECT_EQ(decompressed_size, 8192l);
EXPECT_BYTES_EQ(data.get(), out_buf.get(), len);
}
TEST(ChunkedDecompressorTest, Decompress_MultiFrame_Zeroes) {
// 3 data frames, last one partial
size_t len = (2 * CompressionParams::MinChunkSize()) + 42ul;
fbl::Array<uint8_t> data(new uint8_t[len], len);
memset(data.get(), 0x00, len);
fbl::Array<uint8_t> compressed_data;
size_t compressed_len;
ASSERT_EQ(ChunkedCompressor::CompressBytes(data.get(), len, &compressed_data, &compressed_len),
kStatusOk);
ASSERT_GE(compressed_data.size(), compressed_len);
fbl::Array<uint8_t> out_buf;
size_t decompressed_size;
ASSERT_EQ(ChunkedDecompressor::DecompressBytes(compressed_data.get(), compressed_len, &out_buf,
&decompressed_size),
kStatusOk);
EXPECT_EQ(decompressed_size, len);
EXPECT_BYTES_EQ(data.get(), out_buf.get(), len);
}
TEST(ChunkedDecompressorTest, Decompress_MultiFrame_Random) {
// 3 data frames, last one partial
size_t len = (2 * CompressionParams::MinChunkSize()) + 42ul;
fbl::Array<uint8_t> data(new uint8_t[len], len);
RandomFill(data.get(), len);
fbl::Array<uint8_t> compressed_data;
size_t compressed_len;
ASSERT_EQ(ChunkedCompressor::CompressBytes(data.get(), len, &compressed_data, &compressed_len),
kStatusOk);
ASSERT_GE(compressed_data.size(), compressed_len);
fbl::Array<uint8_t> out_buf;
size_t decompressed_size;
ASSERT_EQ(ChunkedDecompressor::DecompressBytes(compressed_data.get(), compressed_len, &out_buf,
&decompressed_size),
kStatusOk);
EXPECT_EQ(decompressed_size, len);
EXPECT_BYTES_EQ(data.get(), out_buf.get(), len);
}
TEST(ChunkedDecompressorTest, DecompressFrame_MultiFrame_Zeroes) {
size_t chunk_size = CompressionParams::MinChunkSize();
// 3 data frames, last one partial
size_t len = (2 * chunk_size) + 42ul;
fbl::Array<uint8_t> data(new uint8_t[len], len);
memset(data.get(), 0x00, len);
fbl::Array<uint8_t> compressed_data;
size_t compressed_len;
ASSERT_EQ(ChunkedCompressor::CompressBytes(data.get(), len, &compressed_data, &compressed_len),
kStatusOk);
ASSERT_GE(compressed_data.size(), compressed_len);
fbl::Array<uint8_t> out_buf(new uint8_t[chunk_size], chunk_size);
SeekTable table;
HeaderReader reader;
ASSERT_OK(reader.Parse(compressed_data.get(), compressed_len, compressed_len, &table));
ChunkedDecompressor decompressor;
// Frame 0
uint8_t* frame_start = compressed_data.get() + table.Entries()[0].compressed_offset;
size_t frame_length = table.Entries()[0].compressed_size;
size_t bytes_written;
ASSERT_EQ(decompressor.DecompressFrame(table, 0, frame_start, frame_length, out_buf.get(),
out_buf.size(), &bytes_written),
kStatusOk);
EXPECT_EQ(bytes_written, chunk_size);
EXPECT_BYTES_EQ(data.get(), out_buf.get(), bytes_written);
// Frame 1
frame_start = compressed_data.get() + table.Entries()[1].compressed_offset;
frame_length = table.Entries()[1].compressed_size;
ASSERT_EQ(decompressor.DecompressFrame(table, 1, frame_start, frame_length, out_buf.get(),
out_buf.size(), &bytes_written),
kStatusOk);
EXPECT_EQ(bytes_written, chunk_size);
EXPECT_BYTES_EQ(data.get() + chunk_size, out_buf.get(), bytes_written);
// Frame 2
frame_start = compressed_data.get() + table.Entries()[2].compressed_offset;
frame_length = table.Entries()[2].compressed_size;
ASSERT_EQ(decompressor.DecompressFrame(table, 2, frame_start, frame_length, out_buf.get(),
out_buf.size(), &bytes_written),
kStatusOk);
EXPECT_EQ(bytes_written, 42ul);
EXPECT_BYTES_EQ(data.get() + (2 * chunk_size), out_buf.get(), bytes_written);
}
TEST(ChunkedDecompressorTest, DecompressFrame_MultiFrame_Random) {
size_t chunk_size = CompressionParams::MinChunkSize();
// 3 data frames, last one partial
size_t len = (2 * chunk_size) + 42ul;
fbl::Array<uint8_t> data(new uint8_t[len], len);
RandomFill(data.get(), len);
fbl::Array<uint8_t> compressed_data;
size_t compressed_len;
ASSERT_EQ(ChunkedCompressor::CompressBytes(data.get(), len, &compressed_data, &compressed_len),
kStatusOk);
ASSERT_GE(compressed_data.size(), compressed_len);
fbl::Array<uint8_t> out_buf(new uint8_t[chunk_size], chunk_size);
SeekTable table;
HeaderReader reader;
ASSERT_OK(reader.Parse(compressed_data.get(), compressed_len, compressed_len, &table));
ChunkedDecompressor decompressor;
// Frame 0
uint8_t* frame_start = compressed_data.get() + table.Entries()[0].compressed_offset;
size_t frame_length = table.Entries()[0].compressed_size;
size_t bytes_written;
ASSERT_EQ(decompressor.DecompressFrame(table, 0, frame_start, frame_length, out_buf.get(),
out_buf.size(), &bytes_written),
kStatusOk);
EXPECT_EQ(bytes_written, chunk_size);
EXPECT_BYTES_EQ(data.get(), out_buf.get(), bytes_written);
// Frame 1
frame_start = compressed_data.get() + table.Entries()[1].compressed_offset;
frame_length = table.Entries()[1].compressed_size;
ASSERT_EQ(decompressor.DecompressFrame(table, 1, frame_start, frame_length, out_buf.get(),
out_buf.size(), &bytes_written),
kStatusOk);
EXPECT_EQ(bytes_written, chunk_size);
EXPECT_BYTES_EQ(data.get() + chunk_size, out_buf.get(), bytes_written);
// Frame 2
frame_start = compressed_data.get() + table.Entries()[2].compressed_offset;
frame_length = table.Entries()[2].compressed_size;
ASSERT_EQ(decompressor.DecompressFrame(table, 2, frame_start, frame_length, out_buf.get(),
out_buf.size(), &bytes_written),
kStatusOk);
EXPECT_EQ(bytes_written, 42ul);
EXPECT_BYTES_EQ(data.get() + (2 * chunk_size), out_buf.get(), bytes_written);
}
TEST(ChunkedDecompressorTest, DecompressFrame_BadFrameNumber) {
size_t chunk_size = CompressionParams::MinChunkSize();
// 3 data frames, last one partial
size_t len = (2 * chunk_size) + 42ul;
fbl::Array<uint8_t> data(new uint8_t[len], len);
RandomFill(data.get(), len);
fbl::Array<uint8_t> compressed_data;
size_t compressed_len;
ASSERT_EQ(ChunkedCompressor::CompressBytes(data.get(), len, &compressed_data, &compressed_len),
kStatusOk);
ASSERT_GE(compressed_data.size(), compressed_len);
fbl::Array<uint8_t> out_buf(new uint8_t[chunk_size], chunk_size);
SeekTable table;
HeaderReader reader;
ASSERT_OK(reader.Parse(compressed_data.get(), compressed_len, compressed_len, &table));
ChunkedDecompressor decompressor;
uint8_t* frame_start = compressed_data.get() + table.Entries()[0].compressed_offset;
size_t frame_length = table.Entries()[0].compressed_size;
size_t bytes_written;
ASSERT_EQ(decompressor.DecompressFrame(table, 3, frame_start, frame_length, out_buf.get(),
out_buf.size(), &bytes_written),
kStatusErrInvalidArgs);
}
TEST(ChunkedDecompressorTest, DecompressFrame_BadOffset) {
size_t chunk_size = CompressionParams::MinChunkSize();
// 3 data frames, last one partial
size_t len = (2 * chunk_size) + 42ul;
fbl::Array<uint8_t> data(new uint8_t[len], len);
RandomFill(data.get(), len);
fbl::Array<uint8_t> compressed_data;
size_t compressed_len;
ASSERT_EQ(ChunkedCompressor::CompressBytes(data.get(), len, &compressed_data, &compressed_len),
kStatusOk);
ASSERT_GE(compressed_data.size(), compressed_len);
fbl::Array<uint8_t> out_buf(new uint8_t[chunk_size], chunk_size);
SeekTable table;
HeaderReader reader;
ASSERT_OK(reader.Parse(compressed_data.get(), compressed_len, compressed_len, &table));
ChunkedDecompressor decompressor;
uint8_t* frame_start = compressed_data.get() + table.Entries()[0].compressed_offset;
size_t frame_length = table.Entries()[0].compressed_size;
size_t bytes_written;
// Starting at 1 byte past the actual frame start.
ASSERT_EQ(decompressor.DecompressFrame(table, 0, frame_start + 1, frame_length, out_buf.get(),
out_buf.size(), &bytes_written),
kStatusErrInternal);
// Starting at 1 byte before the actual frame start.
ASSERT_EQ(decompressor.DecompressFrame(table, 0, frame_start - 1, frame_length, out_buf.get(),
out_buf.size(), &bytes_written),
kStatusErrInternal);
}
TEST(ChunkedDecompressorTest, DecompressFrame_SmallBuffer) {
size_t chunk_size = CompressionParams::MinChunkSize();
// 3 data frames, last one partial
size_t len = (2 * chunk_size) + 42ul;
fbl::Array<uint8_t> data(new uint8_t[len], len);
RandomFill(data.get(), len);
fbl::Array<uint8_t> compressed_data;
size_t compressed_len;
ASSERT_EQ(ChunkedCompressor::CompressBytes(data.get(), len, &compressed_data, &compressed_len),
kStatusOk);
ASSERT_GE(compressed_data.size(), compressed_len);
fbl::Array<uint8_t> out_buf(new uint8_t[chunk_size], chunk_size);
SeekTable table;
HeaderReader reader;
ASSERT_OK(reader.Parse(compressed_data.get(), compressed_len, compressed_len, &table));
ChunkedDecompressor decompressor;
uint8_t* frame_start = compressed_data.get() + table.Entries()[0].compressed_offset;
size_t frame_length = table.Entries()[0].compressed_size;
size_t bytes_written;
// Compressed buffer 1 byte too small
ASSERT_EQ(decompressor.DecompressFrame(table, 0, frame_start, frame_length - 1, out_buf.get(),
out_buf.size(), &bytes_written),
kStatusErrBufferTooSmall);
// Decompressed buffer 1 byte too small
ASSERT_EQ(decompressor.DecompressFrame(table, 0, frame_start, frame_length, out_buf.get(),
out_buf.size() - 1, &bytes_written),
kStatusErrBufferTooSmall);
}
} // namespace chunked_compression