Refactor enumerableMap generate and tests (#4760)

pull/4764/head
Renan Souza 1 year ago committed by GitHub
parent 769071d473
commit e0ac73cd6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      scripts/generate/templates/EnumerableMap.js
  2. 18
      scripts/generate/templates/EnumerableMap.opts.js
  3. 14
      test/utils/structs/EnumerableMap.behavior.js
  4. 188
      test/utils/structs/EnumerableMap.test.js

@ -1,12 +1,6 @@
const format = require('../format-lines'); const format = require('../format-lines');
const { fromBytes32, toBytes32 } = require('./conversion'); const { fromBytes32, toBytes32 } = require('./conversion');
const { TYPES } = require('./EnumerableMap.opts');
const TYPES = [
{ name: 'UintToUintMap', keyType: 'uint256', valueType: 'uint256' },
{ name: 'UintToAddressMap', keyType: 'uint256', valueType: 'address' },
{ name: 'AddressToUintMap', keyType: 'address', valueType: 'uint256' },
{ name: 'Bytes32ToUintMap', keyType: 'bytes32', valueType: 'uint256' },
];
/* eslint-disable max-len */ /* eslint-disable max-len */
const header = `\ const header = `\

@ -0,0 +1,18 @@
const mapType = str => (str == 'uint256' ? 'Uint' : `${str.charAt(0).toUpperCase()}${str.slice(1)}`);
const formatType = (keyType, valueType) => ({
name: `${mapType(keyType)}To${mapType(valueType)}Map`,
keyType,
valueType,
});
const TYPES = [
['uint256', 'uint256'],
['uint256', 'address'],
['address', 'uint256'],
['bytes32', 'uint256'],
].map(args => formatType(...args));
module.exports = {
TYPES,
formatType,
};

@ -3,7 +3,7 @@ const { ethers } = require('hardhat');
const zip = (array1, array2) => array1.map((item, index) => [item, array2[index]]); const zip = (array1, array2) => array1.map((item, index) => [item, array2[index]]);
function shouldBehaveLikeMap(zeroValue, keyType, events) { function shouldBehaveLikeMap() {
async function expectMembersMatch(methods, keys, values) { async function expectMembersMatch(methods, keys, values) {
expect(keys.length).to.equal(values.length); expect(keys.length).to.equal(values.length);
expect(await methods.length()).to.equal(keys.length); expect(await methods.length()).to.equal(keys.length);
@ -25,7 +25,7 @@ function shouldBehaveLikeMap(zeroValue, keyType, events) {
describe('set', function () { describe('set', function () {
it('adds a key', async function () { it('adds a key', async function () {
await expect(this.methods.set(this.keyA, this.valueA)).to.emit(this.mock, events.setReturn).withArgs(true); await expect(this.methods.set(this.keyA, this.valueA)).to.emit(this.mock, this.events.setReturn).withArgs(true);
await expectMembersMatch(this.methods, [this.keyA], [this.valueA]); await expectMembersMatch(this.methods, [this.keyA], [this.valueA]);
}); });
@ -41,7 +41,7 @@ function shouldBehaveLikeMap(zeroValue, keyType, events) {
it('returns false when adding keys already in the set', async function () { it('returns false when adding keys already in the set', async function () {
await this.methods.set(this.keyA, this.valueA); await this.methods.set(this.keyA, this.valueA);
await expect(this.methods.set(this.keyA, this.valueA)).to.emit(this.mock, events.setReturn).withArgs(false); await expect(this.methods.set(this.keyA, this.valueA)).to.emit(this.mock, this.events.setReturn).withArgs(false);
await expectMembersMatch(this.methods, [this.keyA], [this.valueA]); await expectMembersMatch(this.methods, [this.keyA], [this.valueA]);
}); });
@ -58,7 +58,7 @@ function shouldBehaveLikeMap(zeroValue, keyType, events) {
it('removes added keys', async function () { it('removes added keys', async function () {
await this.methods.set(this.keyA, this.valueA); await this.methods.set(this.keyA, this.valueA);
await expect(this.methods.remove(this.keyA)).to.emit(this.mock, events.removeReturn).withArgs(true); await expect(this.methods.remove(this.keyA)).to.emit(this.mock, this.events.removeReturn).withArgs(true);
expect(await this.methods.contains(this.keyA)).to.be.false; expect(await this.methods.contains(this.keyA)).to.be.false;
await expectMembersMatch(this.methods, [], []); await expectMembersMatch(this.methods, [], []);
@ -66,7 +66,7 @@ function shouldBehaveLikeMap(zeroValue, keyType, events) {
it('returns false when removing keys not in the set', async function () { it('returns false when removing keys not in the set', async function () {
await expect(await this.methods.remove(this.keyA)) await expect(await this.methods.remove(this.keyA))
.to.emit(this.mock, events.removeReturn) .to.emit(this.mock, this.events.removeReturn)
.withArgs(false); .withArgs(false);
expect(await this.methods.contains(this.keyA)).to.be.false; expect(await this.methods.contains(this.keyA)).to.be.false;
@ -130,7 +130,7 @@ function shouldBehaveLikeMap(zeroValue, keyType, events) {
it('missing value', async function () { it('missing value', async function () {
await expect(this.methods.get(this.keyB)) await expect(this.methods.get(this.keyB))
.to.be.revertedWithCustomError(this.mock, 'EnumerableMapNonexistentKey') .to.be.revertedWithCustomError(this.mock, 'EnumerableMapNonexistentKey')
.withArgs(ethers.AbiCoder.defaultAbiCoder().encode([keyType], [this.keyB])); .withArgs(ethers.AbiCoder.defaultAbiCoder().encode([this.keyType], [this.keyB]));
}); });
}); });
@ -140,7 +140,7 @@ function shouldBehaveLikeMap(zeroValue, keyType, events) {
}); });
it('missing value', async function () { it('missing value', async function () {
expect(await this.methods.tryGet(this.keyB)).to.have.ordered.members([false, zeroValue]); expect(await this.methods.tryGet(this.keyB)).to.have.ordered.members([false, this.zeroValue]);
}); });
}); });
}); });

@ -2,6 +2,7 @@ const { ethers } = require('hardhat');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { mapValues } = require('../../helpers/iterate'); const { mapValues } = require('../../helpers/iterate');
const { randomArray, generators } = require('../../helpers/random'); const { randomArray, generators } = require('../../helpers/random');
const { TYPES, formatType } = require('../../../scripts/generate/templates/EnumerableMap.opts');
const { shouldBehaveLikeMap } = require('./EnumerableMap.behavior'); const { shouldBehaveLikeMap } = require('./EnumerableMap.behavior');
@ -14,132 +15,77 @@ const getMethods = (mock, fnSigs) => {
); );
}; };
describe('EnumerableMap', function () { const testTypes = [formatType('bytes32', 'bytes32'), ...TYPES];
// UintToAddressMap
describe('UintToAddressMap', function () { async function fixture() {
const fixture = async () => { const mock = await ethers.deployContract('$EnumerableMap');
const mock = await ethers.deployContract('$EnumerableMap');
const zeroValue = {
const [keyA, keyB, keyC] = randomArray(generators.uint256); uint256: 0n,
const [valueA, valueB, valueC] = randomArray(generators.address); address: ethers.ZeroAddress,
bytes32: ethers.ZeroHash,
const methods = getMethods(mock, { };
set: '$set(uint256,uint256,address)',
get: '$get_EnumerableMap_UintToAddressMap(uint256,uint256)', const env = Object.fromEntries(
tryGet: '$tryGet_EnumerableMap_UintToAddressMap(uint256,uint256)', testTypes.map(({ name, keyType, valueType }) => [
remove: '$remove_EnumerableMap_UintToAddressMap(uint256,uint256)', name,
length: '$length_EnumerableMap_UintToAddressMap(uint256)', {
at: '$at_EnumerableMap_UintToAddressMap(uint256,uint256)', keyType,
contains: '$contains_EnumerableMap_UintToAddressMap(uint256,uint256)', keys: randomArray(generators[keyType]),
keys: '$keys_EnumerableMap_UintToAddressMap(uint256)', values: randomArray(generators[valueType]),
});
methods: getMethods(
return { mock, keyA, keyB, keyC, valueA, valueB, valueC, methods }; mock,
}; testTypes.filter(t => keyType == t.keyType).length == 1
? {
beforeEach(async function () { set: `$set(uint256,${keyType},${valueType})`,
Object.assign(this, await loadFixture(fixture)); get: `$get(uint256,${keyType})`,
}); tryGet: `$tryGet(uint256,${keyType})`,
remove: `$remove(uint256,${keyType})`,
shouldBehaveLikeMap(ethers.ZeroAddress, 'uint256', { length: `$length_EnumerableMap_${name}(uint256)`,
setReturn: 'return$set_EnumerableMap_UintToAddressMap_uint256_address', at: `$at_EnumerableMap_${name}(uint256,uint256)`,
removeReturn: 'return$remove_EnumerableMap_UintToAddressMap_uint256', contains: `$contains(uint256,${keyType})`,
}); keys: `$keys_EnumerableMap_${name}(uint256)`,
}); }
: {
// Bytes32ToBytes32Map set: `$set(uint256,${keyType},${valueType})`,
describe('Bytes32ToBytes32Map', function () { get: `$get_EnumerableMap_${name}(uint256,${keyType})`,
const fixture = async () => { tryGet: `$tryGet_EnumerableMap_${name}(uint256,${keyType})`,
const mock = await ethers.deployContract('$EnumerableMap'); remove: `$remove_EnumerableMap_${name}(uint256,${keyType})`,
length: `$length_EnumerableMap_${name}(uint256)`,
const [keyA, keyB, keyC] = randomArray(generators.bytes32); at: `$at_EnumerableMap_${name}(uint256,uint256)`,
const [valueA, valueB, valueC] = randomArray(generators.bytes32); contains: `$contains_EnumerableMap_${name}(uint256,${keyType})`,
keys: `$keys_EnumerableMap_${name}(uint256)`,
const methods = getMethods(mock, { },
set: '$set(uint256,bytes32,bytes32)', ),
get: '$get_EnumerableMap_Bytes32ToBytes32Map(uint256,bytes32)',
tryGet: '$tryGet_EnumerableMap_Bytes32ToBytes32Map(uint256,bytes32)', zeroValue: zeroValue[valueType],
remove: '$remove_EnumerableMap_Bytes32ToBytes32Map(uint256,bytes32)', events: {
length: '$length_EnumerableMap_Bytes32ToBytes32Map(uint256)', setReturn: `return$set_EnumerableMap_${name}_${keyType}_${valueType}`,
at: '$at_EnumerableMap_Bytes32ToBytes32Map(uint256,uint256)', removeReturn: `return$remove_EnumerableMap_${name}_${keyType}`,
contains: '$contains_EnumerableMap_Bytes32ToBytes32Map(uint256,bytes32)', },
keys: '$keys_EnumerableMap_Bytes32ToBytes32Map(uint256)', },
}); ]),
);
return { mock, keyA, keyB, keyC, valueA, valueB, valueC, methods };
};
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
shouldBehaveLikeMap(ethers.ZeroHash, 'bytes32', {
setReturn: 'return$set_EnumerableMap_Bytes32ToBytes32Map_bytes32_bytes32',
removeReturn: 'return$remove_EnumerableMap_Bytes32ToBytes32Map_bytes32',
});
});
// UintToUintMap
describe('UintToUintMap', function () {
const fixture = async () => {
const mock = await ethers.deployContract('$EnumerableMap');
const [keyA, keyB, keyC] = randomArray(generators.uint256);
const [valueA, valueB, valueC] = randomArray(generators.uint256);
const methods = getMethods(mock, {
set: '$set(uint256,uint256,uint256)',
get: '$get_EnumerableMap_UintToUintMap(uint256,uint256)',
tryGet: '$tryGet_EnumerableMap_UintToUintMap(uint256,uint256)',
remove: '$remove_EnumerableMap_UintToUintMap(uint256,uint256)',
length: '$length_EnumerableMap_UintToUintMap(uint256)',
at: '$at_EnumerableMap_UintToUintMap(uint256,uint256)',
contains: '$contains_EnumerableMap_UintToUintMap(uint256,uint256)',
keys: '$keys_EnumerableMap_UintToUintMap(uint256)',
});
return { mock, keyA, keyB, keyC, valueA, valueB, valueC, methods };
};
beforeEach(async function () { return { mock, env };
Object.assign(this, await loadFixture(fixture)); }
});
shouldBehaveLikeMap(0n, 'uint256', { describe('EnumerableMap', function () {
setReturn: 'return$set_EnumerableMap_UintToUintMap_uint256_uint256', beforeEach(async function () {
removeReturn: 'return$remove_EnumerableMap_UintToUintMap_uint256', Object.assign(this, await loadFixture(fixture));
});
}); });
// Bytes32ToUintMap // UintToAddressMap
describe('Bytes32ToUintMap', function () { for (const { name } of testTypes) {
const fixture = async () => { describe(name, function () {
const mock = await ethers.deployContract('$EnumerableMap'); beforeEach(async function () {
Object.assign(this, this.env[name]);
const [keyA, keyB, keyC] = randomArray(generators.bytes32); [this.keyA, this.keyB, this.keyC] = this.keys;
const [valueA, valueB, valueC] = randomArray(generators.uint256); [this.valueA, this.valueB, this.valueC] = this.values;
const methods = getMethods(mock, {
set: '$set(uint256,bytes32,uint256)',
get: '$get_EnumerableMap_Bytes32ToUintMap(uint256,bytes32)',
tryGet: '$tryGet_EnumerableMap_Bytes32ToUintMap(uint256,bytes32)',
remove: '$remove_EnumerableMap_Bytes32ToUintMap(uint256,bytes32)',
length: '$length_EnumerableMap_Bytes32ToUintMap(uint256)',
at: '$at_EnumerableMap_Bytes32ToUintMap(uint256,uint256)',
contains: '$contains_EnumerableMap_Bytes32ToUintMap(uint256,bytes32)',
keys: '$keys_EnumerableMap_Bytes32ToUintMap(uint256)',
}); });
return { mock, keyA, keyB, keyC, valueA, valueB, valueC, methods }; shouldBehaveLikeMap();
};
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
shouldBehaveLikeMap(0n, 'bytes32', {
setReturn: 'return$set_EnumerableMap_Bytes32ToUintMap_bytes32_uint256',
removeReturn: 'return$remove_EnumerableMap_Bytes32ToUintMap_bytes32',
}); });
}); }
}); });

Loading…
Cancel
Save