From c1e3f7d07aeeb38cb6cf415ef09ae9de9c6c68ba Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 18:00:57 +0200 Subject: [PATCH] test(legacy): cover the v0.2 decoder ABI The v0.2 decoder implementation and C boundary were already Rust-owned, but its public error predicate, frame-size sentinel behavior, and opaque streaming context lifecycle lacked focused Rust coverage. Add tests for malformed input, content-size error reporting, one-shot decoding, reset behavior, and idempotent context release so future legacy changes are checked at the ABI boundary. Test Plan: - Focused v0.2 Cargo tests under both feature sets -- 6 passed each - `make -C tests -j2 test-legacy` -- passed - `cargo +nightly fmt --manifest-path rust/Cargo.toml -- --check` -- passed - Scoped `git diff --check` -- passed --- rust/src/legacy/zstd_v02.rs | 90 +++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/rust/src/legacy/zstd_v02.rs b/rust/src/legacy/zstd_v02.rs index 1d360bc63..48a538793 100644 --- a/rust/src/legacy/zstd_v02.rs +++ b/rust/src/legacy/zstd_v02.rs @@ -1710,6 +1710,96 @@ mod tests { decode(&RAW_FRAME[..RAW_FRAME.len() - 1], 32).unwrap_err(), ERROR(ZstdErrorCode::SrcSizeWrong) ); + assert_eq!( + decode(RAW_FRAME, 4).unwrap_err(), + ERROR(ZstdErrorCode::DstSizeTooSmall) + ); + } + } + + #[test] + fn public_error_predicate_matches_the_c_abi() { + assert_eq!(ZSTDv02_isError(0), 0); + assert_eq!(ZSTDv02_isError(ERROR(ZstdErrorCode::Generic)), 1); + assert_eq!(ZSTDv02_isError(ERROR(ZstdErrorCode::SrcSizeWrong)), 1); + assert_eq!(ZSTDv02_isError(ERROR(ZstdErrorCode::MaxCode)), 0); + assert_eq!(ZSTDv02_isError(usize::MAX), 1); + } + + #[test] + fn frame_size_abi_writes_error_and_content_size_sentinel() { + unsafe { + let mut c_size = 0xA5A5_A5A5usize; + let mut d_bound = 0x5A5A_5A5A_5A5A_5A5Au64; + ZSTDv02_findFrameSizeInfoLegacy( + RAW_FRAME.as_ptr() as *const c_void, + RAW_FRAME.len() - 1, + &mut c_size, + &mut d_bound, + ); + assert_eq!(c_size, ERROR(ZstdErrorCode::SrcSizeWrong)); + assert_eq!(d_bound, ZSTD_CONTENTSIZE_ERROR); + + let mut bad_magic = RAW_FRAME.to_vec(); + bad_magic[0] ^= 1; + ZSTDv02_findFrameSizeInfoLegacy( + bad_magic.as_ptr() as *const c_void, + bad_magic.len(), + &mut c_size, + &mut d_bound, + ); + assert_eq!(c_size, ERROR(ZstdErrorCode::PrefixUnknown)); + assert_eq!(d_bound, ZSTD_CONTENTSIZE_ERROR); + } + } + + #[test] + fn context_abi_preserves_stream_state_and_supports_one_shot_decode() { + unsafe { + let dctx = ZSTDv02_createDCtx(); + assert!(!dctx.is_null()); + assert_eq!(ZSTDv02_nextSrcSizeToDecompress(dctx), 4); + + let mut output = [0u8; 32]; + assert_eq!( + ZSTDv02_decompressContinue( + dctx, + output.as_mut_ptr() as *mut c_void, + output.len(), + RAW_FRAME.as_ptr() as *const c_void, + 3, + ), + ERROR(ZstdErrorCode::SrcSizeWrong) + ); + assert_eq!(ZSTDv02_nextSrcSizeToDecompress(dctx), 4); + + let mut bad_magic = RAW_FRAME.to_vec(); + bad_magic[0] ^= 1; + assert_eq!( + ZSTDv02_decompressContinue( + dctx, + output.as_mut_ptr() as *mut c_void, + output.len(), + bad_magic.as_ptr() as *const c_void, + 4, + ), + ERROR(ZstdErrorCode::PrefixUnknown) + ); + assert_eq!(ZSTDv02_nextSrcSizeToDecompress(dctx), 4); + + let decoded = ZSTDv02_decompressDCtx( + dctx as *mut c_void, + output.as_mut_ptr() as *mut c_void, + output.len(), + RAW_FRAME.as_ptr() as *const c_void, + RAW_FRAME.len(), + ); + assert_eq!(decoded, b"raw v0.2!!!".len()); + assert_eq!(&output[..decoded], b"raw v0.2!!!"); + assert_eq!(ZSTDv02_resetDCtx(dctx), 0); + assert_eq!(ZSTDv02_nextSrcSizeToDecompress(dctx), 4); + assert_eq!(ZSTDv02_freeDCtx(dctx), 0); + assert_eq!(ZSTDv02_freeDCtx(ptr::null_mut()), 0); } }