Implementation of an address Enumerable Set (#2061)

* Drafted Enumerable.sol.

* Drafted test framework.

* Tweaked the tests to follow oz structure.

* Coded EnumerableSet.

* Moved EnumerableSet to `utils`.

* Fixed linting.

* Improved comments.

* Tweaked contract description.

* Renamed struct to AddressSet.

* Relaxed version pragma to 0.5.0

* Removed events.

* Revert on useless operations.

* Small comment.

* Created AddressSet factory method.

* Failed transactions return false.

* Transactions now return false on failure.

* Remove comments from mock

* Rename mock functions

* Adapt tests to code style, use test-helpers

* Fix bug in remove, improve tests.

* Add changelog entry

* Add entry on Utils doc

* Add optimization for removal of last slot

* Update docs

* Fix headings of utilities documentation

Co-authored-by: Nicolás Venturo <nicolas.venturo@gmail.com>
This commit is contained in:
Alberto Cuesta Cañada
2020-01-24 17:50:24 +00:00
committed by Nicolás Venturo
parent 73abd54cbe
commit 1e0f07751e
6 changed files with 272 additions and 3 deletions

View File

@ -0,0 +1,108 @@
const { accounts, contract } = require('@openzeppelin/test-environment');
const { expectEvent } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const EnumerableSetMock = contract.fromArtifact('EnumerableSetMock');
describe('EnumerableSet', function () {
const [ accountA, accountB, accountC ] = accounts;
beforeEach(async function () {
this.set = await EnumerableSetMock.new();
});
it('starts empty', async function () {
expect(await this.set.contains(accountA)).to.equal(false);
expect(await this.set.enumerate()).to.have.same.members([]);
});
it('adds a value', async function () {
const receipt = await this.set.add(accountA);
expectEvent(receipt, 'TransactionResult', { result: true });
expect(await this.set.contains(accountA)).to.equal(true);
expect(await this.set.enumerate()).to.have.same.members([ accountA ]);
});
it('adds several values', async function () {
await this.set.add(accountA);
await this.set.add(accountB);
expect(await this.set.contains(accountA)).to.equal(true);
expect(await this.set.contains(accountB)).to.equal(true);
expect(await this.set.contains(accountC)).to.equal(false);
expect(await this.set.enumerate()).to.have.same.members([ accountA, accountB ]);
});
it('returns false when adding elements already in the set', async function () {
await this.set.add(accountA);
const receipt = (await this.set.add(accountA));
expectEvent(receipt, 'TransactionResult', { result: false });
expect(await this.set.enumerate()).to.have.same.members([ accountA ]);
});
it('removes added values', async function () {
await this.set.add(accountA);
const receipt = await this.set.remove(accountA);
expectEvent(receipt, 'TransactionResult', { result: true });
expect(await this.set.contains(accountA)).to.equal(false);
expect(await this.set.enumerate()).to.have.same.members([]);
});
it('returns false when removing elements not in the set', async function () {
const receipt = await this.set.remove(accountA);
expectEvent(receipt, 'TransactionResult', { result: false });
expect(await this.set.contains(accountA)).to.equal(false);
});
it('adds and removes multiple values', async function () {
// []
await this.set.add(accountA);
await this.set.add(accountC);
// [A, C]
await this.set.remove(accountA);
await this.set.remove(accountB);
// [C]
await this.set.add(accountB);
// [C, B]
await this.set.add(accountA);
await this.set.remove(accountC);
// [A, B]
await this.set.add(accountA);
await this.set.add(accountB);
// [A, B]
await this.set.add(accountC);
await this.set.remove(accountA);
// [B, C]
await this.set.add(accountA);
await this.set.remove(accountB);
// [A, C]
expect(await this.set.contains(accountA)).to.equal(true);
expect(await this.set.contains(accountB)).to.equal(false);
expect(await this.set.contains(accountC)).to.equal(true);
expect(await this.set.enumerate()).to.have.same.members([ accountA, accountC ]);
});
});