Files
bitcoin/src/test/amount_tests.cpp
rustaceanrob e8691056c0 test: Unroll && conditions in macros
Using `&&` in `BOOST_CHECK` is problematic as failures will not indicate
which condition failed. By unrolling these checks, the user knows
exactly which expression is the failing case.

As an example, here is a line that would be particularly hard to debug
if it failed:

```
src/test/net_tests.cpp

BOOST_CHECK((*ret)[1] && (*ret)[1]->m_type == "headers" && std::ranges::equal((*ret)[1]->m_recv, MakeByteSpan(msg_data_2)));
```

If any one of these conditions fail, the whole expression fails, with no
values printed or indication as to which condition failed.

This is also required when using test macros that support value
decomposition, which requires `&&` and `||` are `delete`. Examples
include `BOOST_TEST`, doctest, Catch2, etc.

ref: https://catch2-temp.readthedocs.io/en/latest/assertions.html#other-limitations
ref: https://fekir.info/post/decomposing-an-expression/
2026-08-03 11:10:13 +01:00

150 lines
5.7 KiB
C++

// Copyright (c) 2016-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/amount.h>
#include <policy/feerate.h>
#include <limits>
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(amount_tests)
BOOST_AUTO_TEST_CASE(MoneyRangeTest)
{
BOOST_CHECK_EQUAL(MoneyRange(CAmount(-1)), false);
BOOST_CHECK_EQUAL(MoneyRange(CAmount(0)), true);
BOOST_CHECK_EQUAL(MoneyRange(CAmount(1)), true);
BOOST_CHECK_EQUAL(MoneyRange(MAX_MONEY), true);
BOOST_CHECK_EQUAL(MoneyRange(MAX_MONEY + CAmount(1)), false);
}
BOOST_AUTO_TEST_CASE(GetFeeTest)
{
CFeeRate feeRate, altFeeRate;
feeRate = CFeeRate(0);
// Must always return 0
BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
BOOST_CHECK_EQUAL(feeRate.GetFee(1e5), CAmount(0));
feeRate = CFeeRate(1000);
// Must always just return the arg
BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
BOOST_CHECK_EQUAL(feeRate.GetFee(1), CAmount(1));
BOOST_CHECK_EQUAL(feeRate.GetFee(121), CAmount(121));
BOOST_CHECK_EQUAL(feeRate.GetFee(999), CAmount(999));
BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), CAmount(1e3));
BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), CAmount(9e3));
feeRate = CFeeRate(-1000);
// Must always just return -1 * arg
BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
BOOST_CHECK_EQUAL(feeRate.GetFee(1), CAmount(-1));
BOOST_CHECK_EQUAL(feeRate.GetFee(121), CAmount(-121));
BOOST_CHECK_EQUAL(feeRate.GetFee(999), CAmount(-999));
BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), CAmount(-1e3));
BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), CAmount(-9e3));
feeRate = CFeeRate(123);
// Rounds up the result, if not integer
BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
BOOST_CHECK_EQUAL(feeRate.GetFee(8), CAmount(1)); // Special case: returns 1 instead of 0
BOOST_CHECK_EQUAL(feeRate.GetFee(9), CAmount(2));
BOOST_CHECK_EQUAL(feeRate.GetFee(121), CAmount(15));
BOOST_CHECK_EQUAL(feeRate.GetFee(122), CAmount(16));
BOOST_CHECK_EQUAL(feeRate.GetFee(999), CAmount(123));
BOOST_CHECK_EQUAL(feeRate.GetFee(1e3), CAmount(123));
BOOST_CHECK_EQUAL(feeRate.GetFee(9e3), CAmount(1107));
feeRate = CFeeRate(-123);
// Truncates the result, if not integer
BOOST_CHECK_EQUAL(feeRate.GetFee(0), CAmount(0));
BOOST_CHECK_EQUAL(feeRate.GetFee(8), CAmount(-1)); // Special case: returns -1 instead of 0
BOOST_CHECK_EQUAL(feeRate.GetFee(9), CAmount(-1));
// check alternate constructor
feeRate = CFeeRate(1000);
altFeeRate = CFeeRate(feeRate);
BOOST_CHECK_EQUAL(feeRate.GetFee(100), altFeeRate.GetFee(100));
// Check full constructor
BOOST_CHECK(CFeeRate(CAmount(-1), 0) == CFeeRate(0));
BOOST_CHECK(CFeeRate(CAmount(0), 0) == CFeeRate(0));
BOOST_CHECK(CFeeRate(CAmount(1), 0) == CFeeRate(0));
BOOST_CHECK(CFeeRate(CAmount(1), -1000) == CFeeRate(0));
// default value
BOOST_CHECK(CFeeRate(CAmount(-1), 1000) == CFeeRate(-1));
BOOST_CHECK(CFeeRate(CAmount(0), 1000) == CFeeRate(0));
BOOST_CHECK(CFeeRate(CAmount(1), 1000) == CFeeRate(1));
// Previously, precision was limited to three decimal digits
// due to only supporting satoshis per kB, so CFeeRate(CAmount(1), 1001) was equal to CFeeRate(0)
// Since #32750, higher precision is maintained.
BOOST_CHECK(CFeeRate(CAmount(1), 1001) > CFeeRate(0));
BOOST_CHECK(CFeeRate(CAmount(1), 1001) < CFeeRate(1));
BOOST_CHECK(CFeeRate(CAmount(2), 1001) > CFeeRate(1));
BOOST_CHECK(CFeeRate(CAmount(2), 1001) < CFeeRate(2));
// some more integer checks
BOOST_CHECK(CFeeRate(CAmount(26), 789) > CFeeRate(32));
BOOST_CHECK(CFeeRate(CAmount(26), 789) < CFeeRate(33));
BOOST_CHECK(CFeeRate(CAmount(27), 789) > CFeeRate(34));
BOOST_CHECK(CFeeRate(CAmount(27), 789) < CFeeRate(35));
// Maximum size in bytes, should not crash
CFeeRate(MAX_MONEY, std::numeric_limits<int32_t>::max()).GetFeePerK();
// check multiplication operator
// check multiplying by zero
feeRate = CFeeRate(1000);
BOOST_CHECK(0 * feeRate == CFeeRate(0));
BOOST_CHECK(feeRate * 0 == CFeeRate(0));
// check multiplying by a positive integer
BOOST_CHECK(3 * feeRate == CFeeRate(3000));
BOOST_CHECK(feeRate * 3 == CFeeRate(3000));
// check multiplying by a negative integer
BOOST_CHECK(-3 * feeRate == CFeeRate(-3000));
BOOST_CHECK(feeRate * -3 == CFeeRate(-3000));
// check commutativity
BOOST_CHECK(2 * feeRate == feeRate * 2);
// check with large numbers
int largeNumber = 1000000;
BOOST_CHECK(largeNumber * feeRate == feeRate * largeNumber);
// check boundary values
int maxInt = std::numeric_limits<int>::max();
feeRate = CFeeRate(maxInt);
BOOST_CHECK(feeRate * 2 == CFeeRate(static_cast<int64_t>(maxInt) * 2));
BOOST_CHECK(2 * feeRate == CFeeRate(static_cast<int64_t>(maxInt) * 2));
// check with zero fee rate
feeRate = CFeeRate(0);
BOOST_CHECK(feeRate * 5 == CFeeRate(0));
BOOST_CHECK(5 * feeRate == CFeeRate(0));
}
BOOST_AUTO_TEST_CASE(BinaryOperatorTest)
{
CFeeRate a, b;
a = CFeeRate(1);
b = CFeeRate(2);
BOOST_CHECK(a < b);
BOOST_CHECK(b > a);
BOOST_CHECK(a == a);
BOOST_CHECK(a <= b);
BOOST_CHECK(a <= a);
BOOST_CHECK(b >= a);
BOOST_CHECK(b >= b);
// a should be 0.00000002 BTC/kvB now
a += a;
BOOST_CHECK(a == b);
}
BOOST_AUTO_TEST_CASE(ToStringTest)
{
CFeeRate feeRate;
feeRate = CFeeRate(1);
BOOST_CHECK_EQUAL(feeRate.ToString(), "0.00000001 BTC/kvB");
BOOST_CHECK_EQUAL(feeRate.ToString(FeeRateFormat::BTC_KVB), "0.00000001 BTC/kvB");
BOOST_CHECK_EQUAL(feeRate.ToString(FeeRateFormat::SAT_VB), "0.001 sat/vB");
}
BOOST_AUTO_TEST_SUITE_END()