forked from OpenZeppelin/openzeppelin-contracts
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathArrays.test.js
More file actions
134 lines (121 loc) · 4.65 KB
/
Arrays.test.js
File metadata and controls
134 lines (121 loc) · 4.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
const { ethers } = require('hardhat');
const { expect } = require('chai');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { randomArray, generators } = require('../helpers/random');
// See https://en.cppreference.com/w/cpp/algorithm/ranges/lower_bound
const lowerBound = (array, value) => {
const i = array.findIndex(element => value <= element);
return i == -1 ? array.length : i;
};
// See https://en.cppreference.com/w/cpp/algorithm/upper_bound
const upperBound = (array, value) => {
const i = array.findIndex(element => value < element);
return i == -1 ? array.length : i;
};
const hasDuplicates = array => array.some((v, i) => array.indexOf(v) != i);
describe('Arrays', function () {
describe('search', function () {
for (const [title, { array, tests }] of Object.entries({
'Even number of elements': {
array: [11n, 12n, 13n, 14n, 15n, 16n, 17n, 18n, 19n, 20n],
tests: {
'basic case': 16n,
'first element': 11n,
'last element': 20n,
'searched value is over the upper boundary': 32n,
'searched value is under the lower boundary': 2n,
},
},
'Odd number of elements': {
array: [11n, 12n, 13n, 14n, 15n, 16n, 17n, 18n, 19n, 20n, 21n],
tests: {
'basic case': 16n,
'first element': 11n,
'last element': 21n,
'searched value is over the upper boundary': 32n,
'searched value is under the lower boundary': 2n,
},
},
'Array with gap': {
array: [11n, 12n, 13n, 14n, 15n, 20n, 21n, 22n, 23n, 24n],
tests: {
'search value in gap': 17n,
},
},
'Array with duplicated elements': {
array: [0n, 10n, 10n, 10n, 10n, 10n, 10n, 10n, 20n],
tests: {
'search value is duplicated': 10n,
},
},
'Array with duplicated first element': {
array: [10n, 10n, 10n, 10n, 10n, 10n, 10n, 20n],
tests: {
'search value is duplicated first element': 10n,
},
},
'Array with duplicated last element': {
array: [0n, 10n, 10n, 10n, 10n, 10n, 10n, 10n],
tests: {
'search value is duplicated last element': 10n,
},
},
'Empty array': {
array: [],
tests: {
'always returns 0 for empty array': 10n,
},
},
})) {
describe(title, function () {
const fixture = async () => {
return { mock: await ethers.deployContract('Uint256ArraysMock', [array]) };
};
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
for (const [name, input] of Object.entries(tests)) {
describe(name, function () {
it('[deprecated] findUpperBound', async function () {
// findUpperBound does not support duplicated
if (hasDuplicates(array)) this.skip();
expect(await this.mock.findUpperBound(input)).to.be.equal(lowerBound(array, input));
});
it('lowerBound', async function () {
expect(await this.mock.lowerBound(input)).to.be.equal(lowerBound(array, input));
expect(await this.mock.lowerBoundMemory(array, input)).to.be.equal(lowerBound(array, input));
});
it('upperBound', async function () {
expect(await this.mock.upperBound(input)).to.be.equal(upperBound(array, input));
expect(await this.mock.upperBoundMemory(array, input)).to.be.equal(upperBound(array, input));
});
});
}
});
}
});
describe('unsafeAccess', function () {
for (const [title, { artifact, elements }] of Object.entries({
address: { artifact: 'AddressArraysMock', elements: randomArray(generators.address, 10) },
bytes32: { artifact: 'Bytes32ArraysMock', elements: randomArray(generators.bytes32, 10) },
uint256: { artifact: 'Uint256ArraysMock', elements: randomArray(generators.uint256, 10) },
})) {
describe(title, function () {
const fixture = async () => {
return { mock: await ethers.deployContract(artifact, [elements]) };
};
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});
for (const i in elements) {
it(`unsafeAccess within bounds #${i}`, async function () {
expect(await this.mock.unsafeAccess(i)).to.equal(elements[i]);
});
}
it('unsafeAccess outside bounds', async function () {
await expect(this.mock.unsafeAccess(elements.length)).to.not.be.rejected;
});
});
}
});
});