Added unit tests for SafeMath

This commit is contained in:
Alex Towle 2019-07-26 13:11:29 -07:00
parent 1634c90179
commit 4a4d2e7079
8 changed files with 225 additions and 4 deletions

View File

@ -35,6 +35,7 @@
"src/interfaces/IOwnable.sol",
"test/TestConstants.sol",
"test/TestLibAddressArray.sol",
"test/TestLibBytes.sol"
"test/TestLibBytes.sol",
"test/TestSafeMath.sol"
]
}

View File

@ -0,0 +1,66 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
import "../src/SafeMath.sol";
contract TestSafeMath is
SafeMath
{
function externalSafeMul(uint256 a, uint256 b)
external
pure
returns (uint256)
{
return _safeMul(a, b);
}
function externalSafeSub(uint256 a, uint256 b)
external
pure
returns (uint256)
{
return _safeSub(a, b);
}
function externalSafeAdd(uint256 a, uint256 b)
external
pure
returns (uint256)
{
return _safeAdd(a, b);
}
function externalMaxUint256(uint256 a, uint256 b)
external
pure
returns (uint256)
{
return _max256(a, b);
}
function externalMinUint256(uint256 a, uint256 b)
external
pure
returns (uint256)
{
return _min256(a, b);
}
}

View File

