Checkpoints.t.sol 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // SPDX-License-Identifier: MIT
  2. // This file was procedurally generated from scripts/generate/templates/Checkpoints.t.js.
  3. pragma solidity ^0.8.20;
  4. import {Test} from "forge-std/Test.sol";
  5. import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
  6. import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol";
  7. contract CheckpointsTrace224Test is Test {
  8. using Checkpoints for Checkpoints.Trace224;
  9. // Maximum gap between keys used during the fuzzing tests: the `_prepareKeys` function will make sure that
  10. // key#n+1 is in the [key#n, key#n + _KEY_MAX_GAP] range.
  11. uint8 internal constant _KEY_MAX_GAP = 64;
  12. Checkpoints.Trace224 internal _ckpts;
  13. // helpers
  14. function _boundUint32(uint32 x, uint32 min, uint32 max) internal pure returns (uint32) {
  15. return SafeCast.toUint32(bound(uint256(x), uint256(min), uint256(max)));
  16. }
  17. function _prepareKeys(uint32[] memory keys, uint32 maxSpread) internal pure {
  18. uint32 lastKey = 0;
  19. for (uint256 i = 0; i < keys.length; ++i) {
  20. uint32 key = _boundUint32(keys[i], lastKey, lastKey + maxSpread);
  21. keys[i] = key;
  22. lastKey = key;
  23. }
  24. }
  25. function _assertLatestCheckpoint(bool exist, uint32 key, uint224 value) internal view {
  26. (bool _exist, uint32 _key, uint224 _value) = _ckpts.latestCheckpoint();
  27. assertEq(_exist, exist);
  28. assertEq(_key, key);
  29. assertEq(_value, value);
  30. }
  31. // tests
  32. function testPush(uint32[] memory keys, uint224[] memory values, uint32 pastKey) public {
  33. vm.assume(values.length > 0 && values.length <= keys.length);
  34. _prepareKeys(keys, _KEY_MAX_GAP);
  35. // initial state
  36. assertEq(_ckpts.length(), 0);
  37. assertEq(_ckpts.latest(), 0);
  38. _assertLatestCheckpoint(false, 0, 0);
  39. uint256 duplicates = 0;
  40. for (uint256 i = 0; i < keys.length; ++i) {
  41. uint32 key = keys[i];
  42. uint224 value = values[i % values.length];
  43. if (i > 0 && key == keys[i - 1]) ++duplicates;
  44. // push
  45. _ckpts.push(key, value);
  46. // check length & latest
  47. assertEq(_ckpts.length(), i + 1 - duplicates);
  48. assertEq(_ckpts.latest(), value);
  49. _assertLatestCheckpoint(true, key, value);
  50. }
  51. if (keys.length > 0) {
  52. uint32 lastKey = keys[keys.length - 1];
  53. if (lastKey > 0) {
  54. pastKey = _boundUint32(pastKey, 0, lastKey - 1);
  55. vm.expectRevert();
  56. this.push(pastKey, values[keys.length % values.length]);
  57. }
  58. }
  59. }
  60. // used to test reverts
  61. function push(uint32 key, uint224 value) external {
  62. _ckpts.push(key, value);
  63. }
  64. function testLookup(uint32[] memory keys, uint224[] memory values, uint32 lookup) public {
  65. vm.assume(values.length > 0 && values.length <= keys.length);
  66. _prepareKeys(keys, _KEY_MAX_GAP);
  67. uint32 lastKey = keys.length == 0 ? 0 : keys[keys.length - 1];
  68. lookup = _boundUint32(lookup, 0, lastKey + _KEY_MAX_GAP);
  69. uint224 upper = 0;
  70. uint224 lower = 0;
  71. uint32 lowerKey = type(uint32).max;
  72. for (uint256 i = 0; i < keys.length; ++i) {
  73. uint32 key = keys[i];
  74. uint224 value = values[i % values.length];
  75. // push
  76. _ckpts.push(key, value);
  77. // track expected result of lookups
  78. if (key <= lookup) {
  79. upper = value;
  80. }
  81. // find the first key that is not smaller than the lookup key
  82. if (key >= lookup && (i == 0 || keys[i - 1] < lookup)) {
  83. lowerKey = key;
  84. }
  85. if (key == lowerKey) {
  86. lower = value;
  87. }
  88. }
  89. // check lookup
  90. assertEq(_ckpts.lowerLookup(lookup), lower);
  91. assertEq(_ckpts.upperLookup(lookup), upper);
  92. assertEq(_ckpts.upperLookupRecent(lookup), upper);
  93. }
  94. }
  95. contract CheckpointsTrace208Test is Test {
  96. using Checkpoints for Checkpoints.Trace208;
  97. // Maximum gap between keys used during the fuzzing tests: the `_prepareKeys` function will make sure that
  98. // key#n+1 is in the [key#n, key#n + _KEY_MAX_GAP] range.
  99. uint8 internal constant _KEY_MAX_GAP = 64;
  100. Checkpoints.Trace208 internal _ckpts;
  101. // helpers
  102. function _boundUint48(uint48 x, uint48 min, uint48 max) internal pure returns (uint48) {
  103. return SafeCast.toUint48(bound(uint256(x), uint256(min), uint256(max)));
  104. }
  105. function _prepareKeys(uint48[] memory keys, uint48 maxSpread) internal pure {
  106. uint48 lastKey = 0;
  107. for (uint256 i = 0; i < keys.length; ++i) {
  108. uint48 key = _boundUint48(keys[i], lastKey, lastKey + maxSpread);
  109. keys[i] = key;
  110. lastKey = key;
  111. }
  112. }
  113. function _assertLatestCheckpoint(bool exist, uint48 key, uint208 value) internal view {
  114. (bool _exist, uint48 _key, uint208 _value) = _ckpts.latestCheckpoint();
  115. assertEq(_exist, exist);
  116. assertEq(_key, key);
  117. assertEq(_value, value);
  118. }
  119. // tests
  120. function testPush(uint48[] memory keys, uint208[] memory values, uint48 pastKey) public {
  121. vm.assume(values.length > 0 && values.length <= keys.length);
  122. _prepareKeys(keys, _KEY_MAX_GAP);
  123. // initial state
  124. assertEq(_ckpts.length(), 0);
  125. assertEq(_ckpts.latest(), 0);
  126. _assertLatestCheckpoint(false, 0, 0);
  127. uint256 duplicates = 0;
  128. for (uint256 i = 0; i < keys.length; ++i) {
  129. uint48 key = keys[i];
  130. uint208 value = values[i % values.length];
  131. if (i > 0 && key == keys[i - 1]) ++duplicates;
  132. // push
  133. _ckpts.push(key, value);
  134. // check length & latest
  135. assertEq(_ckpts.length(), i + 1 - duplicates);
  136. assertEq(_ckpts.latest(), value);
  137. _assertLatestCheckpoint(true, key, value);
  138. }
  139. if (keys.length > 0) {
  140. uint48 lastKey = keys[keys.length - 1];
  141. if (lastKey > 0) {
  142. pastKey = _boundUint48(pastKey, 0, lastKey - 1);
  143. vm.expectRevert();
  144. this.push(pastKey, values[keys.length % values.length]);
  145. }
  146. }
  147. }
  148. // used to test reverts
  149. function push(uint48 key, uint208 value) external {
  150. _ckpts.push(key, value);
  151. }
  152. function testLookup(uint48[] memory keys, uint208[] memory values, uint48 lookup) public {
  153. vm.assume(values.length > 0 && values.length <= keys.length);
  154. _prepareKeys(keys, _KEY_MAX_GAP);
  155. uint48 lastKey = keys.length == 0 ? 0 : keys[keys.length - 1];
  156. lookup = _boundUint48(lookup, 0, lastKey + _KEY_MAX_GAP);
  157. uint208 upper = 0;
  158. uint208 lower = 0;
  159. uint48 lowerKey = type(uint48).max;
  160. for (uint256 i = 0; i < keys.length; ++i) {
  161. uint48 key = keys[i];
  162. uint208 value = values[i % values.length];
  163. // push
  164. _ckpts.push(key, value);
  165. // track expected result of lookups
  166. if (key <= lookup) {
  167. upper = value;
  168. }
  169. // find the first key that is not smaller than the lookup key
  170. if (key >= lookup && (i == 0 || keys[i - 1] < lookup)) {
  171. lowerKey = key;
  172. }
  173. if (key == lowerKey) {
  174. lower = value;
  175. }
  176. }
  177. // check lookup
  178. assertEq(_ckpts.lowerLookup(lookup), lower);
  179. assertEq(_ckpts.upperLookup(lookup), upper);
  180. assertEq(_ckpts.upperLookupRecent(lookup), upper);
  181. }
  182. }
  183. contract CheckpointsTrace160Test is Test {
  184. using Checkpoints for Checkpoints.Trace160;
  185. // Maximum gap between keys used during the fuzzing tests: the `_prepareKeys` function will make sure that
  186. // key#n+1 is in the [key#n, key#n + _KEY_MAX_GAP] range.
  187. uint8 internal constant _KEY_MAX_GAP = 64;
  188. Checkpoints.Trace160 internal _ckpts;
  189. // helpers
  190. function _boundUint96(uint96 x, uint96 min, uint96 max) internal pure returns (uint96) {
  191. return SafeCast.toUint96(bound(uint256(x), uint256(min), uint256(max)));
  192. }
  193. function _prepareKeys(uint96[] memory keys, uint96 maxSpread) internal pure {
  194. uint96 lastKey = 0;
  195. for (uint256 i = 0; i < keys.length; ++i) {
  196. uint96 key = _boundUint96(keys[i], lastKey, lastKey + maxSpread);
  197. keys[i] = key;
  198. lastKey = key;
  199. }
  200. }
  201. function _assertLatestCheckpoint(bool exist, uint96 key, uint160 value) internal view {
  202. (bool _exist, uint96 _key, uint160 _value) = _ckpts.latestCheckpoint();
  203. assertEq(_exist, exist);
  204. assertEq(_key, key);
  205. assertEq(_value, value);
  206. }
  207. // tests
  208. function testPush(uint96[] memory keys, uint160[] memory values, uint96 pastKey) public {
  209. vm.assume(values.length > 0 && values.length <= keys.length);
  210. _prepareKeys(keys, _KEY_MAX_GAP);
  211. // initial state
  212. assertEq(_ckpts.length(), 0);
  213. assertEq(_ckpts.latest(), 0);
  214. _assertLatestCheckpoint(false, 0, 0);
  215. uint256 duplicates = 0;
  216. for (uint256 i = 0; i < keys.length; ++i) {
  217. uint96 key = keys[i];
  218. uint160 value = values[i % values.length];
  219. if (i > 0 && key == keys[i - 1]) ++duplicates;
  220. // push
  221. _ckpts.push(key, value);
  222. // check length & latest
  223. assertEq(_ckpts.length(), i + 1 - duplicates);
  224. assertEq(_ckpts.latest(), value);
  225. _assertLatestCheckpoint(true, key, value);
  226. }
  227. if (keys.length > 0) {
  228. uint96 lastKey = keys[keys.length - 1];
  229. if (lastKey > 0) {
  230. pastKey = _boundUint96(pastKey, 0, lastKey - 1);
  231. vm.expectRevert();
  232. this.push(pastKey, values[keys.length % values.length]);
  233. }
  234. }
  235. }
  236. // used to test reverts
  237. function push(uint96 key, uint160 value) external {
  238. _ckpts.push(key, value);
  239. }
  240. function testLookup(uint96[] memory keys, uint160[] memory values, uint96 lookup) public {
  241. vm.assume(values.length > 0 && values.length <= keys.length);
  242. _prepareKeys(keys, _KEY_MAX_GAP);
  243. uint96 lastKey = keys.length == 0 ? 0 : keys[keys.length - 1];
  244. lookup = _boundUint96(lookup, 0, lastKey + _KEY_MAX_GAP);
  245. uint160 upper = 0;
  246. uint160 lower = 0;
  247. uint96 lowerKey = type(uint96).max;
  248. for (uint256 i = 0; i < keys.length; ++i) {
  249. uint96 key = keys[i];
  250. uint160 value = values[i % values.length];
  251. // push
  252. _ckpts.push(key, value);
  253. // track expected result of lookups
  254. if (key <= lookup) {
  255. upper = value;
  256. }
  257. // find the first key that is not smaller than the lookup key
  258. if (key >= lookup && (i == 0 || keys[i - 1] < lookup)) {
  259. lowerKey = key;
  260. }
  261. if (key == lowerKey) {
  262. lower = value;
  263. }
  264. }
  265. // check lookup
  266. assertEq(_ckpts.lowerLookup(lookup), lower);
  267. assertEq(_ckpts.upperLookup(lookup), upper);
  268. assertEq(_ckpts.upperLookupRecent(lookup), upper);
  269. }
  270. }