-
Notifications
You must be signed in to change notification settings - Fork 676
/
Copy pathDSTestPlus.sol
179 lines (144 loc) · 5.79 KB
/
DSTestPlus.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// 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;
}
}