@ -34,7 +34,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "./generated-artifacts/@(IOwnable|LibAddress|LibBytes|LibEIP712|Ownable|ReentrancyGuard|RichErrors|SafeMath|TestConstants|TestLibAddressArray|TestLibBytes).json",
"abis": "./generated-artifacts/@(IOwnable|LibAddress|LibBytes|LibEIP1271|LibEIP712|Ownable|ReentrancyGuard|RichErrors|SafeMath|TestConstants|TestLibAddressArray|TestLibBytes|TestSafeMath).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@ -8,6 +8,7 @@ import { ContractArtifact } from 'ethereum-types';
import * as IOwnable from '../generated-artifacts/IOwnable.json';
import * as LibAddress from '../generated-artifacts/LibAddress.json';
import * as LibBytes from '../generated-artifacts/LibBytes.json';
import * as LibEIP1271 from '../generated-artifacts/LibEIP1271.json';
import * as LibEIP712 from '../generated-artifacts/LibEIP712.json';
import * as Ownable from '../generated-artifacts/Ownable.json';
import * as ReentrancyGuard from '../generated-artifacts/ReentrancyGuard.json';
@ -16,9 +17,11 @@ import * as SafeMath from '../generated-artifacts/SafeMath.json';
import * as TestConstants from '../generated-artifacts/TestConstants.json';
import * as TestLibAddressArray from '../generated-artifacts/TestLibAddressArray.json';
import * as TestLibBytes from '../generated-artifacts/TestLibBytes.json';
import * as TestSafeMath from '../generated-artifacts/TestSafeMath.json';
export const artifacts = {
LibAddress: LibAddress as ContractArtifact,
LibBytes: LibBytes as ContractArtifact,
LibEIP1271: LibEIP1271 as ContractArtifact,
LibEIP712: LibEIP712 as ContractArtifact,
Ownable: Ownable as ContractArtifact,
ReentrancyGuard: ReentrancyGuard as ContractArtifact,
@ -28,4 +31,5 @@ export const artifacts = {
TestConstants: TestConstants as ContractArtifact,
TestLibAddressArray: TestLibAddressArray as ContractArtifact,
TestLibBytes: TestLibBytes as ContractArtifact,
TestSafeMath: TestSafeMath as ContractArtifact,
};

View File

@ -6,6 +6,7 @@
export * from '../generated-wrappers/i_ownable';
export * from '../generated-wrappers/lib_address';
export * from '../generated-wrappers/lib_bytes';
export * from '../generated-wrappers/lib_e_i_p1271';
export * from '../generated-wrappers/lib_e_i_p712';
export * from '../generated-wrappers/ownable';
export * from '../generated-wrappers/reentrancy_guard';
@ -14,3 +15,4 @@ export * from '../generated-wrappers/safe_math';
export * from '../generated-wrappers/test_constants';
export * from '../generated-wrappers/test_lib_address_array';
export * from '../generated-wrappers/test_lib_bytes';
export * from '../generated-wrappers/test_safe_math';

View File

@ -0,0 +1,146 @@
import { chaiSetup, constants, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { BigNumber, SafeMathRevertErrors } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import { artifacts, TestSafeMathContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
function toBN(a: number | string): BigNumber {
return new BigNumber(a);
}
describe('SafeMath', () => {
let safeMath: TestSafeMathContract;
before(async () => {
await blockchainLifecycle.startAsync();
// Deploy SafeMath
safeMath = await TestSafeMathContract.deployFrom0xArtifactAsync(
artifacts.TestSafeMath,
provider,
txDefaults,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
describe('_safeMul', () => {
it('should return zero if first argument is zero', async () => {
const result = await safeMath.externalSafeMul.callAsync(constants.ZERO_AMOUNT, toBN(1));
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should return zero if second argument is zero', async () => {
const result = await safeMath.externalSafeMul.callAsync(toBN(1), constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should revert if the multiplication overflows', async () => {
const a = toBN('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); // The largest uint256 number
const b = toBN(2);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256MultiplicationOverflow,
a,
b,
);
return expect(safeMath.externalSafeMul.callAsync(a, b)).to.revertWith(expectedError);
});
it('should calculate correct value for values that don\'t overflow', async () => {
const result = await safeMath.externalSafeMul.callAsync(toBN(15), toBN(13));
expect(result).bignumber.to.be.eq(toBN(195));
});
});
describe('_safeSub', () => {
it('should throw if the subtraction underflows', async () => {
const a = toBN(0);
const b = toBN(1);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256SubtractionUnderflow,
a,
b,
);
return expect(safeMath.externalSafeSub.callAsync(a, b)).to.revertWith(expectedError);
});
it('should calculate correct value for values that are equal', async () => {
const result = await safeMath.externalSafeMul.callAsync(constants.ZERO_AMOUNT, constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should calculate correct value for values that are not equal', async () => {
const result = await safeMath.externalSafeSub.callAsync(toBN(15), toBN(13));
expect(result).bignumber.to.be.eq(toBN(2));
});
});
describe('_safeAdd', () => {
it('should throw if the addition overflows', async () => {
const a = toBN('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); // The largest uint256 number
const b = toBN(1);
const expectedError = new SafeMathRevertErrors.SafeMathError(
SafeMathRevertErrors.SafeMathErrorCodes.Uint256AdditionOverflow,
a,
b,
);
return expect(safeMath.externalSafeAdd.callAsync(a, b)).to.revertWith(expectedError);
});
it('should calculate correct value if addition does not overflow', async () => {
const result = await safeMath.externalSafeAdd.callAsync(toBN(15), toBN(13));
expect(result).bignumber.to.be.eq(toBN(28));
});
it('should calculate correct value if first argument is zero', async () => {
const result = await safeMath.externalSafeAdd.callAsync(constants.ZERO_AMOUNT, toBN(13));
expect(result).bignumber.to.be.eq(toBN(13));
});
it('should calculate correct value if second argument is zero', async () => {
const result = await safeMath.externalSafeAdd.callAsync(toBN(13), constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(toBN(13));
});
});
describe('_maxUint256', () => {
it('should return first argument if it is greater than the second', async () => {
const result = await safeMath.externalMaxUint256.callAsync(toBN(13), constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(toBN(13));
});
it('should return first argument if it is equal the second', async () => {
const result = await safeMath.externalMaxUint256.callAsync(constants.ZERO_AMOUNT, constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should return second argument if it is greater than the first', async () => {
const result = await safeMath.externalMaxUint256.callAsync(constants.ZERO_AMOUNT, toBN(13));
expect(result).bignumber.to.be.eq(toBN(13));
});
});
describe('_minUint256', () => {
it('should return first argument if it is less than the second', async () => {
const result = await safeMath.externalMaxUint256.callAsync(constants.ZERO_AMOUNT, toBN(13));
expect(result).bignumber.to.be.eq(toBN(13));
});
it('should return first argument if it is equal the second', async () => {
const result = await safeMath.externalMaxUint256.callAsync(constants.ZERO_AMOUNT, constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(constants.ZERO_AMOUNT);
});
it('should return second argument if it is greater than the first', async () => {
const result = await safeMath.externalMaxUint256.callAsync(toBN(13), constants.ZERO_AMOUNT);
expect(result).bignumber.to.be.eq(toBN(13));
});
});
});

View File

@ -6,6 +6,7 @@
"generated-artifacts/IOwnable.json",
"generated-artifacts/LibAddress.json",
"generated-artifacts/LibBytes.json",
"generated-artifacts/LibEIP1271.json",
"generated-artifacts/LibEIP712.json",
"generated-artifacts/Ownable.json",
"generated-artifacts/ReentrancyGuard.json",
@ -13,7 +14,8 @@
"generated-artifacts/SafeMath.json",
"generated-artifacts/TestConstants.json",
"generated-artifacts/TestLibAddressArray.json",
"generated-artifacts/TestLibBytes.json"
"generated-artifacts/TestLibBytes.json",
"generated-artifacts/TestSafeMath.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@ -10,7 +10,7 @@ export enum SafeMathErrorCodes {
}
export class SafeMathError extends RevertError {
constructor(error?: SafeMathErrorCodes, a?: BigNumber | number | string, b?: BigNumber | number | string) {
constructor(error?: SafeMathErrorCodes, a?: BigNumber, b?: BigNumber) {
super('SafeMathError', 'SafeMathError(uint8 error, uint256 a, uint256 b)', {
error,
a,