From 7e0754a8c6e5e21852895d5dd5981b4a794f0b9b Mon Sep 17 00:00:00 2001 From: mortimr Date: Tue, 7 Nov 2023 21:31:44 +0100 Subject: [PATCH] feat: add message to expect utilities (#231) * feat: add message to expect utilities - also fixes the not.toBeAContract condition - add test cases for toBeAContract * fix: invalid test names --------- Co-authored-by: Gonzalo --- src/_internal/Expect.sol | 396 ++++++++++++++++++++++++++++++++++++-- test/modules/Expect.t.sol | 232 ++++++++++++++++++++++ 2 files changed, 607 insertions(+), 21 deletions(-) diff --git a/src/_internal/Expect.sol b/src/_internal/Expect.sol index f2a7a02c..3feb03ba 100644 --- a/src/_internal/Expect.sol +++ b/src/_internal/Expect.sol @@ -90,7 +90,12 @@ library ExpectLib { /* BOOL */ function toEqual(_BoolExpectation memory self, bool expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_BoolExpectation memory self, bool expected, string memory message) internal { if (self.actual != expected) { + printMessage(message); console.log("Error: a == b not satisfied [bool]"); console.log(" Expected", expected); console.log(" Actual", self.actual); @@ -99,7 +104,12 @@ library ExpectLib { } function toEqual(_BoolExpectationNot memory self, bool expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_BoolExpectationNot memory self, bool expected, string memory message) internal { if (self.actual == expected) { + printMessage(message); console.log("Error: a != b not satisfied [bool]"); console.log(" Value a", expected); console.log(" Value b", self.actual); @@ -108,17 +118,30 @@ library ExpectLib { } function toBeTrue(_BoolExpectation memory self) internal { - toEqual(self, true); + self.toBeTrue(string("")); + } + + function toBeTrue(_BoolExpectation memory self, string memory message) internal { + toEqual(self, true, message); } function toBeFalse(_BoolExpectation memory self) internal { - toEqual(self, false); + self.toBeFalse(string("")); + } + + function toBeFalse(_BoolExpectation memory self, string memory message) internal { + toEqual(self, false, message); } /* ADDRESS */ function toEqual(_AddressExpectation memory self, address expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_AddressExpectation memory self, address expected, string memory message) internal { if (self.actual != expected) { + printMessage(message); console.log("Error: a == b not satisfied [address]"); console.log(" Expected", expected); console.log(" Actual", self.actual); @@ -127,7 +150,12 @@ library ExpectLib { } function toEqual(_AddressExpectationNot memory self, address expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_AddressExpectationNot memory self, address expected, string memory message) internal { if (self.actual == expected) { + printMessage(message); console.log("Error: a != b not satisfied [address]"); console.log(" Value a", expected); console.log(" Value b", self.actual); @@ -136,7 +164,12 @@ library ExpectLib { } function toBeAContract(_AddressExpectation memory self) internal { + self.toBeAContract(string("")); + } + + function toBeAContract(_AddressExpectation memory self, string memory message) internal { if (self.actual.code.length == 0) { + printMessage(message); console.log("Error: a is not a contract [address]"); console.log(" Value", self.actual); vulcan.fail(); @@ -144,7 +177,12 @@ library ExpectLib { } function toBeAContract(_AddressExpectationNot memory self) internal { - if (self.actual.code.length == 0) { + self.toBeAContract(string("")); + } + + function toBeAContract(_AddressExpectationNot memory self, string memory message) internal { + if (self.actual.code.length != 0) { + printMessage(message); console.log("Error: a is a contract [address]"); console.log(" Value", self.actual); vulcan.fail(); @@ -154,7 +192,12 @@ library ExpectLib { /* BYTES32 */ function toEqual(_Bytes32Expectation memory self, bytes32 expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_Bytes32Expectation memory self, bytes32 expected, string memory message) internal { if (self.actual != expected) { + printMessage(message); console.log("Error: a == b not satisfied [bytes32]"); console.log(" Expected", strings.toString(expected)); console.log(" Actual", strings.toString(self.actual)); @@ -163,7 +206,12 @@ library ExpectLib { } function toEqual(_Bytes32ExpectationNot memory self, bytes32 expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_Bytes32ExpectationNot memory self, bytes32 expected, string memory message) internal { if (self.actual == expected) { + printMessage(message); console.log("Error: a != b not satisfied [bytes32]"); console.log(" Value a", strings.toString(expected)); console.log(" Value b", strings.toString(self.actual)); @@ -172,7 +220,12 @@ library ExpectLib { } function toBeTheHashOf(_Bytes32Expectation memory self, bytes memory data) internal { + self.toBeTheHashOf(data, string("")); + } + + function toBeTheHashOf(_Bytes32Expectation memory self, bytes memory data, string memory message) internal { if (self.actual != keccak256(data)) { + printMessage(message); console.log("Error: a is not the hash of b [bytes32]"); console.log(" Expected", strings.toString(data)); console.log(" Actual", strings.toString(self.actual)); @@ -181,7 +234,12 @@ library ExpectLib { } function toBeTheHashOf(_Bytes32ExpectationNot memory self, bytes memory data) internal { + self.toBeTheHashOf(data, string("")); + } + + function toBeTheHashOf(_Bytes32ExpectationNot memory self, bytes memory data, string memory message) internal { if (self.actual == keccak256(data)) { + printMessage(message); console.log("Error: a is the hash of b [bytes32]"); console.log(" Value a", strings.toString(data)); console.log(" Value b", strings.toString(self.actual)); @@ -192,7 +250,12 @@ library ExpectLib { /* BYTES */ function toEqual(_BytesExpectation memory self, bytes memory expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_BytesExpectation memory self, bytes memory expected, string memory message) internal { if (keccak256(self.actual) != keccak256(expected)) { + printMessage(message); console.log("Error: a == b not satisfied [bytes]"); console.log(" Expected", strings.toString(expected)); console.log(" Actual", strings.toString(self.actual)); @@ -201,7 +264,12 @@ library ExpectLib { } function toEqual(_BytesExpectationNot memory self, bytes memory expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_BytesExpectationNot memory self, bytes memory expected, string memory message) internal { if (keccak256(self.actual) == keccak256(expected)) { + printMessage(message); console.log("Error: a != b not satisfied [bytes]"); console.log(" Value", strings.toString(expected)); console.log(" Value", strings.toString(self.actual)); @@ -212,7 +280,12 @@ library ExpectLib { /* STRING */ function toEqual(_StringExpectation memory self, string memory expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_StringExpectation memory self, string memory expected, string memory message) internal { if (keccak256(abi.encodePacked(self.actual)) != keccak256(abi.encodePacked(expected))) { + printMessage(message); console.log("Error: a == b not satisfied [string]"); console.log(" Expected", expected); console.log(" Actual", self.actual); @@ -221,7 +294,12 @@ library ExpectLib { } function toEqual(_StringExpectationNot memory self, string memory expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_StringExpectationNot memory self, string memory expected, string memory message) internal { if (keccak256(abi.encodePacked(self.actual)) == keccak256(abi.encodePacked(expected))) { + printMessage(message); console.log("Error: a != b not satisfied [string]"); console.log(" Value a", expected); console.log(" Value b", self.actual); @@ -230,6 +308,10 @@ library ExpectLib { } function toContain(_StringExpectation memory self, string memory contained) internal { + self.toContain(contained, string("")); + } + + function toContain(_StringExpectation memory self, string memory contained, string memory message) internal { bytes memory actual = bytes(self.actual); bytes memory expected = bytes(contained); @@ -250,6 +332,7 @@ library ExpectLib { } } + printMessage(message); console.log("Error: a does not contain b [string]"); console.log(" Value a", self.actual); console.log(" Value b", contained); @@ -257,6 +340,10 @@ library ExpectLib { } function toContain(_StringExpectationNot memory self, string memory contained) internal { + self.toContain(contained, string("")); + } + + function toContain(_StringExpectationNot memory self, string memory contained, string memory message) internal { bytes memory actual = bytes(self.actual); bytes memory expected = bytes(contained); @@ -274,6 +361,7 @@ library ExpectLib { // Found if (j == expected.length) { + printMessage(message); console.log("Error: a contains b [string]"); console.log(" Value a", self.actual); console.log(" Value b", contained); @@ -284,7 +372,12 @@ library ExpectLib { } function toHaveLength(_StringExpectation memory self, uint256 expected) internal { + self.toHaveLength(expected, string("")); + } + + function toHaveLength(_StringExpectation memory self, uint256 expected, string memory message) internal { if (bytes(self.actual).length != expected) { + printMessage(message); console.log("Error: a.length != b [string]"); console.log(" Expected", expected); console.log(" Actual", bytes(self.actual).length); @@ -293,7 +386,12 @@ library ExpectLib { } function toHaveLength(_StringExpectationNot memory self, uint256 expected) internal { + self.toHaveLength(expected, string("")); + } + + function toHaveLength(_StringExpectationNot memory self, uint256 expected, string memory message) internal { if (bytes(self.actual).length == expected) { + printMessage(message); console.log("Error: a.length == b [string]"); console.log(" Value a", expected); console.log(" Value b", bytes(self.actual).length); @@ -304,7 +402,12 @@ library ExpectLib { /* UINT256 */ function toEqual(_UintExpectation memory self, uint256 expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_UintExpectation memory self, uint256 expected, string memory message) internal { if (self.actual != expected) { + printMessage(message); console.log("Error: a == b not satisfied [uint]"); console.log(" Expected", expected); console.log(" Actual", self.actual); @@ -313,7 +416,12 @@ library ExpectLib { } function toEqual(_UintExpectationNot memory self, uint256 expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_UintExpectationNot memory self, uint256 expected, string memory message) internal { if (self.actual == expected) { + printMessage(message); console.log("Error: a != b not satisfied [uint]"); console.log(" Value a", expected); console.log(" Value b", self.actual); @@ -322,8 +430,13 @@ library ExpectLib { } function toBeCloseTo(_UintExpectation memory self, uint256 expected, uint256 d) internal { + self.toBeCloseTo(expected, d, string("")); + } + + function toBeCloseTo(_UintExpectation memory self, uint256 expected, uint256 d, string memory message) internal { uint256 diff = delta(self.actual, expected); if (diff > d) { + printMessage(message); console.log("Error: a ~= b not satisfied [uint]"); console.log(" Expected", expected); console.log(" Actual", self.actual); @@ -334,7 +447,12 @@ library ExpectLib { } function toBeLessThan(_UintExpectation memory self, uint256 expected) internal { + self.toBeLessThan(expected, string("")); + } + + function toBeLessThan(_UintExpectation memory self, uint256 expected, string memory message) internal { if (self.actual >= expected) { + printMessage(message); console.log("Error: a < b not satisfied [uint]"); console.log(" Value a", self.actual); console.log(" Value b", expected); @@ -343,7 +461,12 @@ library ExpectLib { } function toBeLessThanOrEqual(_UintExpectation memory self, uint256 expected) internal { + self.toBeLessThanOrEqual(expected, string("")); + } + + function toBeLessThanOrEqual(_UintExpectation memory self, uint256 expected, string memory message) internal { if (self.actual > expected) { + printMessage(message); console.log("Error: a <= b not satisfied [uint]"); console.log(" Value a", self.actual); console.log(" Value b", expected); @@ -352,7 +475,12 @@ library ExpectLib { } function toBeGreaterThan(_UintExpectation memory self, uint256 expected) internal { + self.toBeGreaterThan(expected, string("")); + } + + function toBeGreaterThan(_UintExpectation memory self, uint256 expected, string memory message) internal { if (self.actual <= expected) { + printMessage(message); console.log("Error: a > b not satisfied [uint]"); console.log(" Value a", self.actual); console.log(" Value b", expected); @@ -361,7 +489,12 @@ library ExpectLib { } function toBeGreaterThanOrEqual(_UintExpectation memory self, uint256 expected) internal { + self.toBeGreaterThanOrEqual(expected, string("")); + } + + function toBeGreaterThanOrEqual(_UintExpectation memory self, uint256 expected, string memory message) internal { if (self.actual < expected) { + printMessage(message); console.log("Error: a >= b not satisfied [uint]"); console.log(" Value a", self.actual); console.log(" Value b", expected); @@ -372,7 +505,12 @@ library ExpectLib { /* INT */ function toEqual(_IntExpectation memory self, int256 expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_IntExpectation memory self, int256 expected, string memory message) internal { if (self.actual != expected) { + printMessage(message); console.log("Error: a == b not satisfied [int]"); console.log(" Expected", expected); console.log(" Actual", self.actual); @@ -381,7 +519,12 @@ library ExpectLib { } function toEqual(_IntExpectationNot memory self, int256 expected) internal { + self.toEqual(expected, string("")); + } + + function toEqual(_IntExpectationNot memory self, int256 expected, string memory message) internal { if (self.actual == expected) { + printMessage(message); console.log("Error: a != b not satisfied [int]"); console.log(" Value a", expected); console.log(" Value b", self.actual); @@ -390,9 +533,14 @@ library ExpectLib { } function toBeCloseTo(_IntExpectation memory self, int256 expected, uint256 d) internal { + self.toBeCloseTo(expected, d, string("")); + } + + function toBeCloseTo(_IntExpectation memory self, int256 expected, uint256 d, string memory message) internal { uint256 diff = delta(self.actual, expected); if (diff > d) { + printMessage(message); console.log("Error: a ~= b not satisfied [uint]"); console.log(" Expected", expected); console.log(" Actual", self.actual); @@ -403,7 +551,12 @@ library ExpectLib { } function toBeLessThan(_IntExpectation memory self, int256 expected) internal { + self.toBeLessThan(expected, string("")); + } + + function toBeLessThan(_IntExpectation memory self, int256 expected, string memory message) internal { if (self.actual >= expected) { + printMessage(message); console.log("Error: a < b not satisfied [int]"); console.log(" Value a", self.actual); console.log(" Value b", expected); @@ -412,7 +565,12 @@ library ExpectLib { } function toBeLessThanOrEqual(_IntExpectation memory self, int256 expected) internal { + self.toBeLessThanOrEqual(expected, string("")); + } + + function toBeLessThanOrEqual(_IntExpectation memory self, int256 expected, string memory message) internal { if (self.actual > expected) { + printMessage(message); console.log("Error: a <= b not satisfied [int]"); console.log(" Value a", self.actual); console.log(" Value b", expected); @@ -421,7 +579,12 @@ library ExpectLib { } function toBeGreaterThan(_IntExpectation memory self, int256 expected) internal { + self.toBeGreaterThan(expected, string("")); + } + + function toBeGreaterThan(_IntExpectation memory self, int256 expected, string memory message) internal { if (self.actual <= expected) { + printMessage(message); console.log("Error: a > b not satisfied [int]"); console.log(" Value a", self.actual); console.log(" Value b", expected); @@ -430,7 +593,12 @@ library ExpectLib { } function toBeGreaterThanOrEqual(_IntExpectation memory self, int256 expected) internal { + self.toBeGreaterThanOrEqual(expected, string("")); + } + + function toBeGreaterThanOrEqual(_IntExpectation memory self, int256 expected, string memory message) internal { if (self.actual < expected) { + printMessage(message); console.log("Error: a >= b not satisfied [int]"); console.log(" Value a", self.actual); console.log(" Value b", expected); @@ -441,18 +609,30 @@ library ExpectLib { /* CALLS */ function toHaveReverted(_CallExpectation memory self) internal { + self.toHaveReverted(string("")); + } + + function toHaveReverted(_CallExpectation memory self, string memory message) internal { if (self.call.success) { + printMessage(message); console.log("Error: call expected to revert [call]"); vulcan.fail(); } } function toHaveRevertedWith(_CallExpectation memory self, bytes4 expectedSelector) internal { + self.toHaveRevertedWith(expectedSelector, string("")); + } + + function toHaveRevertedWith(_CallExpectation memory self, bytes4 expectedSelector, string memory message) + internal + { self.toHaveReverted(); bytes4 actualSelector = bytes4(self.call.returnData); if (actualSelector != expectedSelector) { + printMessage(message); console.log("Error: call expected to revert with selector [call]"); console.log(" Expected error", strings.toString(bytes32(expectedSelector))); console.log(" Actual error", strings.toString(bytes32(actualSelector))); @@ -462,9 +642,16 @@ library ExpectLib { } function toHaveRevertedWith(_CallExpectationNot memory self, bytes4 expectedSelector) internal { + self.toHaveRevertedWith(expectedSelector, string("")); + } + + function toHaveRevertedWith(_CallExpectationNot memory self, bytes4 expectedSelector, string memory message) + internal + { bytes4 actualSelector = bytes4(self.call.returnData); if (!self.call.success && actualSelector == expectedSelector) { + printMessage(message); console.log("Error: call expected to not revert with selector [call]"); console.log(" Actual error", strings.toString(bytes32(actualSelector))); @@ -473,25 +660,40 @@ library ExpectLib { } function toHaveRevertedWith(_CallExpectation memory self, string memory error) internal { + self.toHaveRevertedWith(error, string("")); + } + + function toHaveRevertedWith(_CallExpectation memory self, string memory error, string memory message) internal { bytes memory expectedError = abi.encodeWithSignature("Error(string)", error); - self.toHaveRevertedWith(expectedError); + self.toHaveRevertedWith(expectedError, message); } function toHaveRevertedWith(_CallExpectationNot memory self, string memory error) internal { + self.toHaveRevertedWith(error, string("")); + } + + function toHaveRevertedWith(_CallExpectationNot memory self, string memory error, string memory message) internal { bytes memory expectedError = abi.encodeWithSignature("Error(string)", error); // This will use the `not` version of the function - self.toHaveRevertedWith(expectedError); + self.toHaveRevertedWith(expectedError, message); } function toHaveRevertedWith(_CallExpectation memory self, bytes memory expectedError) internal { + self.toHaveRevertedWith(expectedError, string("")); + } + + function toHaveRevertedWith(_CallExpectation memory self, bytes memory expectedError, string memory message) + internal + { self.toHaveReverted(); bytes32 expectedHash = keccak256(expectedError); bytes32 actualHash = keccak256(self.call.returnData); if (!self.call.success && actualHash != expectedHash) { + printMessage(message); console.log("Error: function expected to revert with error [call]"); console.log(" Expected error", strings.toString(expectedError)); console.log(" Actual error", strings.toString(self.call.returnData)); @@ -501,10 +703,17 @@ library ExpectLib { } function toHaveRevertedWith(_CallExpectationNot memory self, bytes memory expectedError) internal { + self.toHaveRevertedWith(expectedError, string("")); + } + + function toHaveRevertedWith(_CallExpectationNot memory self, bytes memory expectedError, string memory message) + internal + { bytes32 expectedHash = keccak256(expectedError); bytes32 actualHash = keccak256(self.call.returnData); if (!self.call.success && actualHash == expectedHash) { + printMessage(message); console.log("Error: function expected to not revert with error [call]"); console.log(" Actual error", strings.toString(self.call.returnData)); @@ -513,62 +722,160 @@ library ExpectLib { } function toHaveSucceeded(_CallExpectation memory self) internal { + self.toHaveSucceeded(string("")); + } + + function toHaveSucceeded(_CallExpectation memory self, string memory message) internal { if (!self.call.success) { + printMessage(message); console.log("Error: call expected to succeed [call]"); vulcan.fail(); } } function toHaveEmitted(_CallExpectation memory self, string memory eventSig) internal { - self.toHaveEmitted(eventSig, new bytes32[](0), new bytes(0)); + self.toHaveEmitted(eventSig, string("")); + } + + function toHaveEmitted(_CallExpectation memory self, string memory eventSig, string memory message) internal { + self.toHaveEmitted(eventSig, new bytes32[](0), new bytes(0), message); } function toHaveEmitted(_CallExpectation memory self, bytes32[1] memory topics) internal { - self.toHaveEmitted("", _toDynamic(topics), new bytes(0)); + self.toHaveEmitted(topics, string("")); + } + + function toHaveEmitted(_CallExpectation memory self, bytes32[1] memory topics, string memory message) internal { + self.toHaveEmitted("", _toDynamic(topics), new bytes(0), message); } function toHaveEmitted(_CallExpectation memory self, bytes32[2] memory topics) internal { - self.toHaveEmitted("", _toDynamic(topics), new bytes(0)); + self.toHaveEmitted(topics, string("")); + } + + function toHaveEmitted(_CallExpectation memory self, bytes32[2] memory topics, string memory message) internal { + self.toHaveEmitted("", _toDynamic(topics), new bytes(0), message); } function toHaveEmitted(_CallExpectation memory self, bytes32[3] memory topics) internal { - self.toHaveEmitted("", _toDynamic(topics), new bytes(0)); + self.toHaveEmitted(topics, string("")); + } + + function toHaveEmitted(_CallExpectation memory self, bytes32[3] memory topics, string memory message) internal { + self.toHaveEmitted("", _toDynamic(topics), new bytes(0), message); } function toHaveEmitted(_CallExpectation memory self, bytes32[4] memory topics) internal { - self.toHaveEmitted("", _toDynamic(topics), new bytes(0)); + self.toHaveEmitted(topics, string("")); + } + + function toHaveEmitted(_CallExpectation memory self, bytes32[4] memory topics, string memory message) internal { + self.toHaveEmitted("", _toDynamic(topics), new bytes(0), message); } function toHaveEmitted(_CallExpectation memory self, string memory eventSig, bytes memory data) internal { - self.toHaveEmitted(eventSig, new bytes32[](0), data); + self.toHaveEmitted(eventSig, data, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + string memory eventSig, + bytes memory data, + string memory message + ) internal { + self.toHaveEmitted(eventSig, new bytes32[](0), data, message); } function toHaveEmitted(_CallExpectation memory self, string memory eventSig, bytes32[1] memory topics) internal { - self.toHaveEmitted(eventSig, _toDynamic(topics), new bytes(0)); + self.toHaveEmitted(eventSig, topics, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + string memory eventSig, + bytes32[1] memory topics, + string memory message + ) internal { + self.toHaveEmitted(eventSig, _toDynamic(topics), new bytes(0), message); } function toHaveEmitted(_CallExpectation memory self, string memory eventSig, bytes32[2] memory topics) internal { - self.toHaveEmitted(eventSig, _toDynamic(topics), new bytes(0)); + self.toHaveEmitted(eventSig, topics, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + string memory eventSig, + bytes32[2] memory topics, + string memory message + ) internal { + printMessage(message); + self.toHaveEmitted(eventSig, _toDynamic(topics), new bytes(0), message); } function toHaveEmitted(_CallExpectation memory self, string memory eventSig, bytes32[3] memory topics) internal { - self.toHaveEmitted(eventSig, _toDynamic(topics), new bytes(0)); + self.toHaveEmitted(eventSig, topics, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + string memory eventSig, + bytes32[3] memory topics, + string memory message + ) internal { + self.toHaveEmitted(eventSig, _toDynamic(topics), new bytes(0), message); } function toHaveEmitted(_CallExpectation memory self, bytes32[1] memory topics, bytes memory data) internal { - self.toHaveEmitted("", _toDynamic(topics), data); + self.toHaveEmitted(topics, data, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + bytes32[1] memory topics, + bytes memory data, + string memory message + ) internal { + self.toHaveEmitted("", _toDynamic(topics), data, message); } function toHaveEmitted(_CallExpectation memory self, bytes32[2] memory topics, bytes memory data) internal { - self.toHaveEmitted("", _toDynamic(topics), data); + self.toHaveEmitted(topics, data, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + bytes32[2] memory topics, + bytes memory data, + string memory message + ) internal { + self.toHaveEmitted("", _toDynamic(topics), data, message); } function toHaveEmitted(_CallExpectation memory self, bytes32[3] memory topics, bytes memory data) internal { - self.toHaveEmitted("", _toDynamic(topics), data); + self.toHaveEmitted(topics, data, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + bytes32[3] memory topics, + bytes memory data, + string memory message + ) internal { + self.toHaveEmitted("", _toDynamic(topics), data, message); } function toHaveEmitted(_CallExpectation memory self, bytes32[4] memory topics, bytes memory data) internal { - self.toHaveEmitted("", _toDynamic(topics), data); + self.toHaveEmitted(topics, data, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + bytes32[4] memory topics, + bytes memory data, + string memory message + ) internal { + self.toHaveEmitted("", _toDynamic(topics), data, message); } function toHaveEmitted( @@ -577,7 +884,17 @@ library ExpectLib { bytes32[1] memory topics, bytes memory data ) internal { - self.toHaveEmitted(eventSig, _toDynamic(topics), data); + self.toHaveEmitted(eventSig, topics, data, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + string memory eventSig, + bytes32[1] memory topics, + bytes memory data, + string memory message + ) internal { + self.toHaveEmitted(eventSig, _toDynamic(topics), data, message); } function toHaveEmitted( @@ -586,7 +903,17 @@ library ExpectLib { bytes32[2] memory topics, bytes memory data ) internal { - self.toHaveEmitted(eventSig, _toDynamic(topics), data); + self.toHaveEmitted(eventSig, topics, data, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + string memory eventSig, + bytes32[2] memory topics, + bytes memory data, + string memory message + ) internal { + self.toHaveEmitted(eventSig, _toDynamic(topics), data, message); } function toHaveEmitted( @@ -595,7 +922,17 @@ library ExpectLib { bytes32[3] memory topics, bytes memory data ) internal { - self.toHaveEmitted(eventSig, _toDynamic(topics), data); + self.toHaveEmitted(eventSig, topics, data, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + string memory eventSig, + bytes32[3] memory topics, + bytes memory data, + string memory message + ) internal { + self.toHaveEmitted(eventSig, _toDynamic(topics), data, message); } function toHaveEmitted( @@ -603,6 +940,16 @@ library ExpectLib { string memory eventSig, bytes32[] memory topics, bytes memory data + ) internal { + self.toHaveEmitted(eventSig, topics, data, string("")); + } + + function toHaveEmitted( + _CallExpectation memory self, + string memory eventSig, + bytes32[] memory topics, + bytes memory data, + string memory message ) internal { self.toHaveSucceeded(); @@ -642,6 +989,7 @@ library ExpectLib { } // If we reach here, the topics did not match + printMessage(message); console.log("Error: event not emitted [call]"); console.log(" Event", bytes(eventSig).length > 0 ? eventSig : "anonymous"); vulcan.fail(); @@ -684,6 +1032,12 @@ library ExpectLib { _topics[2] = topics[2]; _topics[3] = topics[3]; } + + function printMessage(string memory message) internal pure { + if (bytes(message).length > 0) { + console.log("Message:", message); + } + } } function expect(bool actual) pure returns (_BoolExpectation memory) { diff --git a/test/modules/Expect.t.sol b/test/modules/Expect.t.sol index d51dd345..d53ce326 100644 --- a/test/modules/Expect.t.sol +++ b/test/modules/Expect.t.sol @@ -72,6 +72,7 @@ contract ExpectTest is Test { function testUintToEqualPass(uint256 a) external { expect(a).toEqual(a); + expect(a).toEqual(a, "a should equal a"); } function testUintToEqualFail(uint256 a, uint256 b) external shouldFail { @@ -79,18 +80,29 @@ contract ExpectTest is Test { expect(a).toEqual(b); } + function testUintToEqualFailWithMessage(uint256 a, uint256 b) external shouldFail { + ctx.assume(a != b); + expect(a).toEqual(b, "a should equal b"); + } + function testUintNotToEqualPass(uint256 a, uint256 b) external { ctx.assume(a != b); expect(a).not.toEqual(b); + expect(a).not.toEqual(b, "a should not equal b"); } function testUintNotToEqualFail(uint256 a) external shouldFail { expect(a).not.toEqual(a); } + function testUintNotToEqualFailWithMessage(uint256 a) external shouldFail { + expect(a).not.toEqual(a, "a should not equal a"); + } + function testUintToBeCloseToPass(uint256 a, uint256 b, uint256 d) external { ctx.assume(delta(a, b) <= d); expect(a).toBeCloseTo(b, d); + expect(a).toBeCloseTo(b, d, "a should be close to b"); } function testUintToBeCloseToFail(uint256 a, uint256 b, uint256 d) external shouldFail { @@ -98,30 +110,42 @@ contract ExpectTest is Test { expect(a).toBeCloseTo(b, d); } + function testUintToBeCloseToFailWithMessage(uint256 a, uint256 b, uint256 d) external shouldFail { + ctx.assume(delta(a, b) > d); + expect(a).toBeCloseTo(b, d, "a should be close to b"); + } + function testUintToBeLessThanPass(uint256 a, uint256 b) external { ctx.assume(a < b); expect(a).toBeLessThan(b); + expect(a).toBeLessThan(b, "a should be less than b"); } function testUintToBeLessThanOrEqualPass(uint256 a, uint256 b) external { ctx.assume(a <= b); expect(a).toBeLessThanOrEqual(b); expect(a).toBeLessThanOrEqual(a); + expect(a).toBeLessThanOrEqual(b, "a should be less than or equal to b"); + expect(a).toBeLessThanOrEqual(a, "a should be less than or equal to a"); } function testUintToBeGreaterThanPass(uint256 a, uint256 b) external { ctx.assume(a > b); expect(a).toBeGreaterThan(b); + expect(a).toBeGreaterThan(b, "a should be greater than b"); } function testUintToBeGreaterThanOrEqualPass(uint256 a, uint256 b) external { ctx.assume(a >= b); expect(a).toBeGreaterThanOrEqual(b); expect(a).toBeGreaterThanOrEqual(a); + expect(a).toBeGreaterThanOrEqual(b, "a should be greater than or equal to b"); + expect(a).toBeGreaterThanOrEqual(a, "a should be greater than or equal to a"); } function testIntToEqualPass(int256 a) external { expect(a).toEqual(a); + expect(a).toEqual(a, "a should equal a"); } function testIntToEqualFail(int256 a, int256 b) external shouldFail { @@ -129,18 +153,29 @@ contract ExpectTest is Test { expect(a).toEqual(b); } + function testIntToEqualFailWithMessage(int256 a, int256 b) external shouldFail { + ctx.assume(a != b); + expect(a).toEqual(b, "a should equal b"); + } + function testIntNotToEqualPass(int256 a, int256 b) external { ctx.assume(a != b); expect(a).not.toEqual(b); + expect(a).not.toEqual(b, "a should not equal b"); } function testIntNotToEqualFail(int256 a) external shouldFail { expect(a).not.toEqual(a); } + function testIntNotToEqualFailWithMessage(int256 a) external shouldFail { + expect(a).not.toEqual(a, "a should not equal a"); + } + function testIntToBeCloseToPass(int256 a, int256 b, uint256 d) external { ctx.assume(delta(a, b) <= d); expect(a).toBeCloseTo(b, d); + expect(a).toBeCloseTo(b, d, "a should be close to b"); } function testIntToBeCloseToFail(int256 a, int256 b, uint256 d) external shouldFail { @@ -148,9 +183,15 @@ contract ExpectTest is Test { expect(a).toBeCloseTo(b, d); } + function testIntToBeCloseToFailWithMessage(int256 a, int256 b, uint256 d) external shouldFail { + ctx.assume(delta(a, b) > d); + expect(a).toBeCloseTo(b, d, "a should be close to b"); + } + function testIntToBeLessThanPass(int256 a, int256 b) external { ctx.assume(a < b); expect(a).toBeLessThan(b); + expect(a).toBeLessThan(b, "a should be less than b"); } function testIntToBeLessThanFail(int256 a, int256 b) external shouldFail { @@ -158,10 +199,17 @@ contract ExpectTest is Test { expect(a).toBeLessThan(b); } + function testIntToBeLessThanFailWithMessage(int256 a, int256 b) external shouldFail { + ctx.assume(a >= b); + expect(a).toBeLessThan(b, "a should be less than b"); + } + function testIntToBeLessThanOrEqualPass(int256 a, int256 b) external { ctx.assume(a <= b); expect(a).toBeLessThanOrEqual(b); expect(a).toBeLessThanOrEqual(a); + expect(a).toBeLessThanOrEqual(b, "a should be less than or equal to b"); + expect(a).toBeLessThanOrEqual(a, "a should be less than or equal to a"); } function testIntToBeLessThanOrEqualFail(int256 a, int256 b) external shouldFail { @@ -169,9 +217,15 @@ contract ExpectTest is Test { expect(a).toBeLessThanOrEqual(b); } + function testIntToBeLessThanOrEqualFailWithMessage(int256 a, int256 b) external shouldFail { + ctx.assume(a > b); + expect(a).toBeLessThanOrEqual(b, "a should be less than or equal to b"); + } + function testIntToBeGreaterThanPass(int256 a, int256 b) external { ctx.assume(a > b); expect(a).toBeGreaterThan(b); + expect(a).toBeGreaterThan(b, "a should be greater than b"); } function testIntToBeGreaterThanFail(int256 a, int256 b) external shouldFail { @@ -179,10 +233,17 @@ contract ExpectTest is Test { expect(a).toBeGreaterThan(b); } + function testIntToBeGreaterThanFailWithMessage(int256 a, int256 b) external shouldFail { + ctx.assume(a <= b); + expect(a).toBeGreaterThan(b, "a should be greater than b"); + } + function testIntToBeGreaterThanOrEqualPass(int256 a, int256 b) external { ctx.assume(a >= b); expect(a).toBeGreaterThanOrEqual(b); expect(a).toBeGreaterThanOrEqual(a); + expect(a).toBeGreaterThanOrEqual(b, "a should be greater than or equal to b"); + expect(a).toBeGreaterThanOrEqual(a, "a should be greater than or equal to a"); } function testIntToBeGreaterThanOrEqualFail(int256 a, int256 b) external shouldFail { @@ -190,13 +251,22 @@ contract ExpectTest is Test { expect(a).toBeGreaterThanOrEqual(b); } + function testIntToBeGreaterThanOrEqualFailWithMessage(int256 a, int256 b) external shouldFail { + ctx.assume(a < b); + expect(a).toBeGreaterThanOrEqual(b, "a should be greater than or equal to b"); + } + function testBoolToEqualPass(bool a) external { if (a) { expect(a).toEqual(true); + expect(a).toEqual(true, "a should be true"); expect(a).toBeTrue(); + expect(a).toBeTrue("a should be true"); } else { expect(a).toEqual(false); + expect(a).toEqual(false, "a should be false"); expect(a).toBeFalse(); + expect(a).toBeFalse("a should be false"); } } @@ -204,6 +274,7 @@ contract ExpectTest is Test { function testBytes32ToEqualPass(bytes32 a) external { expect(a).toEqual(a); + expect(a).toEqual(a, "a should equal a"); } function testBytes32ToEqualFail(bytes32 a, bytes32 b) external shouldFail { @@ -211,17 +282,28 @@ contract ExpectTest is Test { expect(a).toEqual(b); } + function testBytes32ToEqualFailWithMessage(bytes32 a, bytes32 b) external shouldFail { + ctx.assume(a != b); + expect(a).toEqual(b, "a should equal b"); + } + function testBytes32NotToEqualPass(bytes32 a, bytes32 b) external { ctx.assume(a != b); expect(a).not.toEqual(b); + expect(a).not.toEqual(b, "a should not equal b"); } function testBytes32NotToEqualFail(bytes32 a) external shouldFail { expect(a).not.toEqual(a); } + function testBytes32NotToEqualFailWithMessage(bytes32 a) external shouldFail { + expect(a).not.toEqual(a, "a should not equal a"); + } + function testBytes32ToBeTheHashOfPass(bytes memory a) external { expect(keccak256(a)).toBeTheHashOf(a); + expect(keccak256(a)).toBeTheHashOf(a, "keccak(a) should be the hash of a"); } function testBytes32ToBeTheHashOfFail(bytes32 a, bytes memory b) external shouldFail { @@ -229,15 +311,22 @@ contract ExpectTest is Test { expect(a).toBeTheHashOf(b); } + function testBytes32ToBeTheHashOfFailWithMessage(bytes32 a, bytes memory b) external shouldFail { + ctx.assume(keccak256(b) != a); + expect(a).toBeTheHashOf(b, "a should be the hash of b"); + } + function testBytes32NotToBeTheHashOfPass(bytes32 a, bytes memory b) external { ctx.assume(keccak256(b) != a); expect(a).not.toBeTheHashOf(b); + expect(a).not.toBeTheHashOf(b, "a should not be the hash of b"); } /* STRING */ function testStringToEqualPass(string memory a) external { expect(a).toEqual(a); + expect(a).toEqual(a, "a should equal a"); } function testStringToEqualFail(string memory a, string memory b) external shouldFail { @@ -245,17 +334,28 @@ contract ExpectTest is Test { expect(a).toEqual(b); } + function testStringToEqualFailWithMessage(string memory a, string memory b) external shouldFail { + ctx.assume(keccak256(bytes(a)) != keccak256(bytes(b))); + expect(a).toEqual(b, "a should equal b"); + } + function testStringNotToEqualPass(string memory a, string memory b) external { ctx.assume(keccak256(bytes(a)) != keccak256(bytes(b))); expect(a).not.toEqual(b); + expect(a).not.toEqual(b, "a should not equal b"); } function testStringNotToEqualFail(string memory a) external shouldFail { expect(a).not.toEqual(a); } + function testStringNotToEqualFailWithMessage(string memory a) external shouldFail { + expect(a).not.toEqual(a, "a should not equal a"); + } + function testStringToContainPass(string memory a, string memory b, string memory c) external { expect(string.concat(a, b, c)).toContain(b); + expect(string.concat(a, b, c)).toContain(b, "abc should contain b"); } // TODO: fuzzing? @@ -263,17 +363,31 @@ contract ExpectTest is Test { expect(string("abc")).toContain("d"); } + function testStringToContainFailWithMessage() external shouldFail { + expect(string("abc")).toContain("d", "abc should contain d"); + } + function testStringNotToContainPass() external { expect(string("abc")).not.toContain("d"); + expect(string("abc")).not.toContain("d", "abc should not contain d"); expect(string("abc")).not.toContain("abcde"); + expect(string("abc")).not.toContain("abcde", "abc should not contain abcde"); } function testStringNotToContainFail(string memory a, string memory b, string memory c) external shouldFail { expect(string.concat(a, b, c)).not.toContain(b); } + function testStringNotToContainFailWithMessage(string memory a, string memory b, string memory c) + external + shouldFail + { + expect(string.concat(a, b, c)).not.toContain(b, "abc should not contain b"); + } + function testStringToHaveLengthPass(string memory a) external { expect(a).toHaveLength(bytes(a).length); + expect(a).toHaveLength(bytes(a).length, "a should have length of bytes(a).length"); } function testStringToHaveLengthFail(string memory a, uint256 len) external shouldFail { @@ -281,6 +395,11 @@ contract ExpectTest is Test { expect(a).toHaveLength(len); } + function testStringToHaveLengthFailWithMessage(string memory a, uint256 len) external shouldFail { + ctx.assume(len != bytes(a).length); + expect(a).toHaveLength(len, "a should have length of len"); + } + function testToHaveReverted() external { CallTest t = new CallTest(); @@ -293,12 +412,19 @@ contract ExpectTest is Test { t.failWithCustomError(); expect(address(t).getCall(0)).toHaveReverted(); + expect(address(t).getCall(0)).toHaveReverted("t.failWithRevert() should have reverted"); expect(address(t).getCall(1)).toHaveReverted(); + expect(address(t).getCall(1)).toHaveReverted("t.failWithStringRevert() should have reverted"); expect(address(t).getCall(2)).toHaveReverted(); + expect(address(t).getCall(2)).toHaveReverted("t.failWithRequire() should have reverted"); expect(address(t).getCall(3)).toHaveReverted(); + expect(address(t).getCall(3)).toHaveReverted("t.failWithRequireMessage() should have reverted"); expect(address(t).getCall(4)).toHaveReverted(); + expect(address(t).getCall(4)).toHaveReverted("t.failWithCustomError() should have reverted"); expect(address(t).firstCall()).toHaveReverted(); + expect(address(t).firstCall()).toHaveReverted("first call to t should have reverted"); expect(address(t).lastCall()).toHaveReverted(); + expect(address(t).lastCall()).toHaveReverted("last call to t should have reverted"); } function testToHaveSucceeded() external { @@ -309,9 +435,13 @@ contract ExpectTest is Test { uint256 result = t.ok(); expect(address(t).getCall(0)).toHaveSucceeded(); + expect(address(t).getCall(0)).toHaveSucceeded("t.ok() should have succeeded"); expect(address(t).firstCall()).toHaveSucceeded(); + expect(address(t).firstCall()).toHaveSucceeded("first call to t should have succeeded"); expect(address(t).lastCall()).toHaveSucceeded(); + expect(address(t).lastCall()).toHaveSucceeded("last call to t should have succeeded"); expect(result).toEqual(uint256(keccak256(abi.encodePacked(uint256(69))))); + expect(result).toEqual(uint256(keccak256(abi.encodePacked(uint256(69)))), "result should equal keccak256(69)"); } function testToHaveRevertedWith() external { @@ -324,13 +454,31 @@ contract ExpectTest is Test { t.failWithCustomError(); expect(address(t).getCall(0)).toHaveRevertedWith(string("Error")); + expect(address(t).getCall(0)).toHaveRevertedWith( + string("Error"), "t.failWithStringRevert() should have reverted with 'Error'" + ); expect(address(t).getCall(1)).toHaveRevertedWith(string("Require message")); + expect(address(t).getCall(1)).toHaveRevertedWith( + string("Require message"), "t.failWithRequireMessage() should have reverted with 'Require message'" + ); expect(address(t).getCall(2)).toHaveRevertedWith(CallTest.CustomError.selector); + expect(address(t).getCall(2)).toHaveRevertedWith( + CallTest.CustomError.selector, "t.failWithCustomError() should have reverted with CustomError" + ); expect(address(t).firstCall()).toHaveRevertedWith(string("Error")); + expect(address(t).firstCall()).toHaveRevertedWith( + string("Error"), "first call to t should have reverted with 'Error'" + ); expect(address(t).lastCall()).toHaveRevertedWith(CallTest.CustomError.selector); + expect(address(t).lastCall()).toHaveRevertedWith( + CallTest.CustomError.selector, "last call to t should have reverted with CustomError" + ); bytes memory expectedError = abi.encodeWithSelector(CallTest.CustomError.selector, uint256(69)); expect(address(t).getCall(2)).toHaveRevertedWith(expectedError); + expect(address(t).getCall(2)).toHaveRevertedWith( + expectedError, "t.failWithCustomError() should have reverted with CustomError(69)" + ); } function testToHaveEmittedPass() external { @@ -341,6 +489,9 @@ contract ExpectTest is Test { t.emitEvent("foo", 123); expect(address(t).getCall(0)).toHaveEmitted("Event(string,uint256)"); + expect(address(t).getCall(0)).toHaveEmitted( + "Event(string,uint256)", string("t.emitEvent() should have emitted Event") + ); } function testToHaveEmittedFail() external shouldFail { @@ -353,6 +504,18 @@ contract ExpectTest is Test { expect(address(t).getCall(0)).toHaveEmitted("Fake(string,uint256)"); } + function testToHaveEmittedFailWithMessage() external shouldFail { + CallTest t = new CallTest(); + + watchers.watch(address(t)); + + t.emitEvent("foo", 123); + + expect(address(t).getCall(0)).toHaveEmitted( + "Fake(string,uint256)", string("t.emitEvent() should have emitted Fake") + ); + } + function testToHaveEmittedWithDataPass() external { CallTest t = new CallTest(); @@ -361,6 +524,11 @@ contract ExpectTest is Test { t.emitEvent("foo", 123); expect(address(t).getCall(0)).toHaveEmitted("Event(string,uint256)", abi.encode(uint256(123))); + expect(address(t).getCall(0)).toHaveEmitted( + "Event(string,uint256)", + abi.encode(uint256(123)), + string("t.emitEvent() should have emitted Event with data") + ); } function testToHaveEmittedWithDataFail() external shouldFail { @@ -373,6 +541,20 @@ contract ExpectTest is Test { expect(address(t).getCall(0)).toHaveEmitted("Event(string,uint256)", abi.encode(uint256(321))); } + function testToHaveEmittedWithDataFailWithMessage() external shouldFail { + CallTest t = new CallTest(); + + watchers.watch(address(t)); + + t.emitEvent("foo", 123); + + expect(address(t).getCall(0)).toHaveEmitted( + "Event(string,uint256)", + abi.encode(uint256(321)), + string("t.emitEvent() should have emitted Event with data") + ); + } + event Event(string indexed a, uint256 b); function testToHaveEmittedWithTopicsPass() external { @@ -383,6 +565,9 @@ contract ExpectTest is Test { t.emitEvent("foo", 123); expect(address(t).calls()[0]).toHaveEmitted("Event(string,uint256)", [any()]); + expect(address(t).calls()[0]).toHaveEmitted( + "Event(string,uint256)", [any()], string("t.emitEvent() should have emitted Event with topics") + ); } function testToHaveEmittedWithTopicsFail() external shouldFail { @@ -396,4 +581,51 @@ contract ExpectTest is Test { "Fake(string,uint256)", [events.topic(string("bar"))], abi.encode(uint256(123)) ); } + + function testToHaveEmittedWithTopicsFailWithMessage() external shouldFail { + CallTest t = new CallTest(); + + watchers.watch(payable(address(t))); + + t.emitEvent("foo", 123); + + expect(address(t).calls()[0]).toHaveEmitted( + "Fake(string,uint256)", + [events.topic(string("bar"))], + abi.encode(uint256(123)), + string("t.emitEvent() should have emitted Fake with topics") + ); + } + + function testToBeAContractPass() external { + CallTest t = new CallTest(); + + expect(address(t)).toBeAContract(); + expect(address(t)).toBeAContract("address(t) should be a contract"); + } + + function testToBeAContractFail() external shouldFail { + expect(address(0)).toBeAContract(); + } + + function testToBeAContractFailWithMessage() external shouldFail { + expect(address(0)).toBeAContract("address(0) should be a contract"); + } + + function testNotToBeAContractPass() external { + expect(address(0)).not.toBeAContract(); + expect(address(0)).not.toBeAContract("address(0) should not be a contract"); + } + + function testNotToBeAContractFail() external shouldFail { + CallTest t = new CallTest(); + + expect(address(t)).not.toBeAContract(); + } + + function testNotToBeAContractFailWithMessage() external shouldFail { + CallTest t = new CallTest(); + + expect(address(t)).not.toBeAContract("address(t) should not be a contract"); + } }