consensus_encoding: move decoders inline tests to integration
What changed, and why it matters
This commit simply moves existing unit tests from inside the source file to a separate integration test file. No production code behavior was changed, so there is no security impact.
No action required; this is a test-only refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff removes inline #[cfg(test)] unit tests from consensus_encoding/src/decode/decoders.rs and adds equivalent tests to consensus_encoding/tests/decode.rs. It also moves helper types (Inner, InnerDecoder, Test, TestDecoder) used only by tests into the integration test file. The actual decoder implementations and logic remain untouched.
Changed components
consensus_encoding/src/decode/decoders.rsconsensus_encoding/tests/decode.rsInspect captured patch +254 / −193
diff --git a/consensus_encoding/src/decode/decoders.rs b/consensus_encoding/src/decode/decoders.rs
index 087ada07..93e1b6b8 100644
--- a/consensus_encoding/src/decode/decoders.rs
+++ b/consensus_encoding/src/decode/decoders.rs
@@ -1023,104 +1023,9 @@ mod tests {
use alloc::vec;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
- #[cfg(feature = "alloc")]
- use core::iter;
- #[cfg(feature = "std")]
- use std::io::Cursor;
-
- use super::*;
-
- // Stress test the push_bytes impl by passing in a single byte slice repeatedly.
- macro_rules! check_decode_one_byte_at_a_time {
- ($decoder:expr; $($test_name:ident, $want:expr, $array:expr);* $(;)?) => {
- $(
- #[test]
- #[allow(non_snake_case)]
- fn $test_name() {
- let mut decoder = $decoder;
-
- for (i, _) in $array.iter().enumerate() {
- if i < $array.len() - 1 {
- let mut p = &$array[i..i+1];
- assert!(decoder.push_bytes(&mut p).unwrap());
- } else {
- // last byte: `push_bytes` should return false since no more bytes required.
- let mut p = &$array[i..];
- assert!(!decoder.push_bytes(&mut p).unwrap());
- }
- }
-
- let got = decoder.end().unwrap();
- assert_eq!(got, $want);
- }
- )*
-
- }
- }
-
- check_decode_one_byte_at_a_time! {
- CompactSizeDecoder::new_with_limit(0xF0F0_F0F0);
- decode_compact_size_0x10, 0x10, [0x10];
- decode_compact_size_0xFC, 0xFC, [0xFC];
- decode_compact_size_0xFD, 0xFD, [0xFD, 0xFD, 0x00];
- decode_compact_size_0x100, 0x100, [0xFD, 0x00, 0x01];
- decode_compact_size_0xFFF, 0x0FFF, [0xFD, 0xFF, 0x0F];
- decode_compact_size_0x0F0F_0F0F, 0x0F0F_0F0F, [0xFE, 0xF, 0xF, 0xF, 0xF];
- }
-
- #[test]
- #[cfg(target_pointer_width = "64")]
- #[allow(non_snake_case)]
- fn decode_compact_size_0xF0F0_F0F0_F0E0() {
- let mut decoder = CompactSizeDecoder::new_with_limit(0xF0F0_F0F0_F0EF);
- let array = [0xFF, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0];
-
- for (i, _) in array.iter().enumerate() {
- if i < array.len() - 1 {
- let mut p = &array[i..=i];
- assert!(decoder.push_bytes(&mut p).unwrap());
- } else {
- // last byte: `push_bytes` should return false since no more bytes required.
- let mut p = &array[i..];
- assert!(!decoder.push_bytes(&mut p).unwrap());
- }
- }
-
- let got = decoder.end().unwrap();
- assert_eq!(got, 0xF0F0_F0F0_F0E0);
- }
-
- #[test]
- #[cfg(feature = "alloc")]
- fn compact_size_zero() {
- // Zero (eg for an empty vector) with a couple of arbitrary extra bytes.
- let encoded = alloc::vec![0x00, 0xFF, 0xFF];
-
- let mut slice = encoded.as_slice();
- let mut decoder = CompactSizeDecoder::new();
- assert!(!decoder.push_bytes(&mut slice).unwrap());
-
- let got = decoder.end().unwrap();
- assert_eq!(got, 0);
- }
-
- #[cfg(feature = "alloc")]
- fn two_fifty_six_bytes_encoded() -> Vec<u8> {
- let data = [0xff; 256];
- let mut v = Vec::with_capacity(259);
-
- v.extend_from_slice(&[0xFD, 0x00, 0x01]); // 256 encoded as a compact size.
- v.extend_from_slice(&data);
- v
- }
#[cfg(feature = "alloc")]
- check_decode_one_byte_at_a_time! {
- ByteVecDecoder::default();
- decode_byte_vec, alloc::vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef],
- [0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
- decode_byte_vec_multi_byte_length_prefix, [0xff; 256], two_fifty_six_bytes_encoded();
- }
+ use super::*;
#[test]
#[cfg(feature = "alloc")]
@@ -1181,6 +1086,7 @@ mod tests {
panic!("Expected UnexpectedEof error");
}
}
+
#[test]
#[cfg(feature = "alloc")]
fn byte_vec_decoder_reserves_in_batches() {
@@ -1290,52 +1196,6 @@ mod tests {
fn decoder() -> Self::Decoder { TestDecoder(VecDecoder::new()) }
}
- #[test]
- #[cfg(feature = "alloc")]
- fn vec_decoder_empty() {
- // Empty with a couple of arbitrary extra bytes.
- let encoded = vec![0x00, 0xFF, 0xFF];
-
- let mut slice = encoded.as_slice();
- let mut decoder = Test::decoder();
- assert!(!decoder.push_bytes(&mut slice).unwrap());
-
- let got = decoder.end().unwrap();
- let want = Test(vec![]);
-
- assert_eq!(got, want);
- }
-
- #[test]
- #[cfg(feature = "alloc")]
- fn vec_decoder_one_item() {
- let encoded = vec![0x01, 0xEF, 0xBE, 0xAD, 0xDE];
-
- let mut slice = encoded.as_slice();
- let mut decoder = Test::decoder();
- decoder.push_bytes(&mut slice).unwrap();
-
- let got = decoder.end().unwrap();
- let want = Test(vec![Inner(0xDEAD_BEEF)]);
-
- assert_eq!(got, want);
- }
-
- #[test]
- #[cfg(feature = "alloc")]
- fn vec_decoder_two_items() {
- let encoded = vec![0x02, 0xEF, 0xBE, 0xAD, 0xDE, 0xBE, 0xBA, 0xFE, 0xCA];
-
- let mut slice = encoded.as_slice();
- let mut decoder = Test::decoder();
- decoder.push_bytes(&mut slice).unwrap();
-
- let got = decoder.end().unwrap();
- let want = Test(vec![Inner(0xDEAD_BEEF), Inner(0xCAFE_BABE)]);
-
- assert_eq!(got, want);
- }
-
#[test]
#[cfg(feature = "alloc")]
fn vec_decoder_reserves_in_batches() {
@@ -1384,55 +1244,4 @@ mod tests {
assert_eq!(result.len(), total_len);
assert_eq!(result[total_len - 1], Inner(0xDD));
}
-
- #[cfg(feature = "alloc")]
- fn two_fifty_six_elements() -> Test {
- Test(iter::repeat(Inner(0xDEAD_BEEF)).take(256).collect())
- }
-
- #[cfg(feature = "alloc")]
- fn two_fifty_six_elements_encoded() -> Vec<u8> {
- [0xFD, 0x00, 0x01] // 256 encoded as a compact size.
- .into_iter()
- .chain(iter::repeat(0xDEAD_BEEF_u32.to_le_bytes()).take(256).flatten())
- .collect()
- }
-
- #[cfg(feature = "alloc")]
- check_decode_one_byte_at_a_time! {
- TestDecoder::default();
- decode_vec, Test(vec![Inner(0xDEAD_BEEF), Inner(0xCAFE_BABE)]),
- vec![0x02, 0xEF, 0xBE, 0xAD, 0xDE, 0xBE, 0xBA, 0xFE, 0xCA];
- decode_vec_multi_byte_length_prefix, two_fifty_six_elements(), two_fifty_six_elements_encoded();
- }
-
- #[test]
- #[cfg(feature = "alloc")]
- fn vec_decoder_one_item_plus_more_data() {
- // One u32 plus some other bytes.
- let encoded = vec![0x01, 0xEF, 0xBE, 0xAD, 0xDE, 0xff, 0xff, 0xff, 0xff];
-
- let mut slice = encoded.as_slice();
-
- let mut decoder = Test::decoder();
- decoder.push_bytes(&mut slice).unwrap();
-
- let got = decoder.end().unwrap();
- let want = Test(vec![Inner(0xDEAD_BEEF)]);
-
- assert_eq!(got, want);
- }
-
- #[cfg(feature = "std")]
- #[test]
- fn decode_vec_from_read_unbuffered_success() {
- let encoded = [0x01, 0xEF, 0xBE, 0xAD, 0xDE, 0xff, 0xff, 0xff, 0xff];
- let mut cursor = Cursor::new(&encoded);
-
- let got = crate::decode_from_read_unbuffered::<Test, _>(&mut cursor).unwrap();
- assert_eq!(cursor.position(), 5);
-
- let want = Test(vec![Inner(0xDEAD_BEEF)]);
- assert_eq!(got, want);
- }
}
diff --git a/consensus_encoding/tests/decode.rs b/consensus_encoding/tests/decode.rs
index 1161d155..a75e307d 100644
--- a/consensus_encoding/tests/decode.rs
+++ b/consensus_encoding/tests/decode.rs
@@ -8,6 +8,8 @@ use std::io::{Cursor, Read};
use bitcoin_consensus_encoding::{
ArrayDecoder, CompactSizeDecoder, Decodable, Decoder, Decoder2, UnexpectedEofError,
};
+#[cfg(feature = "alloc")]
+use bitcoin_consensus_encoding::{ByteVecDecoder, VecDecoder, VecDecoderError};
#[cfg(feature = "std")]
use bitcoin_consensus_encoding::{decode_from_read, decode_from_read_unbuffered, ReadError};
use bitcoin_consensus_encoding::decode_from_slice;
@@ -372,3 +374,253 @@ fn decode_from_read_unbuffered_extra_data() {
let decoded = result.unwrap();
assert_eq!(decoded.0, [1, 2, 3, 4]);
}
+
+#[cfg(feature = "alloc")]
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct Inner(u32);
+
+#[cfg(feature = "alloc")]
+struct InnerDecoder(ArrayDecoder<4>);
+
+#[cfg(feature = "alloc")]
+impl Decoder for InnerDecoder {
+ type Output = Inner;
+ type Error = UnexpectedEofError;
+
+ fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
+ self.0.push_bytes(bytes)
+ }
+
+ fn end(self) -> Result<Self::Output, Self::Error> {
+ let n = u32::from_le_bytes(self.0.end()?);
+ Ok(Inner(n))
+ }
+
+ fn read_limit(&self) -> usize { self.0.read_limit() }
+}
+
+#[cfg(feature = "alloc")]
+impl Decodable for Inner {
+ type Decoder = InnerDecoder;
+ fn decoder() -> Self::Decoder { InnerDecoder(ArrayDecoder::<4>::new()) }
+}
+
+#[cfg(feature = "alloc")]
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct Test(Vec<Inner>);
+
+#[cfg(feature = "alloc")]
+#[derive(Default)]
+struct TestDecoder(VecDecoder<Inner>);
+
+#[cfg(feature = "alloc")]
+impl Decoder for TestDecoder {
+ type Output = Test;
+ type Error = VecDecoderError<UnexpectedEofError>;
+
+ fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<bool, Self::Error> {
+ self.0.push_bytes(bytes)
+ }
+
+ fn end(self) -> Result<Self::Output, Self::Error> {
+ let v = self.0.end()?;
+ Ok(Test(v))
+ }
+
+ fn read_limit(&self) -> usize { self.0.read_limit() }
+}
+
+#[cfg(feature = "alloc")]
+impl Decodable for Test {
+ type Decoder = TestDecoder;
+ fn decoder() -> Self::Decoder { TestDecoder(VecDecoder::new()) }
+}
+
+// Stress test the push_bytes impl by passing in a single byte slice repeatedly.
+macro_rules! check_decode_one_byte_at_a_time {
+ ($decoder:expr; $($test_name:ident, $want:expr, $array:expr);* $(;)?) => {
+ $(
+ #[test]
+ #[allow(non_snake_case)]
+ fn $test_name() {
+ let mut decoder = $decoder;
+
+ for (i, _) in $array.iter().enumerate() {
+ if i < $array.len() - 1 {
+ let mut p = &$array[i..i+1];
+ assert!(decoder.push_bytes(&mut p).unwrap());
+ } else {
+ // last byte: `push_bytes` should return false since no more bytes required.
+ let mut p = &$array[i..];
+ assert!(!decoder.push_bytes(&mut p).unwrap());
+ }
+ }
+
+ let got = decoder.end().unwrap();
+ assert_eq!(got, $want);
+ }
+ )*
+
+ }
+}
+
+check_decode_one_byte_at_a_time! {
+ CompactSizeDecoder::new_with_limit(0xF0F0_F0F0);
+ decode_compact_size_0x10, 0x10, [0x10];
+ decode_compact_size_0xFC, 0xFC, [0xFC];
+ decode_compact_size_0xFD, 0xFD, [0xFD, 0xFD, 0x00];
+ decode_compact_size_0x100, 0x100, [0xFD, 0x00, 0x01];
+ decode_compact_size_0xFFF, 0x0FFF, [0xFD, 0xFF, 0x0F];
+ decode_compact_size_0x0F0F_0F0F, 0x0F0F_0F0F, [0xFE, 0xF, 0xF, 0xF, 0xF];
+}
+
+#[test]
+#[cfg(target_pointer_width = "64")]
+#[allow(non_snake_case)]
+fn decode_compact_size_0xF0F0_F0F0_F0E0() {
+ let mut decoder = CompactSizeDecoder::new_with_limit(0xF0F0_F0F0_F0EF);
+ let array = [0xFF, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0];
+
+ for (i, _) in array.iter().enumerate() {
+ if i < array.len() - 1 {
+ let mut p = &array[i..=i];
+ assert!(decoder.push_bytes(&mut p).unwrap());
+ } else {
+ // last byte: `push_bytes` should return false since no more bytes required.
+ let mut p = &array[i..];
+ assert!(!decoder.push_bytes(&mut p).unwrap());
+ }
+ }
+
+ let got = decoder.end().unwrap();
+ assert_eq!(got, 0xF0F0_F0F0_F0E0);
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn compact_size_zero() {
+ // Zero (eg for an empty vector) with a couple of arbitrary extra bytes.
+ let encoded = vec![0x00, 0xFF, 0xFF];
+
+ let mut slice = encoded.as_slice();
+ let mut decoder = CompactSizeDecoder::new();
+ assert!(!decoder.push_bytes(&mut slice).unwrap());
+
+ let got = decoder.end().unwrap();
+ assert_eq!(got, 0);
+}
+
+#[cfg(feature = "alloc")]
+fn two_fifty_six_bytes_encoded() -> Vec<u8> {
+ let data = [0xff; 256];
+ let mut v = Vec::with_capacity(259);
+
+ v.extend_from_slice(&[0xFD, 0x00, 0x01]); // 256 encoded as a compact size.
+ v.extend_from_slice(&data);
+ v
+}
+
+#[cfg(feature = "alloc")]
+check_decode_one_byte_at_a_time! {
+ ByteVecDecoder::default();
+ decode_byte_vec, vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef],
+ [0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
+ decode_byte_vec_multi_byte_length_prefix, [0xff; 256], two_fifty_six_bytes_encoded();
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn vec_decoder_empty() {
+ // Empty with a couple of arbitrary extra bytes.
+ let encoded = vec![0x00, 0xFF, 0xFF];
+
+ let mut slice = encoded.as_slice();
+ let mut decoder = Test::decoder();
+ assert!(!decoder.push_bytes(&mut slice).unwrap());
+
+ let got = decoder.end().unwrap();
+ let want = Test(vec![]);
+
+ assert_eq!(got, want);
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn vec_decoder_one_item() {
+ let encoded = vec![0x01, 0xEF, 0xBE, 0xAD, 0xDE];
+
+ let mut slice = encoded.as_slice();
+ let mut decoder = Test::decoder();
+ decoder.push_bytes(&mut slice).unwrap();
+
+ let got = decoder.end().unwrap();
+ let want = Test(vec![Inner(0xDEAD_BEEF)]);
+
+ assert_eq!(got, want);
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn vec_decoder_two_items() {
+ let encoded = vec![0x02, 0xEF, 0xBE, 0xAD, 0xDE, 0xBE, 0xBA, 0xFE, 0xCA];
+
+ let mut slice = encoded.as_slice();
+ let mut decoder = Test::decoder();
+ decoder.push_bytes(&mut slice).unwrap();
+
+ let got = decoder.end().unwrap();
+ let want = Test(vec![Inner(0xDEAD_BEEF), Inner(0xCAFE_BABE)]);
+
+ assert_eq!(got, want);
+}
+
+#[cfg(feature = "alloc")]
+fn two_fifty_six_elements() -> Test {
+ Test(core::iter::repeat(Inner(0xDEAD_BEEF)).take(256).collect())
+}
+
+#[cfg(feature = "alloc")]
+fn two_fifty_six_elements_encoded() -> Vec<u8> {
+ [0xFD, 0x00, 0x01] // 256 encoded as a compact size.
+ .into_iter()
+ .chain(core::iter::repeat(0xDEAD_BEEF_u32.to_le_bytes()).take(256).flatten())
+ .collect()
+}
+
+#[cfg(feature = "alloc")]
+check_decode_one_byte_at_a_time! {
+ TestDecoder::default();
+ decode_vec, Test(vec![Inner(0xDEAD_BEEF), Inner(0xCAFE_BABE)]),
+ vec![0x02, 0xEF, 0xBE, 0xAD, 0xDE, 0xBE, 0xBA, 0xFE, 0xCA];
+ decode_vec_multi_byte_length_prefix, two_fifty_six_elements(), two_fifty_six_elements_encoded();
+}
+
+#[test]
+#[cfg(feature = "alloc")]
+fn vec_decoder_one_item_plus_more_data() {
+ // One u32 plus some other bytes.
+ let encoded = vec![0x01, 0xEF, 0xBE, 0xAD, 0xDE, 0xff, 0xff, 0xff, 0xff];
+
+ let mut slice = encoded.as_slice();
+
+ let mut decoder = Test::decoder();
+ decoder.push_bytes(&mut slice).unwrap();
+
+ let got = decoder.end().unwrap();
+ let want = Test(vec![Inner(0xDEAD_BEEF)]);
+
+ assert_eq!(got, want);
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn decode_vec_from_read_unbuffered_success() {
+ let encoded = [0x01, 0xEF, 0xBE, 0xAD, 0xDE, 0xff, 0xff, 0xff, 0xff];
+ let mut cursor = Cursor::new(&encoded);
+
+ let got = bitcoin_consensus_encoding::decode_from_read_unbuffered::<Test, _>(&mut cursor).unwrap();
+ assert_eq!(cursor.position(), 5);
+
+ let want = Test(vec![Inner(0xDEAD_BEEF)]);
+ assert_eq!(got, want);
+}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.