use alloc::{string::ToString, vec};
use super::{
BlockTypeError, DecodeBlockContentError, DecodeBufferError, DecodeSequenceError,
DecompressBlockError, DecompressLiteralsError, DictionaryDecodeError, ErrorCode,
ExecuteSequencesError, FSETableError, FrameDecoderError, FrameDescriptorError,
FrameHeaderError, HuffmanTableError, ReadFrameHeaderError,
};
#[test]
fn decoder_errors_map_to_their_libzstd_codes() {
let cases = [
(
FrameDecoderError::ReadFrameHeaderError(ReadFrameHeaderError::InvalidFrameDescriptor(
FrameDescriptorError::ReservedBitSet,
)),
ErrorCode::FrameParameterUnsupported,
),
(
FrameDecoderError::ReadFrameHeaderError(ReadFrameHeaderError::InvalidFrameDescriptor(
FrameDescriptorError::InvalidFrameContentSizeFlag { got: 4 },
)),
ErrorCode::CorruptionDetected,
),
(
FrameDecoderError::ReadFrameHeaderError(ReadFrameHeaderError::BadMagicNumber(0)),
ErrorCode::PrefixUnknown,
),
(
FrameDecoderError::FrameHeaderError(FrameHeaderError::FrameSizeIsZero),
ErrorCode::FrameParameterUnsupported,
),
(
FrameDecoderError::FailedToInitialize(FrameHeaderError::WindowTooSmall { got: 1 }),
ErrorCode::FrameParameterUnsupported,
),
(
FrameDecoderError::FrameHeaderError(FrameHeaderError::WindowTooBig { got: u64::MAX }),
ErrorCode::FrameParameterWindowTooLarge,
),
(
FrameDecoderError::FailedToInitialize(FrameHeaderError::WindowTooBig { got: u64::MAX }),
ErrorCode::FrameParameterWindowTooLarge,
),
(
FrameDecoderError::WindowSizeTooBig {
requested: 2,
limit: 1,
},
ErrorCode::FrameParameterWindowTooLarge,
),
(
FrameDecoderError::WindowCeilingOutOfRange { requested: 0 },
ErrorCode::ParameterOutOfBound,
),
(
FrameDecoderError::DictionaryDecodeError(DictionaryDecodeError::ZeroDictionaryId),
ErrorCode::DictionaryCorrupted,
),
(
FrameDecoderError::DictNotProvided { dict_id: 7 },
ErrorCode::DictionaryWrong,
),
(
FrameDecoderError::DictIdMismatch {
expected: 7,
provided: 8,
},
ErrorCode::DictionaryWrong,
),
(
FrameDecoderError::DictAlreadyRegistered { dict_id: 7 },
ErrorCode::DictionaryWrong,
),
(
FrameDecoderError::TargetTooSmall,
ErrorCode::DstSizeTooSmall,
),
(FrameDecoderError::NotYetInitialized, ErrorCode::InitMissing),
(
FrameDecoderError::ChecksumMismatch {
expected: 1,
calculated: 2,
},
ErrorCode::ChecksumWrong,
),
(
FrameDecoderError::FrameContentSizeMismatch {
declared: 1,
produced: 2,
},
ErrorCode::CorruptionDetected,
),
(
FrameDecoderError::FailedToSkipFrame,
ErrorCode::CorruptionDetected,
),
];
for (err, code) in cases {
assert_eq!(err.code(), code, "{err:?}");
}
}
#[test]
fn error_codes_carry_the_libzstd_messages() {
let cases = [
(ErrorCode::PrefixUnknown, "Unknown frame descriptor"),
(
ErrorCode::FrameParameterUnsupported,
"Unsupported frame parameter",
),
(
ErrorCode::FrameParameterWindowTooLarge,
"Frame requires too much memory for decoding",
),
(ErrorCode::ParameterOutOfBound, "Parameter is out of bound"),
(ErrorCode::DictionaryCorrupted, "Dictionary is corrupted"),
(ErrorCode::DictionaryWrong, "Dictionary mismatch"),
(
ErrorCode::DstSizeTooSmall,
"Destination buffer is too small",
),
(ErrorCode::InitMissing, "Context should be init first"),
(
ErrorCode::ChecksumWrong,
"Restored data doesn't match checksum",
),
(ErrorCode::CorruptionDetected, "Data corruption detected"),
];
for (code, message) in cases {
assert_eq!(code.message(), message, "{code:?}");
}
}
#[test]
fn execute_sequences_output_overflow_requested_covers_all_arms() {
let inline = ExecuteSequencesError::OutputBufferOverflow {
tail: 10,
requested: 7,
capacity: 12,
};
assert_eq!(inline.output_overflow_requested(), Some(7));
let repeat =
ExecuteSequencesError::DecodebufferError(DecodeBufferError::OutputBufferOverflow {
tail: 3,
requested: 99,
capacity: 4,
});
assert_eq!(repeat.output_overflow_requested(), Some(99));
assert_eq!(
ExecuteSequencesError::ZeroOffset.output_overflow_requested(),
None
);
assert_eq!(
ExecuteSequencesError::DecodebufferError(DecodeBufferError::ZeroOffset)
.output_overflow_requested(),
None
);
}
#[test]
fn block_and_sequence_display_messages_are_specific() {
assert_eq!(
BlockTypeError::InvalidBlocktypeNumber { num: 7 }.to_string(),
"Invalid Blocktype number. Is: 7. Should be one of: 0, 1, 2, 3 (3 is reserved)."
);
assert_eq!(
DecompressBlockError::MalformedSectionHeader {
expected_len: 12,
remaining_bytes: 3,
}
.to_string(),
"Malformed section header. Says literals would be this long: 12 but there are only 3 bytes left"
);
assert_eq!(
DecodeBlockContentError::ExpectedHeaderOfPreviousBlock.to_string(),
"Can't decode next block body, while expecting to decode the header of the previous block. Results will be nonsense"
);
assert_eq!(
DecodeSequenceError::ExtraPadding { skipped_bits: 11 }.to_string(),
"Padding at the end of the sequence_section was more than a byte long: 11 bits. Probably caused by data corruption"
);
}
#[test]
fn frame_decoder_display_messages_are_specific() {
assert_eq!(
FrameDecoderError::TargetTooSmall.to_string(),
"Target must have at least as many bytes as the content size reported by the frame"
);
assert_eq!(
FrameDecoderError::DictNotProvided { dict_id: 0xABCD }.to_string(),
"Frame header specified dictionary id 0xABCD that wasn't provided via add_dict()/add_dict_from_bytes() (or add_dict_handle() on atomic targets) or reset_with_dict_handle()/decode_all_with_dict_handle()/decode_all_with_dict_bytes()"
);
assert_eq!(
FrameDecoderError::DictIdMismatch {
expected: 0xABCD,
provided: 0x1234
}
.to_string(),
"Frame header dictionary id 0xABCD does not match provided dictionary id 0x1234"
);
assert_eq!(
FrameDecoderError::DictAlreadyRegistered { dict_id: 0xABCD }.to_string(),
"Dictionary id 0xABCD already registered in decoder"
);
assert_eq!(
FrameDecoderError::FrameContentSizeMismatch {
declared: 100,
produced: 87,
}
.to_string(),
"Frame content size mismatch (corrupt frame): declared 100 bytes, blocks summed to 87 bytes"
);
assert_eq!(
FrameDecoderError::ChecksumMismatch {
expected: 0xDEAD_BEEF,
calculated: 0x0BAD_F00D,
}
.to_string(),
"Content checksum mismatch (corrupt frame): frame stored 0xDEADBEEF, decoder calculated 0x0BADF00D"
);
}
#[test]
fn decode_block_content_backend_overflow_display_names_the_step() {
use crate::blocks::block::BlockType;
assert_eq!(
DecodeBlockContentError::BackendOverflow {
step: BlockType::RLE
}
.to_string(),
"RLE block's decompressed payload exceeds the caller-provided output buffer"
);
assert_eq!(
DecodeBlockContentError::BackendOverflow {
step: BlockType::Raw
}
.to_string(),
"Raw block's decompressed payload exceeds the caller-provided output buffer"
);
}
#[test]
fn literal_display_messages_are_specific() {
assert_eq!(
DecompressLiteralsError::MissingCompressedSize.to_string(),
"compressed size was none even though it must be set to something for compressed literals"
);
assert_eq!(
DecompressLiteralsError::MissingNumStreams.to_string(),
"num_streams was none even though it must be set to something (1 or 4) for compressed literals"
);
assert_eq!(
DecompressLiteralsError::ExtraPadding { skipped_bits: 9 }.to_string(),
"Padding at the end of the sequence_section was more than a byte long: 9 bits. Probably caused by data corruption"
);
}
#[test]
fn fse_and_huffman_display_messages_are_specific() {
assert_eq!(
FSETableError::ProbabilityCounterMismatch {
got: 4,
expected_sum: 3,
symbol_probabilities: vec![1, -1],
}
.to_string(),
"FSE probability sum mismatch: got 4, expected 3. Indicates corrupted data or an invalid distribution\n [1, -1]"
);
assert_eq!(
HuffmanTableError::NotEnoughBytesForWeights {
got_bytes: 2,
expected_bytes: 5,
}
.to_string(),
"Header says there should be 5 bytes for the weights but there are only 2 bytes in the stream"
);
assert_eq!(
HuffmanTableError::ExtraPadding { skipped_bits: 13 }.to_string(),
"Padding at the end of the sequence_section was more than a byte long: 13 bits. Probably caused by data corruption"
);
assert_eq!(
HuffmanTableError::FSETableUsedTooManyBytes {
used: 7,
available_bytes: 6,
}
.to_string(),
"FSE table used more bytes: 7 than were meant to be used for the whole stream of huffman weights (6)"
);
}