Skip to content
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: mock multidimensional array #74

Merged
merged 2 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ greeter.set__greeting('Hola');
- Please, note that if you want to mock `internal` functions, you **must** make them `virtual`. The tool will not generate mocks for internal functions that are not virtual.
- Cannot set `private` variables and mock `private` functions.
- Mocking of structs containing mappings is not supported.
- Mocking of multi-dimensional struct arrays is not supported.
gas1cent marked this conversation as resolved.
Show resolved Hide resolved

# Licensing

Expand Down
12 changes: 11 additions & 1 deletion solidity/contracts/utils/ContractG.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,17 @@ contract ContractG {

NestedStruct public nestedStruct;

// CommonStruct[][] public multidimensionalStruct;
CommonStruct[] public structArray;

CommonStruct[][] public twoDimensionalStruct;

CommonStruct[][][] public threeDimensionalStruct;

uint256[][] public twoDimensionalArray;

string[][] public twoDimensionalStringArray;

uint256[][][] public threeDimensionalArray;

function setNestedStruct(NestedStruct memory _nestedStruct) public {
nestedStruct = _nestedStruct;
Expand Down
230 changes: 230 additions & 0 deletions solidity/test/utils/ContractG.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Test} from 'forge-std/Test.sol';
import {MockContractG} from 'test/smock/contracts/utils/MockContractG.sol';
import {SmockHelper} from 'test/smock/SmockHelper.sol';
import {ContractG} from 'contracts/utils/ContractG.sol';

contract UnitMockContractG is Test, SmockHelper {
address internal _owner = makeAddr('owner');
MockContractG internal _contractTest;

function setUp() public {
vm.prank(_owner);

_contractTest = MockContractG(deployMock('TestContractG', type(MockContractG).creationCode, abi.encode()));
}

function test_Set_MyStructArray() public {
ContractG.CommonStruct[] memory _myStructArray = new ContractG.CommonStruct[](2);
_myStructArray[0] = ContractG.CommonStruct(10);
_myStructArray[1] = ContractG.CommonStruct(20);

_contractTest.set_structArray(_myStructArray);
(uint256 _value) = _contractTest.structArray(0);
assertEq(_value, 10);

(_value) = _contractTest.structArray(1);
assertEq(_value, 20);
}

function test_Set_TwoDimensionalArray() public {
uint256[][] memory _twoDimensionalStruct = new uint256[][](2);
_twoDimensionalStruct[0] = new uint256[](2);
_twoDimensionalStruct[1] = new uint256[](2);

_twoDimensionalStruct[0][0] = 10;
_twoDimensionalStruct[0][1] = 20;
_twoDimensionalStruct[1][0] = 30;
_twoDimensionalStruct[1][1] = 40;

_contractTest.set_twoDimensionalArray(_twoDimensionalStruct);
(uint256 _value) = _contractTest.twoDimensionalArray(0, 0);
assertEq(_value, 10);

(_value) = _contractTest.twoDimensionalArray(0, 1);
assertEq(_value, 20);

(_value) = _contractTest.twoDimensionalArray(1, 0);
assertEq(_value, 30);

(_value) = _contractTest.twoDimensionalArray(1, 1);
assertEq(_value, 40);
}

function test_Call_TwoDimensionalArray() public {
uint256[][] memory _twoDimensionalStruct = new uint256[][](2);
_twoDimensionalStruct[0] = new uint256[](2);
_twoDimensionalStruct[1] = new uint256[](2);

_twoDimensionalStruct[0][0] = 10;
_twoDimensionalStruct[0][1] = 20;
_twoDimensionalStruct[1][0] = 30;
_twoDimensionalStruct[1][1] = 40;

_contractTest.mock_call_twoDimensionalArray(0, 0, _twoDimensionalStruct[0][0]);
(uint256 _value) = _contractTest.twoDimensionalArray(0, 0);
assertEq(_value, 10);

_contractTest.mock_call_twoDimensionalArray(0, 1, _twoDimensionalStruct[0][1]);
(_value) = _contractTest.twoDimensionalArray(0, 1);
assertEq(_value, 20);

_contractTest.mock_call_twoDimensionalArray(1, 0, _twoDimensionalStruct[1][0]);
(_value) = _contractTest.twoDimensionalArray(1, 0);
assertEq(_value, 30);

_contractTest.mock_call_twoDimensionalArray(1, 1, _twoDimensionalStruct[1][1]);
(_value) = _contractTest.twoDimensionalArray(1, 1);
assertEq(_value, 40);
}

function test_Set_ThreeDimensionalArray() public {
uint256[][][] memory _threeDimensionalStruct = new uint256[][][](2);
_threeDimensionalStruct[0] = new uint256[][](2);
_threeDimensionalStruct[1] = new uint256[][](2);

_threeDimensionalStruct[0][0] = new uint256[](2);
_threeDimensionalStruct[0][1] = new uint256[](2);
_threeDimensionalStruct[1][0] = new uint256[](2);
_threeDimensionalStruct[1][1] = new uint256[](2);

_threeDimensionalStruct[0][0][0] = 10;
_threeDimensionalStruct[0][0][1] = 20;
_threeDimensionalStruct[0][1][0] = 30;
_threeDimensionalStruct[0][1][1] = 40;
_threeDimensionalStruct[1][0][0] = 50;
_threeDimensionalStruct[1][0][1] = 60;
_threeDimensionalStruct[1][1][0] = 70;
_threeDimensionalStruct[1][1][1] = 80;

_contractTest.set_threeDimensionalArray(_threeDimensionalStruct);
(uint256 _value) = _contractTest.threeDimensionalArray(0, 0, 0);
assertEq(_value, 10);

(_value) = _contractTest.threeDimensionalArray(0, 0, 1);
assertEq(_value, 20);

(_value) = _contractTest.threeDimensionalArray(0, 1, 0);
assertEq(_value, 30);

(_value) = _contractTest.threeDimensionalArray(0, 1, 1);
assertEq(_value, 40);

(_value) = _contractTest.threeDimensionalArray(1, 0, 0);
assertEq(_value, 50);

(_value) = _contractTest.threeDimensionalArray(1, 0, 1);
assertEq(_value, 60);

(_value) = _contractTest.threeDimensionalArray(1, 1, 0);
assertEq(_value, 70);

(_value) = _contractTest.threeDimensionalArray(1, 1, 1);
assertEq(_value, 80);
}

function test_Call_ThreeDimensionalArray() public {
uint256[][][] memory _threeDimensionalStruct = new uint256[][][](2);
_threeDimensionalStruct[0] = new uint256[][](2);
_threeDimensionalStruct[1] = new uint256[][](2);

_threeDimensionalStruct[0][0] = new uint256[](2);
_threeDimensionalStruct[0][1] = new uint256[](2);
_threeDimensionalStruct[1][0] = new uint256[](2);
_threeDimensionalStruct[1][1] = new uint256[](2);

_threeDimensionalStruct[0][0][0] = 10;
_threeDimensionalStruct[0][0][1] = 20;
_threeDimensionalStruct[0][1][0] = 30;
_threeDimensionalStruct[0][1][1] = 40;
_threeDimensionalStruct[1][0][0] = 50;
_threeDimensionalStruct[1][0][1] = 60;
_threeDimensionalStruct[1][1][0] = 70;
_threeDimensionalStruct[1][1][1] = 80;

_contractTest.mock_call_threeDimensionalArray(0, 0, 0, _threeDimensionalStruct[0][0][0]);
(uint256 _value) = _contractTest.threeDimensionalArray(0, 0, 0);
assertEq(_value, 10);

_contractTest.mock_call_threeDimensionalArray(0, 0, 1, _threeDimensionalStruct[0][0][1]);
(_value) = _contractTest.threeDimensionalArray(0, 0, 1);
assertEq(_value, 20);

_contractTest.mock_call_threeDimensionalArray(0, 1, 0, _threeDimensionalStruct[0][1][0]);
(_value) = _contractTest.threeDimensionalArray(0, 1, 0);
assertEq(_value, 30);

_contractTest.mock_call_threeDimensionalArray(0, 1, 1, _threeDimensionalStruct[0][1][1]);
(_value) = _contractTest.threeDimensionalArray(0, 1, 1);
assertEq(_value, 40);

_contractTest.mock_call_threeDimensionalArray(1, 0, 0, _threeDimensionalStruct[1][0][0]);
(_value) = _contractTest.threeDimensionalArray(1, 0, 0);
assertEq(_value, 50);

_contractTest.mock_call_threeDimensionalArray(1, 0, 1, _threeDimensionalStruct[1][0][1]);
(_value) = _contractTest.threeDimensionalArray(1, 0, 1);
assertEq(_value, 60);

_contractTest.mock_call_threeDimensionalArray(1, 1, 0, _threeDimensionalStruct[1][1][0]);
(_value) = _contractTest.threeDimensionalArray(1, 1, 0);
assertEq(_value, 70);

_contractTest.mock_call_threeDimensionalArray(1, 1, 1, _threeDimensionalStruct[1][1][1]);
(_value) = _contractTest.threeDimensionalArray(1, 1, 1);
assertEq(_value, 80);
}

function test_Set_TwoDimensionalStringArray() public {
string[][] memory _twoDimensionalStringArray = new string[][](2);
_twoDimensionalStringArray[0] = new string[](2);
_twoDimensionalStringArray[1] = new string[](2);

_twoDimensionalStringArray[0][0] = '10';
_twoDimensionalStringArray[0][1] = '20';
_twoDimensionalStringArray[1][0] = '30';
_twoDimensionalStringArray[1][1] = '40';

_contractTest.set_twoDimensionalStringArray(_twoDimensionalStringArray);
(string memory _value) = _contractTest.twoDimensionalStringArray(0, 0);
assertEq(_value, '10');

(_value) = _contractTest.twoDimensionalStringArray(0, 1);
assertEq(_value, '20');

(_value) = _contractTest.twoDimensionalStringArray(1, 0);
assertEq(_value, '30');

(_value) = _contractTest.twoDimensionalStringArray(1, 1);
assertEq(_value, '40');
}

function test_Call_TwoDimensionalStringArray() public {
string[][] memory _twoDimensionalStringArray = new string[][](2);
_twoDimensionalStringArray[0] = new string[](2);
_twoDimensionalStringArray[1] = new string[](2);

_twoDimensionalStringArray[0][0] = '10';
_twoDimensionalStringArray[0][1] = '20';
_twoDimensionalStringArray[1][0] = '30';
_twoDimensionalStringArray[1][1] = '40';

_contractTest.mock_call_twoDimensionalStringArray(0, 0, _twoDimensionalStringArray[0][0]);
(string memory _value) = _contractTest.twoDimensionalStringArray(0, 0);
assertEq(_value, '10');

_contractTest.mock_call_twoDimensionalStringArray(0, 1, _twoDimensionalStringArray[0][1]);
(_value) = _contractTest.twoDimensionalStringArray(0, 1);
assertEq(_value, '20');

_contractTest.mock_call_twoDimensionalStringArray(1, 0, _twoDimensionalStringArray[1][0]);
(_value) = _contractTest.twoDimensionalStringArray(1, 0);
assertEq(_value, '30');

_contractTest.mock_call_twoDimensionalStringArray(1, 1, _twoDimensionalStringArray[1][1]);
(_value) = _contractTest.twoDimensionalStringArray(1, 1);
assertEq(_value, '40');
}
}
12 changes: 11 additions & 1 deletion src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export function arrayVariableContext(node: VariableDeclaration): ArrayVariableCo
const arrayType: string = sanitizeParameterType(explicitTypeStorageLocation(node.typeString));

// Base type
const baseType: string = sanitizeParameterType(explicitTypeStorageLocation(node.vType['vBaseType'].typeString));
let baseType: string = sanitizeParameterType(explicitTypeStorageLocation(node.vType['vBaseType'].typeString));

// Struct flag
const isStructArray: boolean = node.typeString.startsWith('struct ');
Expand All @@ -217,6 +217,14 @@ export function arrayVariableContext(node: VariableDeclaration): ArrayVariableCo
// If the array is internal we don't create mockCall for it
const isInternal: boolean = node.visibility === 'internal';

// Check if the array is multi-dimensional
const dimensionsQuantity = node.typeString.split('[]').length - 1;
const isMultiDimensional = dimensionsQuantity > 1;
const isMultiDimensionalStruct = isMultiDimensional && isStructArray;
const dimensions = [...Array(dimensionsQuantity).keys()];

if (isMultiDimensional) baseType = explicitTypeStorageLocation(node.typeString.replace(/\[\]/g, ''));
gas1cent marked this conversation as resolved.
Show resolved Hide resolved

return {
setFunction: {
functionName: arrayName,
Expand All @@ -231,6 +239,8 @@ export function arrayVariableContext(node: VariableDeclaration): ArrayVariableCo
},
isInternal: isInternal,
isStructArray: isStructArray,
isMultiDimensionalStruct,
dimensions,
};
}

