// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {DSTest} from "ds-test/test.sol"; import {Hevm} from "./Hevm.sol"; /// @notice Extended testing framework for DappTools projects. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/test/utils/DSTestPlus.sol) contract DSTestPlus is DSTest { Hevm internal constant hevm = Hevm(HEVM_ADDRESS); address internal constant DEAD_ADDRESS = 0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF; string private checkpointLabel; uint256 private checkpointGasLeft = 1; // Start the slot warm. modifier brutalizeMemory(bytes memory brutalizeWith) { /// @solidity memory-safe-assembly assembly { // Fill the 64 bytes of scratch space with the data. pop( staticcall( gas(), // Pass along all the gas in the call. 0x04, // Call the identity precompile address. brutalizeWith, // Offset is the bytes' pointer. 64, // Copy enough to only fill the scratch space. 0, // Store the return value in the scratch space. 64 // Scratch space is only 64 bytes in size, we don't want to write further. ) ) let size := add(mload(brutalizeWith), 32) // Add 32 to include the 32 byte length slot. // Fill the free memory pointer's destination with the data. pop( staticcall( gas(), // Pass along all the gas in the call. 0x04, // Call the identity precompile address. brutalizeWith, // Offset is the bytes' pointer. size, // We want to pass the length of the bytes. mload(0x40), // Store the return value at the free memory pointer. size // Since the precompile just returns its input, we reuse size. ) ) } _; } function startMeasuringGas(string memory label) internal virtual { checkpointLabel = label; checkpointGasLeft = gasleft(); } function stopMeasuringGas() internal virtual { uint256 checkpointGasLeft2 = gasleft(); // Subtract 100 to account for the warm SLOAD in startMeasuringGas. uint256 gasDelta = checkpointGasLeft - checkpointGasLeft2 - 100; emit log_named_uint(string(abi.encodePacked(checkpointLabel, " Gas")), gasDelta); } function fail(string memory err) internal virtual { emit log_named_string("Error", err); fail(); } function assertFalse(bool data) internal virtual { assertTrue(!data); } function assertUint128Eq(uint128 a, uint128 b) internal virtual { assertEq(uint256(a), uint256(b)); } function assertUint64Eq(uint64 a, uint64 b) internal virtual { assertEq(uint256(a), uint256(b)); } function assertUint96Eq(uint96 a, uint96 b) internal virtual { assertEq(uint256(a), uint256(b)); } function assertUint32Eq(uint32 a, uint32 b) internal virtual { assertEq(uint256(a), uint256(b)); } function assertBoolEq(bool a, bool b) internal virtual { b ? assertTrue(a) : assertFalse(a); } function assertApproxEq( uint256 a, uint256 b, uint256 maxDelta ) internal virtual { uint256 delta = a > b ? a - b : b - a; if (delta > maxDelta) { emit log("Error: a ~= b not satisfied [uint]"); emit log_named_uint(" Expected", b); emit log_named_uint(" Actual", a); emit log_named_uint(" Max Delta", maxDelta); emit log_named_uint(" Delta", delta); fail(); } } function assertRelApproxEq( uint256 a, uint256 b, uint256 maxPercentDelta // An 18 decimal fixed point number, where 1e18 == 100% ) internal virtual { if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. uint256 percentDelta = ((a > b ? a - b : b - a) * 1e18) / b; if (percentDelta > maxPercentDelta) { emit log("Error: a ~= b not satisfied [uint]"); emit log_named_uint(" Expected", b); emit log_named_uint(" Actual", a); emit log_named_decimal_uint(" Max % Delta", maxPercentDelta, 18); emit log_named_decimal_uint(" % Delta", percentDelta, 18); fail(); } } function assertBytesEq(bytes memory a, bytes memory b) internal virtual { if (keccak256(a) != keccak256(b)) { emit log("Error: a == b not satisfied [bytes]"); emit log_named_bytes(" Expected", b); emit log_named_bytes(" Actual", a); fail(); } } function assertUintArrayEq(uint256[] memory a, uint256[] memory b) internal virtual { require(a.length == b.length, "LENGTH_MISMATCH"); for (uint256 i = 0; i < a.length; i++) { assertEq(a[i], b[i]); } } function bound( uint256 x, uint256 min, uint256 max ) internal virtual returns (uint256 result) { require(max >= min, "MAX_LESS_THAN_MIN"); uint256 size = max - min; if (size == 0) result = min; else if (size == type(uint256).max) result = x; else { ++size; // Make max inclusive. uint256 mod = x % size; result = min + mod; } emit log_named_uint("Bound Result", result); } function min3( uint256 a, uint256 b, uint256 c ) internal pure returns (uint256) { return a > b ? (b > c ? c : b) : (a > c ? c : a); } function min2(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? b : a; } }