/* vim:set ts=2 sw=2 sts=2 et: */
/**
* \author Marcus Holland-Moritz (github@mhxnet.de)
* \copyright Copyright (c) Marcus Holland-Moritz
*
* This file is part of dwarfs.
*
* dwarfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* dwarfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dwarfs. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "test_logger.h"
using namespace dwarfs;
using testing::MatchesRegex;
namespace fs = std::filesystem;
namespace {
auto test_dir = fs::path(TEST_DATA_DIR);
FOLLY_PACK_PUSH
struct aiff_file_hdr_t {
uint8_t id[4];
uint32_t size;
uint8_t form[4];
} FOLLY_PACK_ATTR;
aiff_file_hdr_t as_big_endian(aiff_file_hdr_t const& hdr) {
aiff_file_hdr_t result{hdr};
result.size = folly::Endian::big(result.size);
return result;
}
struct aiff_chunk_hdr_t {
uint8_t id[4];
uint32_t size;
} FOLLY_PACK_ATTR;
aiff_chunk_hdr_t as_big_endian(aiff_chunk_hdr_t const& hdr) {
aiff_chunk_hdr_t result{hdr};
result.size = folly::Endian::big(result.size);
return result;
}
struct aiff_comm_chk_t {
uint16_t num_chan;
uint32_t num_sample_frames;
uint16_t sample_size;
uint8_t sample_rate[10]; // long double, but we can't pack that
} FOLLY_PACK_ATTR;
aiff_comm_chk_t as_big_endian(aiff_comm_chk_t const& chk) {
aiff_comm_chk_t result{chk};
result.num_chan = folly::Endian::big(result.num_chan);
result.num_sample_frames = folly::Endian::big(result.num_sample_frames);
result.sample_size = folly::Endian::big(result.sample_size);
return result;
}
struct aiff_ssnd_chk_t {
uint32_t offset;
uint32_t block_size;
} FOLLY_PACK_ATTR;
aiff_ssnd_chk_t as_big_endian(aiff_ssnd_chk_t const& chk) {
aiff_ssnd_chk_t result{chk};
result.offset = folly::Endian::big(result.offset);
result.block_size = folly::Endian::big(result.block_size);
return result;
}
struct caff_file_hdr_t {
uint8_t id[4];
uint16_t version;
uint16_t flags;
} FOLLY_PACK_ATTR;
caff_file_hdr_t as_big_endian(caff_file_hdr_t const& hdr) {
caff_file_hdr_t result{hdr};
result.version = folly::Endian::big(result.version);
result.flags = folly::Endian::big(result.flags);
return result;
}
struct caff_chunk_hdr_t {
uint8_t id[4];
uint64_t size;
} FOLLY_PACK_ATTR;
caff_chunk_hdr_t as_big_endian(caff_chunk_hdr_t const& hdr) {
caff_chunk_hdr_t result{hdr};
result.size = folly::Endian::big(result.size);
return result;
}
struct caff_format_chk_t {
double sample_rate;
uint8_t format_id[4];
uint32_t format_flags;
uint32_t bytes_per_packet;
uint32_t frames_per_packet;
uint32_t channels_per_frame;
uint32_t bits_per_channel;
} FOLLY_PACK_ATTR;
caff_format_chk_t as_big_endian(caff_format_chk_t const& chk) {
caff_format_chk_t result{chk};
result.format_flags = folly::Endian::big(result.format_flags);
result.bytes_per_packet = folly::Endian::big(result.bytes_per_packet);
result.frames_per_packet = folly::Endian::big(result.frames_per_packet);
result.channels_per_frame = folly::Endian::big(result.channels_per_frame);
result.bits_per_channel = folly::Endian::big(result.bits_per_channel);
return result;
}
struct caff_data_chk_t {
uint32_t edit_count;
} FOLLY_PACK_ATTR;
caff_data_chk_t as_big_endian(caff_data_chk_t const& chk) {
caff_data_chk_t result{chk};
result.edit_count = folly::Endian::big(result.edit_count);
return result;
}
struct wav_file_hdr_t {
uint8_t id[4];
uint32_t size;
uint8_t form[4];
} FOLLY_PACK_ATTR;
struct wav_chunk_hdr_t {
uint8_t id[4];
uint32_t size;
} FOLLY_PACK_ATTR;
struct wav64_file_hdr_t {
uint8_t id[16];
uint64_t size;
uint8_t form[16];
} FOLLY_PACK_ATTR;
struct wav64_chunk_hdr_t {
uint8_t id[16];
uint64_t size;
} FOLLY_PACK_ATTR;
struct wav_fmt_chunk_t {
uint16_t format_code;
uint16_t num_channels;
uint32_t samples_per_sec;
uint32_t avg_bytes_per_sec;
uint16_t block_align;
uint16_t bits_per_sample;
uint16_t ext_size{0};
uint16_t valid_bits_per_sample{0};
uint32_t channel_mask{0};
uint16_t sub_format_code{0};
uint8_t guid_remainder[14]{0};
} FOLLY_PACK_ATTR;
FOLLY_PACK_POP
struct pcmfile_builder {
template
void add(T const& t, size_t size = sizeof(T)) {
auto const* p = reinterpret_cast(&t);
data.insert(data.end(), p, p + size);
}
void add_bytes(size_t count, uint8_t value) {
data.insert(data.end(), count, value);
}
std::span span() const { return data; }
size_t size() const { return data.size(); }
std::vector data;
};
} // namespace
TEST(pcmaudio_categorizer, requirements) {
test::test_logger logger(logger::INFO);
boost::program_options::variables_map vm;
auto& catreg = writer::categorizer_registry::instance();
auto catmgr = writer::categorizer_manager(logger);
catmgr.add(catreg.create(logger, "pcmaudio", vm));
try {
catmgr.set_metadata_requirements(
catmgr.category_value("pcmaudio/metadata").value(),
R"({"endianness": ["set", ["big"]], "bytes_per_sample": ["range", 2, 3]})");
FAIL() << "expected std::runtime_error";
} catch (std::runtime_error const& e) {
EXPECT_STREQ(
"unsupported metadata requirements: bytes_per_sample, endianness",
e.what());
} catch (...) {
FAIL() << "unexpected exception: "
<< folly::exceptionStr(std::current_exception());
}
catmgr.set_metadata_requirements(
catmgr.category_value("pcmaudio/waveform").value(),
R"({"endianness": ["set", ["mixed", "big"]], "bytes_per_sample": ["range", 2, 3]})");
auto wav = test_dir / "pcmaudio" / "test16.wav";
auto mm = mmap(wav);
{
auto job = catmgr.job(wav);
job.set_total_size(mm.size());
EXPECT_TRUE(logger.empty());
job.categorize_random_access(mm.span());
auto frag = job.result();
ASSERT_EQ(1, logger.get_log().size());
auto const& ent = logger.get_log().front();
EXPECT_EQ(logger::WARN, ent.level);
EXPECT_THAT(
ent.output,
MatchesRegex(
R"(^\[WAV\] ".*": endianness 'little' does not meet requirements \[big\]$)"));
EXPECT_TRUE(frag.empty());
logger.clear();
}
catmgr.set_metadata_requirements(
catmgr.category_value("pcmaudio/waveform").value(),
R"({"endianness": ["set", ["big", "little"]], "bytes_per_sample": ["range", 1, 4]})");
{
auto job = catmgr.job(wav);
job.set_total_size(mm.size());
EXPECT_TRUE(logger.empty());
job.categorize_random_access(mm.span());
auto frag = job.result();
EXPECT_TRUE(logger.empty());
EXPECT_EQ(2, frag.size());
auto const& first = frag.span()[0];
auto const& second = frag.span()[1];
EXPECT_EQ("pcmaudio/metadata",
catmgr.category_name(first.category().value()));
EXPECT_EQ(44, first.size());
EXPECT_EQ("pcmaudio/waveform",
catmgr.category_name(second.category().value()));
EXPECT_EQ(14, second.size());
EXPECT_EQ(mm.size(), first.size() + second.size());
}
}
class pcmaudio_error_test : public testing::Test {
public:
test::test_logger logger{logger::VERBOSE};
writer::categorizer_manager catmgr{logger};
auto categorize(pcmfile_builder const& builder) {
// std::cout << folly::hexDump(builder.data.data(), builder.data.size());
auto job = catmgr.job(filename());
job.set_total_size(builder.size());
job.categorize_random_access(builder.span());
return job.result();
}
void SetUp() override {
boost::program_options::variables_map vm;
auto& catreg = writer::categorizer_registry::instance();
catmgr.add(catreg.create(logger, "pcmaudio", vm));
catmgr.set_metadata_requirements(
catmgr.category_value("pcmaudio/waveform").value(),
R"({"endianness": ["set", ["big", "little"]], "bytes_per_sample": ["range", 1, 4]})");
}
virtual std::string_view filename() const = 0;
};
class pcmaudio_error_test_aiff : public pcmaudio_error_test {
public:
aiff_file_hdr_t aiff_file_hdr{{'F', 'O', 'R', 'M'}, 62, {'A', 'I', 'F', 'F'}};
aiff_chunk_hdr_t aiff_comm_chunk_hdr{{'C', 'O', 'M', 'M'}, 18};
aiff_comm_chk_t aiff_comm_chunk{1, 8, 16, {0}};
aiff_chunk_hdr_t aiff_ssnd_chunk_hdr{{'S', 'S', 'N', 'D'}, 24};
aiff_ssnd_chk_t aiff_ssnd_chunk{0, 0};
pcmfile_builder build_file() {
pcmfile_builder builder;
builder.add(as_big_endian(aiff_file_hdr));
builder.add(as_big_endian(aiff_comm_chunk_hdr));
builder.add(as_big_endian(aiff_comm_chunk));
builder.add(as_big_endian(aiff_ssnd_chunk_hdr));
builder.add(as_big_endian(aiff_ssnd_chunk));
builder.add_bytes(16, 42);
return builder;
}
std::string_view filename() const override { return "test.aiff"; }
};
class pcmaudio_error_test_caf : public pcmaudio_error_test {
public:
caff_file_hdr_t caff_file_hdr{{'c', 'a', 'f', 'f'}, 1, 0};
caff_chunk_hdr_t caff_format_chunk_hdr{{'d', 'e', 's', 'c'}, 32};
caff_format_chk_t caff_format_chunk{44100, {'l', 'p', 'c', 'm'}, 0, 2, 1, 1,
16};
caff_chunk_hdr_t caff_data_chunk_hdr{{'d', 'a', 't', 'a'}, 20};
caff_data_chk_t caff_data_chunk{0};
pcmfile_builder build_file() {
pcmfile_builder builder;
builder.add(as_big_endian(caff_file_hdr));
builder.add(as_big_endian(caff_format_chunk_hdr));
builder.add(as_big_endian(caff_format_chunk));
builder.add(as_big_endian(caff_data_chunk_hdr));
builder.add(as_big_endian(caff_data_chunk));
builder.add_bytes(16, 42);
return builder;
}
std::string_view filename() const override { return "test.caf"; }
};
class pcmaudio_error_test_wav : public pcmaudio_error_test {
public:
wav_file_hdr_t wav_file_hdr{{'R', 'I', 'F', 'F'}, 52, {'W', 'A', 'V', 'E'}};
wav_chunk_hdr_t wav_fmt_chunk_hdr{{'f', 'm', 't', ' '}, 16};
wav_fmt_chunk_t wav_fmt_chunk{.format_code = 1,
.num_channels = 1,
.samples_per_sec = 44100,
.avg_bytes_per_sec = 44100 * 2,
.block_align = 2,
.bits_per_sample = 16};
wav_chunk_hdr_t wav_data_chunk_hdr{{'d', 'a', 't', 'a'}, 16};
pcmfile_builder build_file() {
pcmfile_builder builder;
builder.add(wav_file_hdr);
builder.add(wav_fmt_chunk_hdr);
builder.add(wav_fmt_chunk, 16);
builder.add(wav_data_chunk_hdr);
builder.add_bytes(16, 42);
return builder;
}
std::string_view filename() const override { return "test.wav"; }
};
class pcmaudio_error_test_wav64 : public pcmaudio_error_test {
public:
wav64_file_hdr_t wav_file_hdr{
{'r', 'i', 'f', 'f', 0x2e, 0x91, 0xcf, 0x11, 0xa5, 0xd6, 0x28, 0xdb, 0x04,
0xc1, 0x00, 0x00},
120,
{'w', 'a', 'v', 'e', 0xf3, 0xac, 0xd3, 0x11, 0x8c, 0xd1, 0x00, 0xc0, 0x4f,
0x8e, 0xdb, 0x8a}};
wav64_chunk_hdr_t wav_fmt_chunk_hdr{{'f', 'm', 't', ' ', 0xf3, 0xac, 0xd3,
0x11, 0x8c, 0xd1, 0x00, 0xc0, 0x4f, 0x8e,
0xdb, 0x8a},
40};
wav_fmt_chunk_t wav_fmt_chunk{.format_code = 1,
.num_channels = 1,
.samples_per_sec = 44100,
.avg_bytes_per_sec = 44100 * 2,
.block_align = 2,
.bits_per_sample = 16};
wav64_chunk_hdr_t wav_data_chunk_hdr{{'d', 'a', 't', 'a', 0xf3, 0xac, 0xd3,
0x11, 0x8c, 0xd1, 0x00, 0xc0, 0x4f,
0x8e, 0xdb, 0x8a},
40};
pcmfile_builder build_file() {
pcmfile_builder builder;
builder.add(wav_file_hdr);
builder.add(wav_fmt_chunk_hdr);
builder.add(wav_fmt_chunk, 16);
builder.add(wav_data_chunk_hdr);
builder.add_bytes(16, 42);
return builder;
}
std::string_view filename() const override { return "test.w64"; }
};
TEST_F(pcmaudio_error_test_wav, no_error) {
auto builder = build_file();
auto frag = categorize(builder);
EXPECT_TRUE(logger.empty());
ASSERT_EQ(2, frag.size());
auto const& first = frag.span()[0];
auto const& second = frag.span()[1];
EXPECT_EQ("pcmaudio/metadata",
catmgr.category_name(first.category().value()));
EXPECT_EQ(44, first.size());
EXPECT_EQ("pcmaudio/waveform",
catmgr.category_name(second.category().value()));
EXPECT_EQ(16, second.size());
EXPECT_EQ(builder.size(), first.size() + second.size());
}
TEST_F(pcmaudio_error_test_wav, missing_fmt_chunk) {
wav_file_hdr.size -= 24;
pcmfile_builder builder;
builder.add(wav_file_hdr);
builder.add(wav_data_chunk_hdr);
builder.add_bytes(16, 42);
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr(
"[WAV] \"test.wav\": got `data` chunk without `fmt ` chunk"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_wav, unknown_format_code) {
wav_file_hdr.form[0] = 'F';
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
// not a WAVE file, so we don't expect any warnings
EXPECT_TRUE(log.empty());
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_wav, unexpected_fmt_chunk_size) {
wav_file_hdr.size += 4;
wav_fmt_chunk_hdr.size += 4;
pcmfile_builder builder;
builder.add(wav_file_hdr);
builder.add(wav_fmt_chunk_hdr);
builder.add(wav_fmt_chunk, 20);
builder.add(wav_data_chunk_hdr);
builder.add_bytes(16, 42);
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr("[WAV] \"test.wav\": unexpected size for `fmt "
"` chunk: 20 (expected 16, 18, 40)"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_wav, unexpected_second_fmt_chunk) {
wav_file_hdr.size += 24;
pcmfile_builder builder;
builder.add(wav_file_hdr);
builder.add(wav_fmt_chunk_hdr);
builder.add(wav_fmt_chunk, 16);
builder.add(wav_fmt_chunk_hdr);
builder.add(wav_fmt_chunk, 16);
builder.add(wav_data_chunk_hdr);
builder.add_bytes(16, 42);
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(
log.front().output,
testing::HasSubstr("[WAV] \"test.wav\": unexpected second `fmt ` chunk"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_wav, unsupported_format_code) {
wav_fmt_chunk.format_code = 2;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(
log.front().output,
testing::HasSubstr("[WAV] \"test.wav\": unsupported format: 2/0"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_wav, metadata_check_failed) {
wav_fmt_chunk.bits_per_sample = 13;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr("[WAV] \"test.wav\": metadata check failed"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_wav, chunk_size_mismatch) {
wav_fmt_chunk.bits_per_sample = 24;
wav_fmt_chunk.avg_bytes_per_sec = 44100 * 3;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(
log.front().output,
testing::HasSubstr("[WAV] \"test.wav\": `data` chunk size includes 1 "
"padding byte(s); got 16, expected 15"));
ASSERT_EQ(3, frag.size());
auto f1 = frag.span()[0];
auto f2 = frag.span()[1];
auto f3 = frag.span()[2];
EXPECT_EQ("pcmaudio/metadata", catmgr.category_name(f1.category().value()));
EXPECT_EQ("pcmaudio/waveform", catmgr.category_name(f2.category().value()));
EXPECT_EQ(15, f2.size());
EXPECT_EQ("pcmaudio/metadata", catmgr.category_name(f3.category().value()));
EXPECT_EQ(1, f3.size());
}
TEST_F(pcmaudio_error_test_wav, unexpected_file_size) {
wav_file_hdr.size -= 4;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(
log.front().output,
testing::HasSubstr(
"[WAV] \"test.wav\": unexpected file size: 48 (expected 52)"));
ASSERT_EQ(2, frag.size());
auto f1 = frag.span()[0];
auto f2 = frag.span()[1];
EXPECT_EQ("pcmaudio/metadata", catmgr.category_name(f1.category().value()));
EXPECT_EQ(44, f1.size());
EXPECT_EQ("pcmaudio/waveform", catmgr.category_name(f2.category().value()));
EXPECT_EQ(16, f2.size());
}
TEST_F(pcmaudio_error_test_wav64, no_error) {
auto builder = build_file();
auto frag = categorize(builder);
EXPECT_TRUE(logger.empty());
ASSERT_EQ(2, frag.size());
auto const& first = frag.span()[0];
auto const& second = frag.span()[1];
EXPECT_EQ("pcmaudio/metadata",
catmgr.category_name(first.category().value()));
EXPECT_EQ(104, first.size());
EXPECT_EQ("pcmaudio/waveform",
catmgr.category_name(second.category().value()));
EXPECT_EQ(16, second.size());
EXPECT_EQ(builder.size(), first.size() + second.size());
}
TEST_F(pcmaudio_error_test_wav64, no_error_alignment) {
wav_file_hdr.size = 128;
wav_fmt_chunk_hdr.size = 42;
pcmfile_builder builder;
builder.add(wav_file_hdr);
builder.add(wav_fmt_chunk_hdr);
builder.add(wav_fmt_chunk, 18);
builder.add_bytes(6, 0); // pad for alignment
builder.add(wav_data_chunk_hdr);
builder.add_bytes(16, 42);
auto frag = categorize(builder);
EXPECT_TRUE(logger.empty());
ASSERT_EQ(2, frag.size());
auto const& first = frag.span()[0];
auto const& second = frag.span()[1];
EXPECT_EQ("pcmaudio/metadata",
catmgr.category_name(first.category().value()));
EXPECT_EQ(112, first.size());
EXPECT_EQ("pcmaudio/waveform",
catmgr.category_name(second.category().value()));
EXPECT_EQ(16, second.size());
EXPECT_EQ(builder.size(), first.size() + second.size());
}
TEST_F(pcmaudio_error_test_wav64, truncated_file) {
auto builder = build_file();
builder.data.resize(builder.data.size() - 41);
auto frag = categorize(builder);
ASSERT_EQ(2, logger.get_log().size());
EXPECT_THAT(
logger.get_log()[0].output,
testing::HasSubstr("[WAV64] \"test.w64\": unexpected file size: 120 "
"(expected 79)"));
EXPECT_THAT(
logger.get_log()[1].output,
testing::HasSubstr("[WAV64] \"test.w64\": unexpected end of file"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_wav64, invalid_chunk_size) {
wav_fmt_chunk_hdr.size = 8;
auto builder = build_file();
auto frag = categorize(builder);
ASSERT_EQ(1, logger.get_log().size());
EXPECT_THAT(
logger.get_log()[0].output,
testing::HasSubstr("[WAV64] \"test.w64\": invalid chunk size: 8"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_aiff, no_error) {
auto builder = build_file();
auto frag = categorize(builder);
EXPECT_TRUE(logger.empty());
ASSERT_EQ(2, frag.size());
auto const& first = frag.span()[0];
auto const& second = frag.span()[1];
EXPECT_EQ("pcmaudio/metadata",
catmgr.category_name(first.category().value()));
EXPECT_EQ(54, first.size());
EXPECT_EQ("pcmaudio/waveform",
catmgr.category_name(second.category().value()));
EXPECT_EQ(16, second.size());
EXPECT_EQ(builder.size(), first.size() + second.size());
}
TEST_F(pcmaudio_error_test_aiff, unexpected_second_comm_chunk) {
aiff_file_hdr.size += 26;
pcmfile_builder builder;
builder.add(as_big_endian(aiff_file_hdr));
builder.add(as_big_endian(aiff_comm_chunk_hdr));
builder.add(as_big_endian(aiff_comm_chunk));
builder.add(as_big_endian(aiff_comm_chunk_hdr));
builder.add(as_big_endian(aiff_comm_chunk));
builder.add(as_big_endian(aiff_ssnd_chunk_hdr));
builder.add(as_big_endian(aiff_ssnd_chunk));
builder.add_bytes(16, 42);
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr(
"[AIFF] \"test.aiff\": unexpected second `COMM` chunk"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_aiff, missing_comm_chunk) {
aiff_file_hdr.size -= 26;
pcmfile_builder builder;
builder.add(as_big_endian(aiff_file_hdr));
builder.add(as_big_endian(aiff_ssnd_chunk_hdr));
builder.add(as_big_endian(aiff_ssnd_chunk));
builder.add_bytes(16, 42);
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(
log.front().output,
testing::HasSubstr(
"[AIFF] \"test.aiff\": got `SSND` chunk without `COMM` chunk"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_aiff, ssnd_invalid_chunk_size) {
aiff_ssnd_chunk_hdr.size -= 1;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr(
"[AIFF] \"test.aiff\": `SSND` invalid chunk size: 23"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, no_error) {
auto builder = build_file();
auto frag = categorize(builder);
EXPECT_TRUE(logger.empty());
ASSERT_EQ(2, frag.size());
auto const& first = frag.span()[0];
auto const& second = frag.span()[1];
EXPECT_EQ("pcmaudio/metadata",
catmgr.category_name(first.category().value()));
EXPECT_EQ(68, first.size());
EXPECT_EQ("pcmaudio/waveform",
catmgr.category_name(second.category().value()));
EXPECT_EQ(16, second.size());
EXPECT_EQ(builder.size(), first.size() + second.size());
}
TEST_F(pcmaudio_error_test_caf, no_error_unkown_data_size) {
caff_data_chunk_hdr.size = -1;
auto builder = build_file();
auto frag = categorize(builder);
EXPECT_TRUE(logger.empty());
ASSERT_EQ(2, frag.size());
auto const& first = frag.span()[0];
auto const& second = frag.span()[1];
EXPECT_EQ("pcmaudio/metadata",
catmgr.category_name(first.category().value()));
EXPECT_EQ(68, first.size());
EXPECT_EQ("pcmaudio/waveform",
catmgr.category_name(second.category().value()));
EXPECT_EQ(16, second.size());
EXPECT_EQ(builder.size(), first.size() + second.size());
}
TEST_F(pcmaudio_error_test_caf, unsupported_version_or_flags) {
caff_file_hdr.version = 2;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr(
"[CAF] \"test.caf\": unsupported file version/flags: 2/0"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, unexpected_second_desc_chunk) {
pcmfile_builder builder;
builder.add(as_big_endian(caff_file_hdr));
builder.add(as_big_endian(caff_format_chunk_hdr));
builder.add(as_big_endian(caff_format_chunk));
builder.add(as_big_endian(caff_format_chunk_hdr));
builder.add(as_big_endian(caff_format_chunk));
builder.add(as_big_endian(caff_data_chunk_hdr));
builder.add(as_big_endian(caff_data_chunk));
builder.add_bytes(16, 42);
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(
log.front().output,
testing::HasSubstr("[CAF] \"test.caf\": unexpected second `desc` chunk"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, missing_desc_chunk) {
pcmfile_builder builder;
builder.add(as_big_endian(caff_file_hdr));
builder.add(as_big_endian(caff_data_chunk_hdr));
builder.add(as_big_endian(caff_data_chunk));
builder.add_bytes(16, 42);
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr(
"[CAF] \"test.caf\": got `data` chunk without `desc` chunk"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, unexpected_desc_chunk_size) {
caff_format_chunk_hdr.size += 1;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(
log.front().output,
testing::HasSubstr("[CAF] \"test.caf\": unexpected size for `desc` "
"chunk: 33 (expected 32)"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, unsupported_format) {
caff_format_chunk.format_id[0] = 'y';
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(
log.front().output,
testing::HasSubstr("[CAF] \"test.caf\": unsupported `ypcm` format"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, unsupported_floating_point) {
caff_format_chunk.format_flags = 1;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr(
"[CAF] \"test.caf\": floating point format not supported"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, unsupported_frames_per_packet) {
caff_format_chunk.frames_per_packet = 2;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr(
"[CAF] \"test.caf\": unsupported frames per packet: 2"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, bytes_per_packet_zero) {
caff_format_chunk.bytes_per_packet = 0;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr(
"[CAF] \"test.caf\": bytes per packet must not be zero"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, bytes_per_packet_out_of_range) {
caff_format_chunk.bytes_per_packet = 5;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr(
"[CAF] \"test.caf\": bytes per packet out of range: 5"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, unsupported_packet_size) {
caff_format_chunk.channels_per_frame = 4;
caff_format_chunk.bytes_per_packet = 10;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(
log.front().output,
testing::HasSubstr(
"[CAF] \"test.caf\": unsupported packet size: 10 (4 channels)"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, metadata_check_failed1) {
caff_format_chunk.channels_per_frame = 1;
caff_format_chunk.channels_per_frame = 4;
caff_format_chunk.bytes_per_packet = 4;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr("[CAF] \"test.caf\": metadata check failed"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, metadata_check_failed2) {
caff_format_chunk.channels_per_frame = 2;
caff_format_chunk.bits_per_channel = 8;
caff_format_chunk.bytes_per_packet = 4;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr("[CAF] \"test.caf\": metadata check failed"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, metadata_check_failed3) {
caff_format_chunk.channels_per_frame = 1;
caff_format_chunk.bits_per_channel = 24;
caff_format_chunk.bytes_per_packet = 2;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr("[CAF] \"test.caf\": metadata check failed"));
EXPECT_EQ(0, frag.size());
}
TEST_F(pcmaudio_error_test_caf, metadata_check_failed4) {
caff_format_chunk.channels_per_frame = 1;
caff_format_chunk.bits_per_channel = 32;
caff_format_chunk.bytes_per_packet = 3;
auto builder = build_file();
auto frag = categorize(builder);
auto const& log = logger.get_log();
ASSERT_EQ(1, log.size());
EXPECT_THAT(log.front().output,
testing::HasSubstr("[CAF] \"test.caf\": metadata check failed"));
EXPECT_EQ(0, frag.size());
}