@@ -16,9 +16,56 @@ const upperBound = (array, value) => {
1616 return i == - 1 ? array . length : i ;
1717} ;
1818
19+ // By default, js "sort" cast to string and then sort in alphabetical order. Use this to sort numbers.
20+ const compareNumbers = ( a , b ) => ( a > b ? 1 : a < b ? - 1 : 0 ) ;
21+
1922const hasDuplicates = array => array . some ( ( v , i ) => array . indexOf ( v ) != i ) ;
2023
2124describe ( 'Arrays' , function ( ) {
25+ const fixture = async ( ) => {
26+ return { mock : await ethers . deployContract ( '$Arrays' ) } ;
27+ } ;
28+
29+ beforeEach ( async function ( ) {
30+ Object . assign ( this , await loadFixture ( fixture ) ) ;
31+ } ) ;
32+
33+ describe ( 'sort' , function ( ) {
34+ for ( const length of [ 0 , 1 , 2 , 8 , 32 , 128 ] ) {
35+ it ( `sort array of length ${ length } ` , async function ( ) {
36+ this . elements = randomArray ( generators . uint256 , length ) ;
37+ this . expected = Array . from ( this . elements ) . sort ( compareNumbers ) ;
38+ } ) ;
39+
40+ if ( length > 1 ) {
41+ it ( `sort array of length ${ length } (identical elements)` , async function ( ) {
42+ this . elements = Array ( length ) . fill ( generators . uint256 ( ) ) ;
43+ this . expected = this . elements ;
44+ } ) ;
45+
46+ it ( `sort array of length ${ length } (already sorted)` , async function ( ) {
47+ this . elements = randomArray ( generators . uint256 , length ) . sort ( compareNumbers ) ;
48+ this . expected = this . elements ;
49+ } ) ;
50+
51+ it ( `sort array of length ${ length } (sorted in reverse order)` , async function ( ) {
52+ this . elements = randomArray ( generators . uint256 , length ) . sort ( compareNumbers ) . reverse ( ) ;
53+ this . expected = Array . from ( this . elements ) . reverse ( ) ;
54+ } ) ;
55+
56+ it ( `sort array of length ${ length } (almost sorted)` , async function ( ) {
57+ this . elements = randomArray ( generators . uint256 , length ) . sort ( compareNumbers ) ;
58+ this . expected = Array . from ( this . elements ) ;
59+ // rotate (move the last element to the front) for an almost sorted effect
60+ this . elements . unshift ( this . elements . pop ( ) ) ;
61+ } ) ;
62+ }
63+ }
64+ afterEach ( async function ( ) {
65+ expect ( await this . mock . $sort ( this . elements ) ) . to . deep . equal ( this . expected ) ;
66+ } ) ;
67+ } ) ;
68+
2269 describe ( 'search' , function ( ) {
2370 for ( const [ title , { array, tests } ] of Object . entries ( {
2471 'Even number of elements' : {
@@ -74,7 +121,7 @@ describe('Arrays', function () {
74121 } ) ) {
75122 describe ( title , function ( ) {
76123 const fixture = async ( ) => {
77- return { mock : await ethers . deployContract ( 'Uint256ArraysMock' , [ array ] ) } ;
124+ return { instance : await ethers . deployContract ( 'Uint256ArraysMock' , [ array ] ) } ;
78125 } ;
79126
80127 beforeEach ( async function ( ) {
@@ -86,20 +133,20 @@ describe('Arrays', function () {
86133 it ( '[deprecated] findUpperBound' , async function ( ) {
87134 // findUpperBound does not support duplicated
88135 if ( hasDuplicates ( array ) ) {
89- expect ( await this . mock . findUpperBound ( input ) ) . to . be . equal ( upperBound ( array , input ) - 1 ) ;
136+ expect ( await this . instance . findUpperBound ( input ) ) . to . equal ( upperBound ( array , input ) - 1 ) ;
90137 } else {
91- expect ( await this . mock . findUpperBound ( input ) ) . to . be . equal ( lowerBound ( array , input ) ) ;
138+ expect ( await this . instance . findUpperBound ( input ) ) . to . equal ( lowerBound ( array , input ) ) ;
92139 }
93140 } ) ;
94141
95142 it ( 'lowerBound' , async function ( ) {
96- expect ( await this . mock . lowerBound ( input ) ) . to . be . equal ( lowerBound ( array , input ) ) ;
97- expect ( await this . mock . lowerBoundMemory ( array , input ) ) . to . be . equal ( lowerBound ( array , input ) ) ;
143+ expect ( await this . instance . lowerBound ( input ) ) . to . equal ( lowerBound ( array , input ) ) ;
144+ expect ( await this . instance . lowerBoundMemory ( array , input ) ) . to . equal ( lowerBound ( array , input ) ) ;
98145 } ) ;
99146
100147 it ( 'upperBound' , async function ( ) {
101- expect ( await this . mock . upperBound ( input ) ) . to . be . equal ( upperBound ( array , input ) ) ;
102- expect ( await this . mock . upperBoundMemory ( array , input ) ) . to . be . equal ( upperBound ( array , input ) ) ;
148+ expect ( await this . instance . upperBound ( input ) ) . to . equal ( upperBound ( array , input ) ) ;
149+ expect ( await this . instance . upperBoundMemory ( array , input ) ) . to . equal ( upperBound ( array , input ) ) ;
103150 } ) ;
104151 } ) ;
105152 }
@@ -108,28 +155,44 @@ describe('Arrays', function () {
108155 } ) ;
109156
110157 describe ( 'unsafeAccess' , function ( ) {
111- for ( const [ title , { artifact, elements } ] of Object . entries ( {
158+ for ( const [ type , { artifact, elements } ] of Object . entries ( {
112159 address : { artifact : 'AddressArraysMock' , elements : randomArray ( generators . address , 10 ) } ,
113160 bytes32 : { artifact : 'Bytes32ArraysMock' , elements : randomArray ( generators . bytes32 , 10 ) } ,
114161 uint256 : { artifact : 'Uint256ArraysMock' , elements : randomArray ( generators . uint256 , 10 ) } ,
115162 } ) ) {
116- describe ( title , function ( ) {
117- const fixture = async ( ) => {
118- return { mock : await ethers . deployContract ( artifact , [ elements ] ) } ;
119- } ;
163+ describe ( type , function ( ) {
164+ describe ( 'storage' , function ( ) {
165+ const fixture = async ( ) => {
166+ return { instance : await ethers . deployContract ( artifact , [ elements ] ) } ;
167+ } ;
120168
121- beforeEach ( async function ( ) {
122- Object . assign ( this , await loadFixture ( fixture ) ) ;
123- } ) ;
169+ beforeEach ( async function ( ) {
170+ Object . assign ( this , await loadFixture ( fixture ) ) ;
171+ } ) ;
124172
125- for ( const i in elements ) {
126- it ( `unsafeAccess within bounds #${ i } ` , async function ( ) {
127- expect ( await this . mock . unsafeAccess ( i ) ) . to . equal ( elements [ i ] ) ;
173+ for ( const i in elements ) {
174+ it ( `unsafeAccess within bounds #${ i } ` , async function ( ) {
175+ expect ( await this . instance . unsafeAccess ( i ) ) . to . equal ( elements [ i ] ) ;
176+ } ) ;
177+ }
178+
179+ it ( 'unsafeAccess outside bounds' , async function ( ) {
180+ await expect ( this . instance . unsafeAccess ( elements . length ) ) . to . not . be . rejected ;
128181 } ) ;
129- }
182+ } ) ;
183+
184+ describe ( 'memory' , function ( ) {
185+ const fragment = `$unsafeMemoryAccess(${ type } [] arr, uint256 pos)` ;
130186
131- it ( 'unsafeAccess outside bounds' , async function ( ) {
132- await expect ( this . mock . unsafeAccess ( elements . length ) ) . to . not . be . rejected ;
187+ for ( const i in elements ) {
188+ it ( `unsafeMemoryAccess within bounds #${ i } ` , async function ( ) {
189+ expect ( await this . mock [ fragment ] ( elements , i ) ) . to . equal ( elements [ i ] ) ;
190+ } ) ;
191+ }
192+
193+ it ( 'unsafeMemoryAccess outside bounds' , async function ( ) {
194+ await expect ( this . mock [ fragment ] ( elements , elements . length ) ) . to . not . be . rejected ;
195+ } ) ;
133196 } ) ;
134197 } ) ;
135198 }
0 commit comments