Merge rust-bitcoin/rust-bitcoin#1513: Add mutation testing to the `locktime` module
ca471557a5
locktime: Add mutation testing (Tobin C. Harding)26c0da41b4
locktime: Add inline to public functions (Tobin C. Harding) Pull request description: Add mutation testing to the `locktime` module and add unit tests to cover all mutants and ensure they are killed. ACKs for top commit: sanket1729: ACKca471557a5
. How stable are the mutation tests? apoelstra: ACKca471557a5
Tree-SHA512: 46a59c90fc25b0c803e96f7c5b98bd39055f7835e45ba137b2a01ad4221a676c54bc228b9ef7663b7300bb4260a6c2c80a0820c4f1bf0987650e1e2bd699f62d
This commit is contained in:
commit
3e4a299615
|
@ -14,6 +14,9 @@ use core::str::FromStr;
|
||||||
|
|
||||||
use bitcoin_internals::write_err;
|
use bitcoin_internals::write_err;
|
||||||
|
|
||||||
|
#[cfg(all(test, mutate))]
|
||||||
|
use mutagen::mutate;
|
||||||
|
|
||||||
use crate::consensus::encode::{self, Decodable, Encodable};
|
use crate::consensus::encode::{self, Decodable, Encodable};
|
||||||
use crate::error::ParseIntError;
|
use crate::error::ParseIntError;
|
||||||
use crate::io::{self, Read, Write};
|
use crate::io::{self, Read, Write};
|
||||||
|
@ -198,6 +201,7 @@ impl LockTime {
|
||||||
/// }
|
/// }
|
||||||
/// ````
|
/// ````
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[cfg_attr(all(test, mutate), mutate)]
|
||||||
pub fn is_satisfied_by(&self, height: Height, time: Time) -> bool {
|
pub fn is_satisfied_by(&self, height: Height, time: Time) -> bool {
|
||||||
use LockTime::*;
|
use LockTime::*;
|
||||||
|
|
||||||
|
@ -226,6 +230,8 @@ impl LockTime {
|
||||||
/// let check = LockTime::from_consensus(100 + 1);
|
/// let check = LockTime::from_consensus(100 + 1);
|
||||||
/// assert!(lock_time.is_implied_by(check));
|
/// assert!(lock_time.is_implied_by(check));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
#[cfg_attr(all(test, mutate), mutate)]
|
||||||
pub fn is_implied_by(&self, other: LockTime) -> bool {
|
pub fn is_implied_by(&self, other: LockTime) -> bool {
|
||||||
use LockTime::*;
|
use LockTime::*;
|
||||||
|
|
||||||
|
@ -273,18 +279,21 @@ impl LockTime {
|
||||||
impl_parse_str_through_int!(LockTime, from_consensus);
|
impl_parse_str_through_int!(LockTime, from_consensus);
|
||||||
|
|
||||||
impl From<Height> for LockTime {
|
impl From<Height> for LockTime {
|
||||||
|
#[inline]
|
||||||
fn from(h: Height) -> Self {
|
fn from(h: Height) -> Self {
|
||||||
LockTime::Blocks(h)
|
LockTime::Blocks(h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Time> for LockTime {
|
impl From<Time> for LockTime {
|
||||||
|
#[inline]
|
||||||
fn from(t: Time) -> Self {
|
fn from(t: Time) -> Self {
|
||||||
LockTime::Seconds(t)
|
LockTime::Seconds(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for LockTime {
|
impl PartialOrd for LockTime {
|
||||||
|
#[inline]
|
||||||
fn partial_cmp(&self, other: &LockTime) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &LockTime) -> Option<Ordering> {
|
||||||
use LockTime::*;
|
use LockTime::*;
|
||||||
|
|
||||||
|
@ -317,6 +326,7 @@ impl fmt::Display for LockTime {
|
||||||
impl FromHexStr for LockTime {
|
impl FromHexStr for LockTime {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
|
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
|
||||||
let packed_lock_time = crate::parse::hex_u32(s)?;
|
let packed_lock_time = crate::parse::hex_u32(s)?;
|
||||||
Ok(Self::from_consensus(packed_lock_time))
|
Ok(Self::from_consensus(packed_lock_time))
|
||||||
|
@ -431,6 +441,7 @@ impl fmt::Display for Height {
|
||||||
impl FromHexStr for Height {
|
impl FromHexStr for Height {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
|
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
|
||||||
let height = crate::parse::hex_u32(s)?;
|
let height = crate::parse::hex_u32(s)?;
|
||||||
Self::from_consensus(height)
|
Self::from_consensus(height)
|
||||||
|
@ -441,6 +452,7 @@ impl FromHexStr for Height {
|
||||||
impl FromStr for Height {
|
impl FromStr for Height {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let n = parse::int(s)?;
|
let n = parse::int(s)?;
|
||||||
Height::from_consensus(n)
|
Height::from_consensus(n)
|
||||||
|
@ -450,6 +462,7 @@ impl FromStr for Height {
|
||||||
impl TryFrom<&str> for Height {
|
impl TryFrom<&str> for Height {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
let n = parse::int(s)?;
|
let n = parse::int(s)?;
|
||||||
Height::from_consensus(n)
|
Height::from_consensus(n)
|
||||||
|
@ -459,6 +472,7 @@ impl TryFrom<&str> for Height {
|
||||||
impl TryFrom<String> for Height {
|
impl TryFrom<String> for Height {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
let n = parse::int(s)?;
|
let n = parse::int(s)?;
|
||||||
Height::from_consensus(n)
|
Height::from_consensus(n)
|
||||||
|
@ -524,6 +538,7 @@ impl fmt::Display for Time {
|
||||||
impl FromHexStr for Time {
|
impl FromHexStr for Time {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
|
fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Self::Error> {
|
||||||
let time = crate::parse::hex_u32(s)?;
|
let time = crate::parse::hex_u32(s)?;
|
||||||
Time::from_consensus(time)
|
Time::from_consensus(time)
|
||||||
|
@ -533,6 +548,7 @@ impl FromHexStr for Time {
|
||||||
impl FromStr for Time {
|
impl FromStr for Time {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let n = parse::int(s)?;
|
let n = parse::int(s)?;
|
||||||
Time::from_consensus(n)
|
Time::from_consensus(n)
|
||||||
|
@ -542,6 +558,7 @@ impl FromStr for Time {
|
||||||
impl TryFrom<&str> for Time {
|
impl TryFrom<&str> for Time {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
let n = parse::int(s)?;
|
let n = parse::int(s)?;
|
||||||
Time::from_consensus(n)
|
Time::from_consensus(n)
|
||||||
|
@ -551,6 +568,7 @@ impl TryFrom<&str> for Time {
|
||||||
impl TryFrom<String> for Time {
|
impl TryFrom<String> for Time {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
let n = parse::int(s)?;
|
let n = parse::int(s)?;
|
||||||
Time::from_consensus(n)
|
Time::from_consensus(n)
|
||||||
|
@ -606,18 +624,21 @@ impl std::error::Error for Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ConversionError> for Error {
|
impl From<ConversionError> for Error {
|
||||||
|
#[inline]
|
||||||
fn from(e: ConversionError) -> Self {
|
fn from(e: ConversionError) -> Self {
|
||||||
Error::Conversion(e)
|
Error::Conversion(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OperationError> for Error {
|
impl From<OperationError> for Error {
|
||||||
|
#[inline]
|
||||||
fn from(e: OperationError) -> Self {
|
fn from(e: OperationError) -> Self {
|
||||||
Error::Operation(e)
|
Error::Operation(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseIntError> for Error {
|
impl From<ParseIntError> for Error {
|
||||||
|
#[inline]
|
||||||
fn from(e: ParseIntError) -> Self {
|
fn from(e: ParseIntError) -> Self {
|
||||||
Error::Parse(e)
|
Error::Parse(e)
|
||||||
}
|
}
|
||||||
|
@ -775,4 +796,90 @@ mod tests {
|
||||||
let result = Height::from_hex_str(hex);
|
let result = Height::from_hex_str(hex);
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_correctly_to_height_or_time() {
|
||||||
|
let lock = LockTime::from_consensus(750_000);
|
||||||
|
|
||||||
|
assert!(lock.is_block_height());
|
||||||
|
assert!(!lock.is_block_time());
|
||||||
|
|
||||||
|
let t: u32 = 1653195600; // May 22nd, 5am UTC.
|
||||||
|
let lock = LockTime::from_consensus(t);
|
||||||
|
|
||||||
|
assert!(!lock.is_block_height());
|
||||||
|
assert!(lock.is_block_time());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn satisfied_by_height() {
|
||||||
|
let lock = LockTime::from_consensus(750_000);
|
||||||
|
|
||||||
|
let height = Height::from_consensus(800_000).expect("failed to parse height");
|
||||||
|
|
||||||
|
let t: u32 = 1653195600; // May 22nd, 5am UTC.
|
||||||
|
let time = Time::from_consensus(t).expect("invalid time value");
|
||||||
|
|
||||||
|
assert!(lock.is_satisfied_by(height, time))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn satisfied_by_time() {
|
||||||
|
let lock = LockTime::from_consensus(1053195600);
|
||||||
|
|
||||||
|
let t: u32 = 1653195600; // May 22nd, 5am UTC.
|
||||||
|
let time = Time::from_consensus(t).expect("invalid time value");
|
||||||
|
|
||||||
|
let height = Height::from_consensus(800_000).expect("failed to parse height");
|
||||||
|
|
||||||
|
assert!(lock.is_satisfied_by(height, time))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn satisfied_by_same_height() {
|
||||||
|
let h = 750_000;
|
||||||
|
let lock = LockTime::from_consensus(h);
|
||||||
|
let height = Height::from_consensus(h).expect("failed to parse height");
|
||||||
|
|
||||||
|
let t: u32 = 1653195600; // May 22nd, 5am UTC.
|
||||||
|
let time = Time::from_consensus(t).expect("invalid time value");
|
||||||
|
|
||||||
|
assert!(lock.is_satisfied_by(height, time))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn satisfied_by_same_time() {
|
||||||
|
let t: u32 = 1653195600; // May 22nd, 5am UTC.
|
||||||
|
let lock = LockTime::from_consensus(t);
|
||||||
|
let time = Time::from_consensus(t).expect("invalid time value");
|
||||||
|
|
||||||
|
let height = Height::from_consensus(800_000).expect("failed to parse height");
|
||||||
|
|
||||||
|
assert!(lock.is_satisfied_by(height, time))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn height_correctly_implies() {
|
||||||
|
let lock = LockTime::from_consensus(750_005);
|
||||||
|
|
||||||
|
assert!(!lock.is_implied_by(LockTime::from_consensus(750_004)));
|
||||||
|
assert!(lock.is_implied_by(LockTime::from_consensus(750_005)));
|
||||||
|
assert!(lock.is_implied_by(LockTime::from_consensus(750_006)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn time_correctly_implies() {
|
||||||
|
let t: u32 = 1700000005;
|
||||||
|
let lock = LockTime::from_consensus(t);
|
||||||
|
|
||||||
|
assert!(!lock.is_implied_by(LockTime::from_consensus(1700000004)));
|
||||||
|
assert!(lock.is_implied_by(LockTime::from_consensus(1700000005)));
|
||||||
|
assert!(lock.is_implied_by(LockTime::from_consensus(1700000006)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incorrect_units_do_not_imply() {
|
||||||
|
let lock = LockTime::from_consensus(750_005);
|
||||||
|
assert!(!lock.is_implied_by(LockTime::from_consensus(1700000004)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
|
#[cfg(all(test, mutate))]
|
||||||
|
use mutagen::mutate;
|
||||||
|
|
||||||
#[cfg(docsrs)]
|
#[cfg(docsrs)]
|
||||||
use crate::relative;
|
use crate::relative;
|
||||||
|
|
||||||
|
@ -52,6 +55,8 @@ impl LockTime {
|
||||||
/// let height_and_time = (current_time(), current_height()); // tuple order does not matter.
|
/// let height_and_time = (current_time(), current_height()); // tuple order does not matter.
|
||||||
/// assert!(lock.is_satisfied_by(current_height(), current_time()));
|
/// assert!(lock.is_satisfied_by(current_height(), current_time()));
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
#[cfg_attr(all(test, mutate), mutate)]
|
||||||
pub fn is_satisfied_by(&self, h: Height, t: Time) -> bool {
|
pub fn is_satisfied_by(&self, h: Height, t: Time) -> bool {
|
||||||
if let Ok(true) = self.is_satisfied_by_height(h) {
|
if let Ok(true) = self.is_satisfied_by_height(h) {
|
||||||
true
|
true
|
||||||
|
@ -92,6 +97,8 @@ impl LockTime {
|
||||||
/// };
|
/// };
|
||||||
/// assert!(satisfied);
|
/// assert!(satisfied);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
#[cfg_attr(all(test, mutate), mutate)]
|
||||||
pub fn is_implied_by(&self, other: LockTime) -> bool {
|
pub fn is_implied_by(&self, other: LockTime) -> bool {
|
||||||
use LockTime::*;
|
use LockTime::*;
|
||||||
|
|
||||||
|
@ -119,6 +126,7 @@ impl LockTime {
|
||||||
/// assert!(lock.is_satisfied_by_height(Height::from(height+1)).expect("a height"));
|
/// assert!(lock.is_satisfied_by_height(Height::from(height+1)).expect("a height"));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[cfg_attr(all(test, mutate), mutate)]
|
||||||
pub fn is_satisfied_by_height(&self, h: Height) -> Result<bool, Error> {
|
pub fn is_satisfied_by_height(&self, h: Height) -> Result<bool, Error> {
|
||||||
use LockTime::*;
|
use LockTime::*;
|
||||||
|
|
||||||
|
@ -145,6 +153,7 @@ impl LockTime {
|
||||||
/// assert!(lock.is_satisfied_by_time(Time::from_512_second_intervals(intervals + 10)).expect("a time"));
|
/// assert!(lock.is_satisfied_by_time(Time::from_512_second_intervals(intervals + 10)).expect("a time"));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[cfg_attr(all(test, mutate), mutate)]
|
||||||
pub fn is_satisfied_by_time(&self, t: Time) -> Result<bool, Error> {
|
pub fn is_satisfied_by_time(&self, t: Time) -> Result<bool, Error> {
|
||||||
use LockTime::*;
|
use LockTime::*;
|
||||||
|
|
||||||
|
@ -170,7 +179,6 @@ impl From<Time> for LockTime {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for LockTime {
|
impl fmt::Display for LockTime {
|
||||||
#[inline]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use LockTime::*;
|
use LockTime::*;
|
||||||
|
|
||||||
|
@ -210,7 +218,6 @@ impl From<u16> for Height {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Height {
|
impl fmt::Display for Height {
|
||||||
#[inline]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
fmt::Display::fmt(&self.0, f)
|
fmt::Display::fmt(&self.0, f)
|
||||||
}
|
}
|
||||||
|
@ -256,7 +263,6 @@ impl Time {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Time {
|
impl fmt::Display for Time {
|
||||||
#[inline]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
fmt::Display::fmt(&self.0, f)
|
fmt::Display::fmt(&self.0, f)
|
||||||
}
|
}
|
||||||
|
@ -295,3 +301,61 @@ impl std::error::Error for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn satisfied_by_height() {
|
||||||
|
let height = Height::from(10);
|
||||||
|
let time = Time::from_512_second_intervals(70);
|
||||||
|
|
||||||
|
let lock = LockTime::from(height);
|
||||||
|
|
||||||
|
assert!(!lock.is_satisfied_by(Height::from(9), time));
|
||||||
|
assert!(lock.is_satisfied_by(Height::from(10), time));
|
||||||
|
assert!(lock.is_satisfied_by(Height::from(11), time));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn satisfied_by_time() {
|
||||||
|
let height = Height::from(10);
|
||||||
|
let time = Time::from_512_second_intervals(70);
|
||||||
|
|
||||||
|
let lock = LockTime::from(time);
|
||||||
|
|
||||||
|
assert!(!lock.is_satisfied_by(height, Time::from_512_second_intervals(69)));
|
||||||
|
assert!(lock.is_satisfied_by(height, Time::from_512_second_intervals(70)));
|
||||||
|
assert!(lock.is_satisfied_by(height, Time::from_512_second_intervals(71)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn height_correctly_implies() {
|
||||||
|
let height = Height::from(10);
|
||||||
|
let lock = LockTime::from(height);
|
||||||
|
|
||||||
|
assert!(!lock.is_implied_by(LockTime::from(Height::from(9))));
|
||||||
|
assert!(lock.is_implied_by(LockTime::from(Height::from(10))));
|
||||||
|
assert!(lock.is_implied_by(LockTime::from(Height::from(11))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn time_correctly_implies() {
|
||||||
|
let time = Time::from_512_second_intervals(70);
|
||||||
|
let lock = LockTime::from(time);
|
||||||
|
|
||||||
|
assert!(!lock.is_implied_by(LockTime::from(Time::from_512_second_intervals(69))));
|
||||||
|
assert!(lock.is_implied_by(LockTime::from(Time::from_512_second_intervals(70))));
|
||||||
|
assert!(lock.is_implied_by(LockTime::from(Time::from_512_second_intervals(71))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incorrect_units_do_not_imply() {
|
||||||
|
let time = Time::from_512_second_intervals(70);
|
||||||
|
let height = Height::from(10);
|
||||||
|
|
||||||
|
let lock = LockTime::from(time);
|
||||||
|
assert!(!lock.is_implied_by(LockTime::from(height)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue