pow: Add more mutation testing

Recently we introduced some mutation testing to the `pow` module but
testing is never done - add more `mutate` attributes and add unit tests
to ensure all mutants are killed.

Of note, the `from_compact` and `to_compact_lossy` functions are not
done, doing so results in a bunch of surviving mutants.
This commit is contained in:
Tobin C. Harding 2022-12-29 14:29:04 +11:00
parent 948b04927d
commit 308c727c82
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
1 changed files with 86 additions and 0 deletions

View File

@ -224,6 +224,7 @@ impl Target {
///
/// Proof-of-work validity for a block requires the hash of the block to be less than or equal
/// to the target.
#[cfg_attr(all(test, mutate), mutate)]
pub fn is_met_by(&self, hash: BlockHash) -> bool {
use crate::hashes::Hash;
let hash = U256::from_le_bytes(hash.into_inner());
@ -257,6 +258,7 @@ impl Target {
///
/// [max]: Target::max
/// [target]: crate::blockdata::block::Header::target
#[cfg_attr(all(test, mutate), mutate)]
pub fn difficulty(&self) -> u128 {
let d = Target::MAX.0 / self.0;
d.saturating_to_u128()
@ -390,6 +392,7 @@ impl U256 {
#[cfg_attr(all(test, mutate), mutate)]
fn is_one(&self) -> bool { self.0 == 0 && self.1 == 1 }
#[cfg_attr(all(test, mutate), mutate)]
fn is_max(&self) -> bool { self.0 == u128::max_value() && self.1 == u128::max_value() }
/// Returns the low 32 bits.
@ -402,6 +405,7 @@ impl U256 {
fn low_u128(&self) -> u128 { self.1 }
/// Returns `self` as a `u128` saturating to `u128::MAX` if `self` is too big.
// Matagen gives false positive because >= and > both return u128::MAX
fn saturating_to_u128(&self) -> u128 {
if *self > U256::from(u128::max_value()) {
u128::max_value()
@ -411,6 +415,7 @@ impl U256 {
}
/// Returns the least number of bits needed to represent the number.
#[cfg_attr(all(test, mutate), mutate)]
fn bits(&self) -> u32 {
if self.0 > 0 {
256 - self.0.leading_zeros()
@ -425,6 +430,7 @@ impl U256 {
///
/// The multiplication result along with a boolean indicating whether an arithmetic overflow
/// occurred. If an overflow occurred then the wrapped value is returned.
// mutagen false positive: binop_bit, replace `|` with `^`
fn mul_u64(self, rhs: u64) -> (U256, bool) {
let mut carry: u128 = 0;
let mut split_le = [self.1 as u64, (self.1 >> 64) as u64, self.0 as u64, (self.0 >> 64) as u64];
@ -452,6 +458,7 @@ impl U256 {
/// # Panics
///
/// If `rhs` is zero.
#[cfg_attr(all(test, mutate), mutate)]
fn div_rem(self, rhs: Self) -> (Self, Self) {
let mut sub_copy = self;
let mut shift_copy = rhs;
@ -491,6 +498,7 @@ impl U256 {
/// Returns a tuple of the addition along with a boolean indicating whether an arithmetic
/// overflow would occur. If an overflow would have occurred then the wrapped value is returned.
#[must_use = "this returns the result of the operation, without modifying the original"]
#[cfg_attr(all(test, mutate), mutate)]
fn overflowing_add(self, rhs: Self) -> (Self, bool) {
let mut ret = U256::ZERO;
let mut ret_overflow = false;
@ -515,6 +523,7 @@ impl U256 {
/// Returns a tuple of the subtraction along with a boolean indicating whether an arithmetic
/// overflow would occur. If an overflow would have occurred then the wrapped value is returned.
#[must_use = "this returns the result of the operation, without modifying the original"]
#[cfg_attr(all(test, mutate), mutate)]
fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
let ret = self.wrapping_add(!rhs).wrapping_add(Self::ONE);
let overflow = rhs > self;
@ -527,6 +536,7 @@ impl U256 {
/// indicating whether an arithmetic overflow would occur. If an
/// overflow would have occurred then the wrapped value is returned.
#[must_use = "this returns the result of the operation, without modifying the original"]
#[cfg_attr(all(test, mutate), mutate)]
fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
let mut ret = U256::ZERO;
let mut ret_overflow = false;
@ -594,6 +604,7 @@ impl U256 {
/// restricted to the range of the type, rather than the bits shifted out of the LHS being
/// returned to the other end. We do not currently support `rotate_left`.
#[must_use = "this returns the result of the operation, without modifying the original"]
#[cfg_attr(all(test, mutate), mutate)]
fn wrapping_shl(self, rhs: u32) -> Self {
let shift = rhs & 0x000000ff;
@ -620,6 +631,7 @@ impl U256 {
/// restricted to the range of the type, rather than the bits shifted out of the LHS being
/// returned to the other end. We do not currently support `rotate_right`.
#[must_use = "this returns the result of the operation, without modifying the original"]
#[cfg_attr(all(test, mutate), mutate)]
fn wrapping_shr(self, rhs: u32) -> Self {
let shift = rhs & 0x000000ff;
@ -942,6 +954,9 @@ mod tests {
assert_eq!(U256::from(60000_u64).bits(), 16);
assert_eq!(U256::from(70000_u64).bits(), 17);
let u = U256::from(u128::max_value()) << 1;
assert_eq!(u.bits(), 129);
// Try to read the following lines out loud quickly
let mut shl = U256::from(70000_u64);
shl = shl << 100;
@ -1199,7 +1214,11 @@ mod tests {
assert_eq!(u << 1, U256::from(2_u64));
assert_eq!(u << 63, U256::from(0x8000_0000_0000_0000_u64));
assert_eq!(u << 64, U256::from_array([0, 0, 0x0000_0000_0000_0001, 0]));
assert_eq!(u << 127, U256(0, 0x8000_0000_0000_0000_0000_0000_0000_0000));
assert_eq!(u << 128, U256(1, 0));
let x = U256(0, 0x8000_0000_0000_0000_0000_0000_0000_0000);
assert_eq!(x << 1, U256(1, 0));
}
#[test]
@ -1207,6 +1226,7 @@ mod tests {
let u = U256(1, 0);
assert_eq!(u >> 0, u);
assert_eq!(u >> 1, U256(0, 0x8000_0000_0000_0000_0000_0000_0000_0000));
assert_eq!(u >> 127, U256(0, 2));
assert_eq!(u >> 128, U256(0, 1));
}
@ -1319,6 +1339,29 @@ mod tests {
);
}
#[test]
fn u256_addition() {
let x = U256::from(u128::max_value());
let (add, overflow) = x.overflowing_add(U256::ONE);
assert!(!overflow);
assert_eq!(add, U256(1, 0));
let (add, _) = add.overflowing_add(U256::ONE);
assert_eq!(add, U256(1, 1));
}
#[test]
fn u256_subtraction() {
let (sub, overflow) = U256::ONE.overflowing_sub(U256::ONE);
assert!(!overflow);
assert_eq!(sub, U256::ZERO);
let x = U256(1, 0);
let (sub, overflow) = x.overflowing_sub(U256::ONE);
assert!(!overflow);
assert_eq!(sub, U256::from(u128::max_value()));
}
#[test]
fn u256_multiplication() {
let u64_val = U256::from(0xDEAD_BEEF_DEAD_BEEF_u64);
@ -1338,6 +1381,23 @@ mod tests {
);
}
#[test]
fn u256_multiplication_bits_in_each_word() {
// Put a digit in the least significant bit of each 64 bit word.
let u = 1_u128 << 64 | 1_u128;
let x = U256(u, u);
// Put a digit in the second least significant bit of each 64 bit word.
let u = 2_u128 << 64 | 2_u128;
let y = U256(u, u);
let (got, overflow) = x.overflowing_mul(y);
let want = U256(0x0000_0000_0000_0008_0000_0000_0000_0008, 0x0000_0000_0000_0006_0000_0000_0000_0004);
assert!(!overflow);
assert_eq!(got, want)
}
#[test]
fn u256_increment() {
let mut val = U256(
@ -1427,6 +1487,22 @@ mod tests {
.is_err()); // invalid length
}
#[test]
fn u256_is_max_correct_negative() {
let tc = vec![U256::ZERO, U256::ONE, U256::from(u128::max_value())];
for t in tc {
assert!(!t.is_max())
}
}
#[test]
fn u256_is_max_correct_positive() {
assert!(U256::MAX.is_max());
let u = u128::max_value();
assert!(((U256::from(u) << 128) + U256::from(u)).is_max());
}
#[test]
fn compact_target_from_hex_str_happy_path() {
let actual = CompactTarget::from_hex_str("0x01003456").unwrap();
@ -1467,6 +1543,16 @@ mod tests {
}
}
#[test]
fn target_is_met_by_for_target_equals_hash() {
use std::str::FromStr;
use crate::hashes::Hash;
let hash = BlockHash::from_str("ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c").expect("failed to parse block hash");
let target = Target(U256::from_le_bytes(hash.into_inner()));
assert!(target.is_met_by(hash));
}
#[test]
fn max_target_from_compact() {
// The highest possible target is defined as 0x1d00ffff