units: Extend test coverage for parse_int module
What changed, and why it matters
This commit only adds new test cases to the rust-bitcoin library. It does not change any production code, fix bugs, or alter behavior. The tests cover edge cases for parsing numbers from strings, including uppercase hex prefixes and overflow cases. There is no security issue here.
No action required. This is a test-only change with no security relevance.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit extends unit tests in units/src/parse_int.rs, units/src/locktime/absolute/mod.rs, and units/src/weight.rs. It adds test coverage for hex parsing with uppercase ‘0X’ prefixes, overflow behavior in unchecked hex parsers, and TryFrom implementations for Height and Weight from String and Box
Changed components
units/src/parse_int.rsunits/src/locktime/absolute/mod.rsunits/src/weight.rsInspect captured patch +111 / −1
diff --git a/units/src/locktime/absolute/mod.rs b/units/src/locktime/absolute/mod.rs
index 06b39a13..d593c152 100644
--- a/units/src/locktime/absolute/mod.rs
+++ b/units/src/locktime/absolute/mod.rs
@@ -788,7 +788,7 @@ impl<'a> Arbitrary<'a> for MedianTimePast {
#[cfg(test)]
mod tests {
#[cfg(feature = "alloc")]
- use alloc::format;
+ use alloc::{boxed::Box, format, string::String};
use super::*;
@@ -973,6 +973,39 @@ mod tests {
assert!(result.is_err());
}
+ #[test]
+ fn height_try_from_stringlike_happy_path() {
+ let want = Height::from_u32(10).unwrap();
+ assert_eq!("10".parse::<Height>().unwrap(), want);
+ assert_eq!(Height::try_from("10").unwrap(), want);
+ #[cfg(feature = "alloc")] {
+ assert_eq!(Height::try_from(String::from("10")).unwrap(), want);
+ assert_eq!(Height::try_from(Box::<str>::from("10")).unwrap(), want);
+ }
+ }
+
+ #[test]
+ fn height_try_from_stringlike_hex_error_path() {
+ // Only base-10 values should parse
+ assert!("0xab".parse::<Height>().is_err());
+ assert!(Height::try_from("0xab").is_err());
+ #[cfg(feature = "alloc")] {
+ assert!(Height::try_from(String::from("0xab")).is_err());
+ assert!(Height::try_from(Box::<str>::from("0xab")).is_err());
+ }
+ }
+
+ #[test]
+ fn height_try_from_stringlike_decimal_error_path() {
+ // Only integers should parse
+ assert!("10.123".parse::<Height>().is_err());
+ assert!(Height::try_from("10.123").is_err());
+ #[cfg(feature = "alloc")] {
+ assert!(Height::try_from(String::from("10.123")).is_err());
+ assert!(Height::try_from(Box::<str>::from("10.123")).is_err());
+ }
+ }
+
#[test]
fn is_block_height_or_time() {
assert!(is_block_height(499_999_999));
diff --git a/units/src/parse_int.rs b/units/src/parse_int.rs
index 1c4591fe..2bfd6a33 100644
--- a/units/src/parse_int.rs
+++ b/units/src/parse_int.rs
@@ -591,6 +591,13 @@ mod tests {
assert_eq!(got, want);
}
+ #[test]
+ fn parse_u32_from_hex_prefixed_upper() {
+ let want = 171;
+ let got = hex_u32("0XAB").expect("failed to parse prefixed hex");
+ assert_eq!(got, want);
+ }
+
#[test]
fn parse_u32_from_hex_no_prefix() {
let want = 171;
@@ -605,6 +612,13 @@ mod tests {
assert!(hex_u32_unprefixed("0xab").is_err());
}
+ #[test]
+ fn parse_hex_u32_upper_prefixed() {
+ let want = 171; // 0xab
+ assert_eq!(hex_u32_prefixed("0Xab").unwrap(), want);
+ assert!(hex_u32_unprefixed("0Xab").is_err());
+ }
+
#[test]
fn parse_hex_u32_unprefixed() {
let want = 171; // 0xab
@@ -619,6 +633,13 @@ mod tests {
assert_eq!(got, want);
}
+ #[test]
+ fn parse_u128_from_hex_upper_prefixed() {
+ let want = 3_735_928_559;
+ let got = hex_u128("0Xdeadbeef").expect("failed to parse prefixed hex");
+ assert_eq!(got, want);
+ }
+
#[test]
fn parse_u128_from_hex_no_prefix() {
let want = 3_735_928_559;
@@ -633,6 +654,13 @@ mod tests {
assert!(hex_u128_unprefixed("0xdeadbeef").is_err());
}
+ #[test]
+ fn parse_hex_u128_upper_prefixed() {
+ let want = 3_735_928_559;
+ assert_eq!(hex_u128_prefixed("0Xdeadbeef").unwrap(), want);
+ assert!(hex_u128_unprefixed("0Xdeadbeef").is_err());
+ }
+
#[test]
fn parse_hex_u128_unprefixed() {
let want = 3_735_928_559;
@@ -643,5 +671,24 @@ mod tests {
#[test]
fn parse_u32_from_hex_unchecked_errors_on_prefix() {
assert!(hex_u32_unchecked("0xab").is_err());
+ assert!(hex_u32_unchecked("0Xab").is_err());
+ }
+
+ #[test]
+ fn parse_u32_from_hex_unchecked_errors_on_overflow() {
+ assert!(hex_u32_unchecked("1234abcd").is_ok());
+ assert!(hex_u32_unchecked("1234abcd1").is_err());
+ }
+
+ #[test]
+ fn parse_u128_from_hex_unchecked_errors_on_prefix() {
+ assert!(hex_u128_unchecked("0xdeadbeef").is_err());
+ assert!(hex_u128_unchecked("0Xdeadbeef").is_err());
+ }
+
+ #[test]
+ fn parse_u128_from_hex_unchecked_errors_on_overflow() {
+ assert!(hex_u128_unchecked("deadbeefabcdffffdeadbeefabcdffff").is_ok());
+ assert!(hex_u128_unchecked("deadbeefabcdffffdeadbeefabcdffff1").is_err());
}
}
diff --git a/units/src/weight.rs b/units/src/weight.rs
index e701a669..558d8339 100644
--- a/units/src/weight.rs
+++ b/units/src/weight.rs
@@ -387,6 +387,36 @@ mod tests {
assert_eq!(got, want);
}
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn try_from_string() {
+ let weight_value: alloc::string::String = "10".into();
+ let got = Weight::try_from(weight_value).unwrap();
+ let want = Weight::from_wu(10);
+ assert_eq!(got, want);
+
+ // Only base-10 integers should parse
+ let weight_value: alloc::string::String = "0xab".into();
+ assert!(Weight::try_from(weight_value).is_err());
+ let weight_value: alloc::string::String = "10.123".into();
+ assert!(Weight::try_from(weight_value).is_err());
+ }
+
+ #[test]
+ #[cfg(feature = "alloc")]
+ fn try_from_box() {
+ let weight_value: alloc::boxed::Box<str> = "10".into();
+ let got = Weight::try_from(weight_value).unwrap();
+ let want = Weight::from_wu(10);
+ assert_eq!(got, want);
+
+ // Only base-10 integers should parse
+ let weight_value: alloc::boxed::Box<str> = "0xab".into();
+ assert!(Weight::try_from(weight_value).is_err());
+ let weight_value: alloc::boxed::Box<str> = "10.123".into();
+ assert!(Weight::try_from(weight_value).is_err());
+ }
+
#[test]
fn to_kwu_floor() {
assert_eq!(Weight::from_wu(5_000).to_kwu_floor(), 5);
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.