diff --git a/.c8rc.json b/.c8rc.json index 22df2fc2..f009d813 100644 --- a/.c8rc.json +++ b/.c8rc.json @@ -6,6 +6,6 @@ "exclude": [ ".solc/soljson-v*.js", "dist/test/", - "scripts/solc.mjs" + "test/scripts/solc.mjs" ] } diff --git a/.mocharc.yaml b/.mocharc.yaml index 47b47c10..0d0fed82 100644 --- a/.mocharc.yaml +++ b/.mocharc.yaml @@ -8,7 +8,7 @@ enable-source-maps: true # Fetch and cache `solc` compilers # https://mochajs.org/#global-setup-fixtures -require: scripts/solc.mjs +require: test/scripts/solc.mjs # https://github.com/mochajs/mocha/blob/master/example/config/.mocharc.yml spec: diff --git a/README.md b/README.md index 6e9e894b..5b5a0bd1 100644 --- a/README.md +++ b/README.md @@ -543,12 +543,18 @@ Generates `function` and `event` lookup tables database for signatures in `json` Generates ERCs function and event definitions from [`scripts/ercs.sol`](./scripts/ercs.sol). - [`help.mjs`](./scripts/help.mjs) Embeds [`examples`](#examples) and `sevm --help` into [`README`](./README.md). -- [`mock.mjs`](./scripts/mock.mjs) + +### [`test/scripts`](./test/scripts/) + +Contains utility scripts that complements the test process. + +- [`mock.mjs`](./test/scripts/mock.mjs) Mocks network requests to avoid brittle **CLI** tests, _i.e._, `::examples` and `::bin` tests. Both `ethers`' JSON-RPC provider `eth_getCode` method and, `function` and `event` signatures from [OpenChain API](https://openchain.xyz/signatures) will be mocked. -It is loaded using Node's flag [`--import=./scripts/mock.mjs`](https://nodejs.org/api/cli.html#--importmodule). -- [`solc.mjs`](./scripts/solc.mjs) -Downloads and caches [`solc-js`](https://github.com/ethereum/solc-js) compiler versions used in tests. It is invoked via Mocha's [_Global Setup Fixtures_](https://mochajs.org/#global-setup-fixtures). +It is loaded using Node's flag [`--import=./test/scripts/mock.mjs`](https://nodejs.org/api/cli.html#--importmodule). +- [`solc.mjs`](./test/scripts/solc.mjs) +Downloads and caches [`solc-js`](https://github.com/ethereum/solc-js) compiler versions used in tests. +It is invoked via Mocha's [_Global Setup Fixtures_](https://mochajs.org/#global-setup-fixtures). ### [`examples`](./examples/) diff --git a/package.json b/package.json index a5a775fb..782f8c68 100644 --- a/package.json +++ b/package.json @@ -96,8 +96,8 @@ "coverage": "yarn compile && c8 mocha", "bundle": "webpack --mode production", "size": "size-limit", - "make:ercs": "scripts/ercs.mjs > src/ercs.ts", "make:4bytedb": "node scripts/4bytedb.mjs", + "make:ercs": "scripts/ercs.mjs > src/ercs.ts", "make:help": "scripts/help.mjs README.md", "make:docs": "cp README.md docs && typedoc --out docs/tsdoc src/index.ts src/ast/index.ts", "docs": "npx http-server docs", diff --git a/scripts/solc.mjs b/scripts/solc.mjs deleted file mode 100644 index 3d8a0c7b..00000000 --- a/scripts/solc.mjs +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env node -/* eslint-env node */ - -import c from 'ansi-colors'; -import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; -import https from 'https'; - -/** - * @param {string} url - * @returns {Promise} - */ -function get(url) { - return new Promise((resolve, reject) => { - const req = https.request(url, function (res) { - /** @type {Uint8Array | null} */ - let body = null; - - res.on('data', (/** @type {Uint8Array} */chunk) => { - if (body == null) { - body = chunk; - } else { - const newBody = new Uint8Array(body.length + chunk.length); - newBody.set(body, 0); - newBody.set(chunk, body.length); - body = newBody; - } - }); - - res.on('end', () => { - resolve(Buffer.from(/** @type {Uint8Array} */(body)).toString('utf8')); - }); - }); - - req.on('error', err => { - reject(err); - }); - - req.end(); - }); -} - -const VERSIONS = ['0.5.5', '0.5.17', '0.6.12', '0.7.6', '0.8.16', '0.8.21']; - -/** @typedef {{ [key: string]: string }} Releases */ - -/** - * Fetch and cache `solc` compilers used in tests - */ -export async function mochaGlobalSetup() { - // `recursive` ensures dir is created instead of failing with 'file already exists' - mkdirSync('.solc', { recursive: true }); - process.stdout.write(c.magenta('> setup solc-js compilers ')); - - const releases = await (async function () { - const path = './.solc/releases.json'; - try { - const ret = /** @type {Releases} */(JSON.parse(readFileSync(path, 'utf-8'))); - return ret; - } catch (_err) { - const resp = await get('https://binaries.soliditylang.org/bin/list.json'); - const { releases } = /** @type {{releases: Releases}} */(JSON.parse(resp)); - writeFileSync(path, JSON.stringify(releases, null, 2)); - return releases; - } - })(); - - for (const version of VERSIONS) { - process.stdout.write(`${c.cyan('v' + version)}`); - const path = `./.solc/soljson-v${version}.js`; - - if (existsSync(path)) { - process.stdout.write(c.green('\u2713 ')); - } else { - const resp = await get(`https://binaries.soliditylang.org/bin/${releases[version]}`); - writeFileSync(path, resp); - process.stdout.write(c.yellow('\u2913 ')); - } - } -} diff --git a/test/bin.test.ts b/test/bin.test.ts index cd0cdbae..9f962ca5 100644 --- a/test/bin.test.ts +++ b/test/bin.test.ts @@ -142,7 +142,7 @@ describe('::bin', function () { * https://etherscan.io/address/0x00000000219ab540356cBB839Cbe05303d7705Fa */ const address = '0x00000000219ab540356cBB839Cbe05303d7705Fa'; - const mock = '--import=./scripts/mock.mjs'; + const mock = '--import=./test/scripts/mock.mjs'; it('should get `bytecode` from default provider', function () { const cli = chaiExec('node', [mock, sevm, 'abi', address, '--no-color', '--no-patch', '--no-cache'], { env: sevmDebugEnv }); diff --git a/test/examples.test.ts b/test/examples.test.ts index 29d29541..a98035aa 100644 --- a/test/examples.test.ts +++ b/test/examples.test.ts @@ -21,7 +21,7 @@ describe(`::examples`, function () { const program = ['.ts', '.mts'].includes(ext) ? `dist/examples/${file.slice(0, -ext.length)}${ext.replace('t', 'j')}` : `examples/${file}`; - const mock = '--import=./scripts/mock.mjs'; + const mock = '--import=./test/scripts/mock.mjs'; const cli = chaiExec('node', mock, program, { env }); expect(cli, cli.stderr).stderr.to.be.empty; diff --git a/test/exprs.test.ts b/test/exprs.test.ts index ecfd8797..0873e292 100644 --- a/test/exprs.test.ts +++ b/test/exprs.test.ts @@ -3,7 +3,7 @@ import util from 'util'; import { Shanghai, sol, yul, type Ram, State, Memory } from 'sevm'; import type { Expr } from 'sevm/ast'; -import { Add, Byte, CallDataLoad, CallValue, Div, Eq, Exp, IsZero, MLoad, Mod, Mul, Not, Prop, Props, Sha3, Shl, Sig, Sub, Val } from 'sevm/ast'; +import { Add, Byte, CallDataLoad, CallValue, Div, Eq, Exp, IsZero, MLoad, Mod, Mul, Not, Prop, Props, Sha3, Shl, Sig, Sub, Val, Xor } from 'sevm/ast'; const id = (expr: E): E => expr; @@ -88,6 +88,8 @@ const $exprs = { t([0x100n, 0x2n, 'EXP'], new Exp(new Val(0x2n), new Val(0x100n)), new Val(0x0n), '0x2 ** 0x100', 'exp(0x2, 0x100)'), t([0x1n, 0x111n, 'SHL'], new Shl(new Val(0x1n), new Val(0x111n)), new Val(0x0n), '0x1 << 0x111', 'shl(0x1, 0x111)'), + + t([0x10n, 0x101n, 'XOR'], new Xor(new Val(0x101n), new Val(0x010n)), new Val(0x111n), '0x101 ^ 0x10', 'xor(0x101, 0x10)'), ], special: [ t(['BASEFEE'], Props['block.basefee'], id, 'block.basefee', 'basefee()'), diff --git a/scripts/mock.mjs b/test/scripts/mock.mjs similarity index 100% rename from scripts/mock.mjs rename to test/scripts/mock.mjs diff --git a/test/scripts/solc.mjs b/test/scripts/solc.mjs new file mode 100644 index 00000000..c7501ec3 --- /dev/null +++ b/test/scripts/solc.mjs @@ -0,0 +1,46 @@ +#!/usr/bin/env node +/* eslint-env node */ + +import c from 'ansi-colors'; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; + +const VERSIONS = ['0.5.5', '0.5.17', '0.6.12', '0.7.6', '0.8.16', '0.8.21']; + +/** + * Fetch and cache `solc` compilers used in tests. + */ +export async function mochaGlobalSetup() { + /** @typedef {{ [key: string]: string }} Releases */ + + // `recursive` ensures dir is created instead of failing with 'file already exists' + mkdirSync('.solc', { recursive: true }); + process.stdout.write(c.magenta('> setup solc-js compilers ')); + + const releases = await (async function () { + const path = './.solc/releases.json'; + try { + const ret = /** @type {Releases} */(JSON.parse(readFileSync(path, 'utf-8'))); + return ret; + } catch (_err) { + const resp = await fetch('https://binaries.soliditylang.org/bin/list.json'); + // Serializes only `releases` property to avoid including `builds` + // property which is unnecessary and clutters the file. + const { releases } = /** @type {{releases: Releases}} */(await resp.json()); + writeFileSync(path, JSON.stringify(releases, null, 2)); + return releases; + } + })(); + + for (const version of VERSIONS) { + process.stdout.write(`${c.cyan('v' + version)}`); + const path = `./.solc/soljson-v${version}.js`; + + if (existsSync(path)) { + process.stdout.write(c.green('\u2713 ')); + } else { + const resp = await fetch(`https://binaries.soliditylang.org/bin/${releases[version]}`); + writeFileSync(path, await resp.text()); + process.stdout.write(c.yellow('\u2913 ')); + } + } +}