-
Notifications
You must be signed in to change notification settings - Fork 6
Combinatorial MultiToken _transferFrom unit test #60
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
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
5648e15
latest
Padraic-O-Mhuiris 49c4456
update
Padraic-O-Mhuiris 5b6d628
weird issue calling mock contract
Padraic-O-Mhuiris 600ff6e
finalize
Padraic-O-Mhuiris af87a48
lint
Padraic-O-Mhuiris 78166e9
rearranging
Padraic-O-Mhuiris bcbbf0a
cleaner error
Padraic-O-Mhuiris ed1e634
added helper fns for dynamic array type casting
Padraic-O-Mhuiris b17e169
comment
Padraic-O-Mhuiris 018e1b4
move event registration closer to the function at test
Padraic-O-Mhuiris dbb1bc1
Abstracted fail asssertions
Padraic-O-Mhuiris 001f02d
Comments
Padraic-O-Mhuiris 3ae81f2
relabel and add errors
Padraic-O-Mhuiris d0422f8
normalized TestCase struct name
Padraic-O-Mhuiris File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| pragma solidity ^0.8.18; | ||
|
|
||
| import "forge-std/Test.sol"; | ||
| import "forge-std/console2.sol"; | ||
|
|
||
| import { BaseTest, TestLib as Lib } from "test/Test.sol"; | ||
| import { MockMultiToken } from "test/mocks/MockMultiToken.sol"; | ||
| import { ForwarderFactory } from "contracts/ForwarderFactory.sol"; | ||
|
|
||
| contract MultiTokenTest is BaseTest { | ||
| ForwarderFactory forwarderFactory; | ||
| MockMultiToken multiToken; | ||
|
|
||
| function setUp() public override { | ||
| super.setUp(); | ||
| vm.startPrank(deployer); | ||
| forwarderFactory = new ForwarderFactory(); | ||
| multiToken = new MockMultiToken(bytes32(0), address(forwarderFactory)); | ||
| vm.stopPrank(); | ||
| } | ||
|
|
||
| function test__name_symbol() public { | ||
| vm.startPrank(alice); | ||
| multiToken.__setNameAndSymbol(5, "Token", "TKN"); | ||
| vm.stopPrank(); | ||
| assertEq(multiToken.name(5), "Token"); | ||
| assertEq(multiToken.symbol(5), "TKN"); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,289 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| pragma solidity ^0.8.13; | ||
|
|
||
| import "forge-std/console2.sol"; | ||
| import "forge-std/Vm.sol"; | ||
|
|
||
| import { Test } from "forge-std/Test.sol"; | ||
| import { Hyperdrive } from "contracts/Hyperdrive.sol"; | ||
| import { HyperdriveMath } from "contracts/libraries/HyperdriveMath.sol"; | ||
| import { ERC20PresetFixedSupply } from "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; | ||
| import { ForwarderFactory } from "contracts/ForwarderFactory.sol"; | ||
| import { FixedPointMath } from "contracts/libraries/FixedPointMath.sol"; | ||
|
|
||
| library TestLib { | ||
| // @notice Generates a matrix of all of the different combinations of | ||
| // inputs for each row. | ||
| // @dev In order to generate the full testing matrix, we need to generate | ||
| // cases for each value that use all of the input values. In order | ||
| // to do this, we segment the set of test cases into subsets for each | ||
| // entry | ||
| // @param inputs A matrix of uint256 values that defines the inputs that | ||
| // will be used to generate combinations for each row. Increasing the | ||
| // number of inputs dramatically increases the amount of test cases | ||
| // that will be generated, so it's important to limit the amount of | ||
| // inputs to a small number of meaningful values. We use uint256 for | ||
| // generality, since uint256 can be converted to small width types. | ||
| // @return The full testing matrix. | ||
| function matrix( | ||
| uint256[][] memory inputs | ||
| ) internal pure returns (uint256[][] memory result) { | ||
| // Compute the divisors that will be used to compute the intervals for | ||
| // every input row. | ||
| uint256 base = 1; | ||
| uint256[] memory intervalDivisors = new uint256[](inputs.length); | ||
| for (uint256 i = 0; i < inputs.length; i++) { | ||
| base *= inputs[i].length; | ||
| intervalDivisors[i] = base; | ||
| } | ||
| // Generate the testing matrix. | ||
| result = new uint256[][](base); | ||
| for (uint256 i = 0; i < result.length; i++) { | ||
| result[i] = new uint256[](inputs.length); | ||
| for (uint256 j = 0; j < inputs.length; j++) { | ||
| // The idea behind this calculation is that we split the set of | ||
| // test cases into sections and assign one input value to each | ||
| // section. For the first row, we'll create {inputs[0].length} | ||
| // sections and assign these values to sections linearly. For | ||
| // row 1, we'll create inputs[0].length * inputs[1].length | ||
| // sections, and we'll assign the 0th input to the first | ||
| // section, the 1st input to the second section, and continue | ||
| // this process (wrapping around once we run out of input values | ||
| // to allocate). | ||
| // | ||
| // The proof that each row of this procedure is unique is easy | ||
| // using induction. Proving that every row is unique also shows | ||
| // that the full test matrix has been covered. | ||
| result[i][j] = inputs[j][ | ||
| (i / (result.length / intervalDivisors[j])) % | ||
| inputs[j].length | ||
| ]; | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| function logArray( | ||
| string memory prelude, | ||
| uint256[] memory array | ||
| ) internal view { | ||
| console2.log(prelude, "["); | ||
| for (uint256 i = 0; i < array.length; i++) { | ||
| if (i < array.length - 1) { | ||
| console2.log(" ", array[i], ","); | ||
| } else { | ||
| console2.log(" ", array[i]); | ||
| } | ||
| } | ||
| console2.log(" ]"); | ||
| console2.log(""); | ||
| } | ||
|
|
||
| function _arr( | ||
| uint256 a, | ||
| uint256 b | ||
| ) internal pure returns (uint256[] memory _arr) { | ||
| _arr = new uint256[](2); | ||
| _arr[0] = a; | ||
| _arr[1] = b; | ||
| } | ||
|
|
||
| function _arr( | ||
| uint256 a, | ||
| uint256 b, | ||
| uint256 c | ||
| ) internal pure returns (uint256[] memory _arr) { | ||
| _arr = new uint256[](3); | ||
| _arr[0] = a; | ||
| _arr[1] = b; | ||
| _arr[2] = c; | ||
| } | ||
|
|
||
| function _arr( | ||
| uint256 a, | ||
| uint256 b, | ||
| uint256 c, | ||
| uint256 d | ||
| ) internal pure returns (uint256[] memory _arr) { | ||
| _arr = new uint256[](4); | ||
| _arr[0] = a; | ||
| _arr[1] = b; | ||
| _arr[2] = c; | ||
| _arr[3] = d; | ||
| } | ||
|
|
||
| function _arr( | ||
| uint256 a, | ||
| uint256 b, | ||
| uint256 c, | ||
| uint256 d, | ||
| uint256 e | ||
| ) internal pure returns (uint256[] memory _arr) { | ||
| _arr = new uint256[](5); | ||
| _arr[0] = a; | ||
| _arr[1] = b; | ||
| _arr[2] = c; | ||
| _arr[3] = d; | ||
| _arr[4] = e; | ||
| } | ||
|
|
||
| function _arr( | ||
| uint256[] memory a, | ||
| uint256[] memory b | ||
| ) internal pure returns (uint256[][] memory _arr) { | ||
| _arr = new uint256[][](2); | ||
| _arr[0] = a; | ||
| _arr[1] = b; | ||
| } | ||
|
|
||
| function _arr( | ||
| uint256[] memory a, | ||
| uint256[] memory b, | ||
| uint256[] memory c | ||
| ) internal pure returns (uint256[][] memory _arr) { | ||
| _arr = new uint256[][](3); | ||
| _arr[0] = a; | ||
| _arr[1] = b; | ||
| _arr[2] = c; | ||
| } | ||
|
|
||
| function _arr( | ||
| uint256[] memory a, | ||
| uint256[] memory b, | ||
| uint256[] memory c, | ||
| uint256[] memory d | ||
| ) internal pure returns (uint256[][] memory _arr) { | ||
| _arr = new uint256[][](4); | ||
| _arr[0] = a; | ||
| _arr[1] = b; | ||
| _arr[2] = c; | ||
| _arr[3] = d; | ||
| } | ||
|
|
||
| function _arr( | ||
| uint256[] memory a, | ||
| uint256[] memory b, | ||
| uint256[] memory c, | ||
| uint256[] memory d, | ||
| uint256[] memory e | ||
| ) internal pure returns (uint256[][] memory _arr) { | ||
| _arr = new uint256[][](5); | ||
| _arr[0] = a; | ||
| _arr[1] = b; | ||
| _arr[2] = c; | ||
| _arr[3] = d; | ||
| _arr[4] = e; | ||
| } | ||
|
|
||
| function eq(bytes memory b1, bytes memory b2) public pure returns (bool) { | ||
| return | ||
| keccak256(abi.encodePacked(b1)) == keccak256(abi.encodePacked(b2)); | ||
| } | ||
|
|
||
| function neq(bytes memory b1, bytes memory b2) public pure returns (bool) { | ||
| return | ||
| keccak256(abi.encodePacked(b1)) != keccak256(abi.encodePacked(b2)); | ||
| } | ||
| } | ||
|
|
||
| contract BaseTest is Test { | ||
| using FixedPointMath for uint256; | ||
|
|
||
| address alice; | ||
| address bob; | ||
| address eve; | ||
|
|
||
| address minter; | ||
| address deployer; | ||
|
|
||
| function setUp() public virtual { | ||
| alice = createUser("alice"); | ||
| bob = createUser("bob"); | ||
| eve = createUser("eve"); | ||
| deployer = createUser("deployer"); | ||
| minter = createUser("minter"); | ||
| } | ||
|
|
||
| // creates a user | ||
| function createUser(string memory name) public returns (address _user) { | ||
| _user = address(uint160(uint256(keccak256(abi.encode(name))))); | ||
| vm.label(_user, name); | ||
| vm.deal(_user, 100 ether); | ||
| } | ||
| } | ||
|
|
||
| contract CombinatorialTest is BaseTest { | ||
| enum CombinatorialTestKind { | ||
| Fail, | ||
| Success | ||
| } | ||
|
|
||
| CombinatorialTestKind internal __combinatorialTestKind = | ||
| CombinatorialTestKind.Success; | ||
|
|
||
| error ExpectedSuccess(); | ||
| error ExpectedFail(); | ||
|
|
||
| error UnassignedCatch(); | ||
| error UnassignedFail(); | ||
|
|
||
| bytes __error = abi.encodeWithSelector(UnassignedCatch.selector); | ||
| bytes __fail_error = abi.encodeWithSelector(UnassignedFail.selector); | ||
|
|
||
| modifier __combinatorial_setup() { | ||
| __combinatorialTestKind = CombinatorialTestKind.Success; | ||
| __error = abi.encodeWithSelector(UnassignedCatch.selector); | ||
| __fail_error = abi.encodeWithSelector(UnassignedFail.selector); | ||
| _; | ||
| } | ||
|
|
||
| modifier __combinatorial_success() { | ||
| // If the test case was set as a fail we short-circuit the __success function | ||
| if (__combinatorialTestKind == CombinatorialTestKind.Fail) { | ||
| return; | ||
| } | ||
| _; | ||
| } | ||
|
|
||
| modifier __combinatorial_fail() { | ||
| _; | ||
| // Detect if the __fail call was caught | ||
| if ( | ||
| TestLib.neq( | ||
| __error, | ||
| abi.encodeWithSelector(UnassignedCatch.selector) | ||
| ) | ||
| ) { | ||
| // If a __fail call was caught then a __fail_error must be assigned | ||
| assertTrue( | ||
| !checkEq0( | ||
| __fail_error, | ||
| abi.encodeWithSelector(UnassignedFail.selector) | ||
| ), | ||
| "__fail_error should be assigned" | ||
| ); | ||
| // If the caught error and the expected error do not match then cause a test revert | ||
| if (TestLib.neq(__error, __fail_error)) { | ||
| assertEq(__error, __fail_error, "Expected different error"); | ||
| } | ||
|
|
||
| // If an error was caught we set this so __success will short-circuit | ||
| __combinatorialTestKind = CombinatorialTestKind.Fail; | ||
| } else { | ||
| assertEq( | ||
| __fail_error, | ||
| abi.encodeWithSelector(UnassignedFail.selector), | ||
| "__fail_error should not be assigned" | ||
| ); | ||
| assertEq( | ||
| __error, | ||
| abi.encodeWithSelector(UnassignedCatch.selector), | ||
| "__error should not be assigned" | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| function setUp() public virtual override { | ||
| super.setUp(); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.