Expand Down
54 changes: 29 additions & 25 deletions src/templates/partials/array-state-variable.hbs
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
function set_{{setFunction.functionName}}({{setFunction.arrayType}} _{{setFunction.paramName}}) public {
{{#if isStructArray}}
for (uint256 _i; _i < _{{setFunction.paramName}}.length; ++_i) {
{{setFunction.functionName}}.push(_{{setFunction.paramName}}[_i]);
}
{{else}}
{{setFunction.functionName}} = _{{setFunction.paramName}};
{{/if}}
}
{{#unless isMultiDimensionalStruct}}
function set_{{setFunction.functionName}}({{setFunction.arrayType}} _{{setFunction.paramName}}) public {
{{#if isStructArray}}
for (uint256 _i; _i < _{{setFunction.paramName}}.length; ++_i) {
{{setFunction.functionName}}.push(_{{setFunction.paramName}}[_i]);
}
{{else}}
{{setFunction.functionName}} = _{{setFunction.paramName}};
{{/if}}
}

{{#unless isInternal}}
function mock_call_{{mockFunction.functionName}}(uint256 _index, {{mockFunction.baseType}} _value) public {
vm.mockCall(
address(this),
abi.encodeWithSignature('{{mockFunction.functionName}}(uint256)', _index),
abi.encode(
{{#if isStructArray}}
{{#each mockFunction.structFields}}
_value.{{this}}{{#unless @last}}, {{/unless}}
{{/each}}
{{else}}
_value
{{/if}}
)
);
}
{{#unless isInternal}}
function mock_call_{{mockFunction.functionName}}({{#each dimensions}}uint256 _index{{@index}}, {{/each}} {{mockFunction.baseType}} _value) public {
vm.mockCall(
address(this),
abi.encodeWithSignature(
'{{mockFunction.functionName}}({{#each dimensions}}uint256{{#unless @last}},{{/unless}}{{/each}})',
{{#each dimensions}}_index{{@index}}{{#unless @last}}, {{/unless}} {{/each}}),
abi.encode(
{{#if isStructArray}}
{{#each mockFunction.structFields}}
_value.{{this}}{{#unless @last}}, {{/unless}}
{{/each}}
{{else}}
_value
{{/if}}
)
);
}
{{/unless}}
{{/unless}}
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export interface ArrayVariableContext {
};
isInternal: boolean;
isStructArray: boolean;
isMultiDimensionalStruct: boolean;
dimensions: number[];
}

export interface StateVariableContext {
Expand Down
Loading
Loading