-
Notifications
You must be signed in to change notification settings - Fork 348
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(test): assertApproxEq, assertNotEq, assertFalse #38
Changes from 15 commits
e17d6f0
1e3b6a2
fb089f0
dfaa2d5
7e73c21
1a78d1b
3e8d693
b61c6c4
51ff84f
3e0ef00
1f08a76
f0e1ba9
03355c9
2c23374
924456a
99a36d8
3c15901
113b11c
b434995
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -139,6 +139,214 @@ abstract contract Test is DSTest { | |
addr := create(0, add(bytecode, 0x20), mload(bytecode)) | ||
} | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////////////////// | ||
ASSERT EQ | ||
//////////////////////////////////////////////////////////////////////////*/ | ||
|
||
function assertEq(bool a, bool b) internal { | ||
if (a != b) { | ||
emit log ("Error: a == b not satisfied [bool]"); | ||
emit log_named_string (" Expected", b ? "true" : "false"); | ||
emit log_named_string (" Actual", a ? "true" : "false"); | ||
fail(); | ||
} | ||
} | ||
|
||
function assertEq(bool a, bool b, string memory err) internal { | ||
if (a != b) { | ||
emit log_named_string("Error", err); | ||
emit log_named_string(" Expected", b ? "true" : "false"); | ||
emit log_named_string(" Actual", a ? "true" : "false"); | ||
fail(); | ||
} | ||
} | ||
|
||
function assertEq(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 assertEq(bytes memory a, bytes memory b, string memory err) internal virtual { | ||
if (keccak256(a) != keccak256(b)) { | ||
emit log_named_string ("Error", err); | ||
emit log_named_bytes (" Expected", b); | ||
emit log_named_bytes (" Actual", a); | ||
fail(); | ||
} | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////////////////// | ||
ASSERT FALSE | ||
//////////////////////////////////////////////////////////////////////////*/ | ||
|
||
function assertFalse(bool data) internal virtual { | ||
assertTrue(!data); | ||
} | ||
|
||
function assertFalse(bool data, string memory err) internal virtual { | ||
assertTrue(!data, err); | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////////////////// | ||
ASSERT APPROX EQ ABS | ||
//////////////////////////////////////////////////////////////////////////*/ | ||
|
||
function assertApproxEqAbs( | ||
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 assertApproxEqAbs( | ||
uint256 a, | ||
uint256 b, | ||
uint256 maxDelta, | ||
string memory err | ||
) internal virtual { | ||
uint256 delta = a > b ? a - b : b - a; | ||
|
||
if (delta > maxDelta) { | ||
emit log_named_string ("Error", err); | ||
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 assertApproxEqAbs( | ||
int256 a, | ||
int256 b, | ||
int256 maxDelta | ||
) internal virtual { | ||
int256 delta = a > b ? a - b : b - a; | ||
|
||
if (delta > maxDelta) { | ||
emit log ("Error: a ~= b not satisfied [int]"); | ||
emit log_named_int (" Expected", b); | ||
emit log_named_int (" Actual", a); | ||
emit log_named_int (" Max Delta", maxDelta); | ||
emit log_named_int (" Delta", delta); | ||
fail(); | ||
} | ||
} | ||
|
||
function assertApproxEqAbs( | ||
int256 a, | ||
int256 b, | ||
int256 maxDelta, | ||
string memory err | ||
) internal virtual { | ||
int256 delta = a > b ? a - b : b - a; | ||
|
||
if (delta > maxDelta) { | ||
emit log_named_string ("Error", err); | ||
emit log_named_int (" Expected", b); | ||
emit log_named_int (" Actual", a); | ||
emit log_named_int (" Max Delta", maxDelta); | ||
emit log_named_int (" Delta", delta); | ||
fail(); | ||
} | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////////////////// | ||
ASSERT APPROX EQ REL | ||
//////////////////////////////////////////////////////////////////////////*/ | ||
|
||
function assertApproxEqRel( | ||
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 assertApproxEqRel( | ||
uint256 a, | ||
uint256 b, | ||
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100% | ||
string memory err | ||
) 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_named_string ("Error", err); | ||
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 assertApproxEqRel( | ||
int256 a, | ||
int256 b, | ||
int256 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. | ||
|
||
int256 percentDelta = ((a > b ? a - b : b - a) * 1e18) / b; | ||
|
||
if (percentDelta > maxPercentDelta) { | ||
emit log ("Error: a ~= b not satisfied [uint]"); | ||
emit log_named_int (" Expected", b); | ||
emit log_named_int (" Actual", a); | ||
emit log_named_decimal_int (" Max % Delta", maxPercentDelta, 18); | ||
emit log_named_decimal_int (" % Delta", percentDelta, 18); | ||
fail(); | ||
} | ||
} | ||
|
||
function assertApproxEqRel( | ||
int256 a, | ||
int256 b, | ||
int256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100% | ||
string memory err | ||
) internal virtual { | ||
if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. | ||
|
||
int256 percentDelta = ((a > b ? a - b : b - a) * 1e18) / b; | ||
|
||
if (percentDelta > maxPercentDelta) { | ||
emit log_named_string ("Error", err); | ||
emit log_named_int (" Expected", b); | ||
emit log_named_int (" Actual", a); | ||
emit log_named_decimal_int (" Max % Delta", maxPercentDelta, 18); | ||
emit log_named_decimal_int (" % Delta", percentDelta, 18); | ||
fail(); | ||
} | ||
} | ||
} | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add a header here before There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done, can resolve if happy |
||
|
@@ -159,7 +367,7 @@ library stdError { | |
struct StdStorage { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here "STD-STORAGE" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done, can resolve if happy |
||
mapping (address => mapping(bytes4 => mapping(bytes32 => uint256))) slots; | ||
mapping (address => mapping(bytes4 => mapping(bytes32 => bool))) finds; | ||
|
||
bytes32[] _keys; | ||
bytes4 _sig; | ||
uint256 _depth; | ||
|
@@ -171,7 +379,7 @@ struct StdStorage { | |
library stdStorage { | ||
event SlotFound(address who, bytes4 fsig, bytes32 keysHash, uint slot); | ||
event WARNING_UninitedSlot(address who, uint slot); | ||
|
||
Vm private constant vm_std_store = Vm(address(uint160(uint256(keccak256('hevm cheat code'))))); | ||
|
||
function sigs( | ||
|
@@ -192,8 +400,8 @@ library stdStorage { | |
// if map struct, will be bytes32(uint256(keccak256(abi.encode(key1, keccak256(abi.encode(key0, uint(slot)))))) + structFieldDepth); | ||
function find( | ||
StdStorage storage self | ||
) | ||
internal | ||
) | ||
internal | ||
returns (uint256) | ||
{ | ||
address who = self._target; | ||
|
@@ -212,7 +420,7 @@ library stdStorage { | |
(, bytes memory rdat) = who.staticcall(cald); | ||
fdat = bytesToBytes32(rdat, 32*field_depth); | ||
} | ||
|
||
(bytes32[] memory reads, ) = vm_std_store.accesses(address(who)); | ||
if (reads.length == 1) { | ||
bytes32 curr = vm_std_store.load(who, reads[0]); | ||
|
@@ -239,7 +447,7 @@ library stdStorage { | |
(success, rdat) = who.staticcall(cald); | ||
fdat = bytesToBytes32(rdat, 32*field_depth); | ||
} | ||
|
||
if (success && fdat == bytes32(hex"1337")) { | ||
// we found which of the slots is the actual one | ||
emit SlotFound(who, fsig, keccak256(abi.encodePacked(ins, field_depth)), uint256(reads[i])); | ||
|
@@ -259,7 +467,7 @@ library stdStorage { | |
delete self._target; | ||
delete self._sig; | ||
delete self._keys; | ||
delete self._depth; | ||
delete self._depth; | ||
|
||
return self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]; | ||
} | ||
|
@@ -343,7 +551,7 @@ library stdStorage { | |
delete self._target; | ||
delete self._sig; | ||
delete self._keys; | ||
delete self._depth; | ||
delete self._depth; | ||
} | ||
|
||
function bytesToBytes32(bytes memory b, uint offset) public pure returns (bytes32) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, but really just need one section header for all the assertions to differentiate them from the other helper methods. Maybe just called "Assertions"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to collapse into ASSERTIONS section, only hesitation would be if we wanted to properly overload
assertApproxEqRel
andassertApproxEqAbs
. Then we might want to group these guys in their own region, separate to theassertEq
region.For now if we don't plan to fully do all the overloads in this PR, happy to just collapse to one collection and move on
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By "properly overload" do you mean with types other than int256/uint256? If so, I think just supporting int256/uint256 would cover most cases and is sufficient.
Either way, both a single Assertions section or two sections (one for assertEqs, one for approx equals) is good with me. I don't feel strongly about this, but see the thumbs up from @ZeroEkkusu so will leave it up to you both 😛
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It ends up being 4 sections if you group overloaded functions together. This may be excessive and the argument for would be if we expect more overloads to come down the track - then this makes it blindingly obvious where to put them and whether they comply with existing style/format.
Will defer to @ZeroEkkusu to act as tie breaker and move this forward. For reference here is the commit where I group overloaded assertions together: 924456a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea I had was to use the big headers to divide the main parts of the contract:
"STD-CHEATS", "STD-ASSERTIONS", "STD-ERRORS", "STD-STORAGE".
I like the order as is,
assertEq
-->assertApproxEqAbs
... It's logical IMO. Just putassertFalse
first. I don't think we need any dividers rn; we can always add them later, as that'd be a non-breaking change.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, summary of changes:
Changes not done:
contract Test