From 2276793629292c6ff38ae107c8eac7a7ba2c4b81 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Wed, 27 Jun 2018 21:34:28 -0400 Subject: using timestamps, skip gen of up-to-date wrappers --- packages/abi-gen/CHANGELOG.json | 9 +++++++++ packages/abi-gen/package.json | 4 +--- packages/abi-gen/src/index.ts | 29 +++++++++++++++++++++++++---- 3 files changed, 35 insertions(+), 7 deletions(-) (limited to 'packages') diff --git a/packages/abi-gen/CHANGELOG.json b/packages/abi-gen/CHANGELOG.json index 8f0beb57e..78aa8a6b4 100644 --- a/packages/abi-gen/CHANGELOG.json +++ b/packages/abi-gen/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "0.4.1", + "changes": [ + { + "note": "skip generation of wrappers that are already up to date", + "pr": 788 + } + ] + }, { "version": "0.4.0", "changes": [ diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json index f8eede705..fd02634f9 100644 --- a/packages/abi-gen/package.json +++ b/packages/abi-gen/package.json @@ -29,12 +29,11 @@ "dependencies": { "@0xproject/typescript-typings": "^0.4.1", "@0xproject/utils": "^0.7.1", - "ethereum-types": "^0.0.2", "chalk": "^2.3.0", + "ethereum-types": "^0.0.2", "glob": "^7.1.2", "handlebars": "^4.0.11", "lodash": "^4.17.4", - "rimraf": "^2.6.2", "mkdirp": "^0.5.1", "to-snake-case": "^1.0.0", "yargs": "^10.0.3" @@ -43,7 +42,6 @@ "@0xproject/monorepo-scripts": "^0.2.1", "@0xproject/tslint-config": "^0.4.20", "@types/glob": "^5.0.33", - "@types/rimraf": "^2.0.2", "@types/handlebars": "^4.0.36", "@types/mkdirp": "^0.5.1", "@types/node": "^8.0.53", diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts index 47f2c404b..f38a162aa 100644 --- a/packages/abi-gen/src/index.ts +++ b/packages/abi-gen/src/index.ts @@ -8,7 +8,6 @@ import { sync as globSync } from 'glob'; import * as Handlebars from 'handlebars'; import * as _ from 'lodash'; import * as mkdirp from 'mkdirp'; -import * as rimraf from 'rimraf'; import * as yargs from 'yargs'; import toSnakeCase = require('to-snake-case'); @@ -71,16 +70,34 @@ function registerPartials(partialsGlob: string): void { } } -function writeOutputFile(name: string, renderedTsCode: string): void { +function makeOutputFilePath(name: string): string { let fileName = toSnakeCase(name); // HACK: Snake case doesn't make a lot of sense for abbreviated names but we can't reliably detect abbreviations // so we special-case the abbreviations we use. fileName = fileName.replace('z_r_x', 'zrx').replace('e_r_c', 'erc'); - const filePath = `${args.output}/${fileName}.ts`; + return `${args.output}/${fileName}.ts`; +} + +function writeOutputFile(name: string, renderedTsCode: string): void { + const filePath = makeOutputFilePath(name); fs.writeFileSync(filePath, renderedTsCode); logUtils.log(`Created: ${chalk.bold(filePath)}`); } +function isOutputFileUpToDate(abiFile: string, outFile: string): boolean { + const abiFileModTimeMs = fs.statSync(abiFile).mtimeMs; + try { + const outFileModTimeMs = fs.statSync(makeOutputFilePath(outFile)).mtimeMs; + return outFileModTimeMs > abiFileModTimeMs; + } catch (err) { + if (err.code === 'ENOENT') { + return false; + } else { + throw err; + } + } +} + Handlebars.registerHelper('parameterType', utils.solTypeToTsType.bind(utils, ParamKind.Input, args.backend)); Handlebars.registerHelper('returnType', utils.solTypeToTsType.bind(utils, ParamKind.Output, args.backend)); if (args.partials) { @@ -97,7 +114,6 @@ if (_.isEmpty(abiFileNames)) { process.exit(1); } else { logUtils.log(`Found ${chalk.green(`${abiFileNames.length}`)} ${chalk.bold('ABI')} files`); - rimraf.sync(args.output); mkdirp.sync(args.output); } for (const abiFileName of abiFileNames) { @@ -120,6 +136,11 @@ for (const abiFileName of abiFileNames) { process.exit(1); } + if (isOutputFileUpToDate(abiFileName, namedContent.name)) { + logUtils.log(`Already up to date: ${chalk.bold(makeOutputFilePath(namedContent.name))}`); + continue; + } + let ctor = ABI.find((abi: AbiDefinition) => abi.type === ABI_TYPE_CONSTRUCTOR) as ConstructorAbi; if (_.isUndefined(ctor)) { ctor = utils.getEmptyConstructor(); // The constructor exists, but it's implicit in JSON's ABI definition -- cgit v1.2.3 From a0e3676e3aef4692732375c02c274223b3349f63 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Sun, 1 Jul 2018 16:05:15 -0400 Subject: move abi-gen funcs from index to utils for testing preparing for unit testing. purely refactoring (no functionality changed). --- packages/abi-gen/src/index.ts | 41 +++++++---------------------------------- packages/abi-gen/src/utils.ts | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 34 deletions(-) (limited to 'packages') diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts index f38a162aa..753bb2cce 100644 --- a/packages/abi-gen/src/index.ts +++ b/packages/abi-gen/src/index.ts @@ -3,15 +3,12 @@ import { abiUtils, logUtils } from '@0xproject/utils'; import chalk from 'chalk'; import { AbiDefinition, ConstructorAbi, EventAbi, MethodAbi } from 'ethereum-types'; -import * as fs from 'fs'; import { sync as globSync } from 'glob'; import * as Handlebars from 'handlebars'; import * as _ from 'lodash'; import * as mkdirp from 'mkdirp'; import * as yargs from 'yargs'; -import toSnakeCase = require('to-snake-case'); - import { ContextData, ContractsBackend, ParamKind } from './types'; import { utils } from './utils'; @@ -70,34 +67,6 @@ function registerPartials(partialsGlob: string): void { } } -function makeOutputFilePath(name: string): string { - let fileName = toSnakeCase(name); - // HACK: Snake case doesn't make a lot of sense for abbreviated names but we can't reliably detect abbreviations - // so we special-case the abbreviations we use. - fileName = fileName.replace('z_r_x', 'zrx').replace('e_r_c', 'erc'); - return `${args.output}/${fileName}.ts`; -} - -function writeOutputFile(name: string, renderedTsCode: string): void { - const filePath = makeOutputFilePath(name); - fs.writeFileSync(filePath, renderedTsCode); - logUtils.log(`Created: ${chalk.bold(filePath)}`); -} - -function isOutputFileUpToDate(abiFile: string, outFile: string): boolean { - const abiFileModTimeMs = fs.statSync(abiFile).mtimeMs; - try { - const outFileModTimeMs = fs.statSync(makeOutputFilePath(outFile)).mtimeMs; - return outFileModTimeMs > abiFileModTimeMs; - } catch (err) { - if (err.code === 'ENOENT') { - return false; - } else { - throw err; - } - } -} - Handlebars.registerHelper('parameterType', utils.solTypeToTsType.bind(utils, ParamKind.Input, args.backend)); Handlebars.registerHelper('returnType', utils.solTypeToTsType.bind(utils, ParamKind.Output, args.backend)); if (args.partials) { @@ -136,8 +105,11 @@ for (const abiFileName of abiFileNames) { process.exit(1); } - if (isOutputFileUpToDate(abiFileName, namedContent.name)) { - logUtils.log(`Already up to date: ${chalk.bold(makeOutputFilePath(namedContent.name))}`); + const outFileName = utils.makeOutputFileName(namedContent.name); + const outFilePath = `${args.output}/${outFileName}.ts`; + + if (utils.isOutputFileUpToDate(abiFileName, outFilePath)) { + logUtils.log(`Aready up to date: ${chalk.bold(outFilePath)}`); continue; } @@ -175,5 +147,6 @@ for (const abiFileName of abiFileNames) { events: eventAbis, }; const renderedTsCode = template(contextData); - writeOutputFile(namedContent.name, renderedTsCode); + utils.writeOutputFile(outFilePath, renderedTsCode); + logUtils.log(`Created: ${chalk.bold(outFilePath)}`); } diff --git a/packages/abi-gen/src/utils.ts b/packages/abi-gen/src/utils.ts index 66390174c..56b996ce3 100644 --- a/packages/abi-gen/src/utils.ts +++ b/packages/abi-gen/src/utils.ts @@ -2,6 +2,7 @@ import { AbiType, ConstructorAbi, DataItem } from 'ethereum-types'; import * as fs from 'fs'; import * as _ from 'lodash'; import * as path from 'path'; +import toSnakeCase = require('to-snake-case'); import { ContractsBackend, ParamKind } from './types'; @@ -92,4 +93,27 @@ export const utils = { inputs: [], }; }, + makeOutputFileName(name: string): string { + let fileName = toSnakeCase(name); + // HACK: Snake case doesn't make a lot of sense for abbreviated names but we can't reliably detect abbreviations + // so we special-case the abbreviations we use. + fileName = fileName.replace('z_r_x', 'zrx').replace('e_r_c', 'erc'); + return fileName; + }, + writeOutputFile(filePath: string, renderedTsCode: string): void { + fs.writeFileSync(filePath, renderedTsCode); + }, + isOutputFileUpToDate(abiFile: string, outputFile: string): boolean { + const abiFileModTimeMs = fs.statSync(abiFile).mtimeMs; + try { + const outFileModTimeMs = fs.statSync(outputFile).mtimeMs; + return outFileModTimeMs > abiFileModTimeMs; + } catch (err) { + if (err.code === 'ENOENT') { + return false; + } else { + throw err; + } + } + }, }; -- cgit v1.2.3 From 9f08916cf18ad3e2686811933116122eea0890b9 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Sun, 1 Jul 2018 17:54:48 -0400 Subject: add tests of abi-gen --- packages/abi-gen/bin/abi-gen.js | 2 +- packages/abi-gen/coverage/.gitkeep | 0 packages/abi-gen/package.json | 12 ++++++ packages/abi-gen/test/utils_test.ts | 86 +++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 packages/abi-gen/coverage/.gitkeep create mode 100644 packages/abi-gen/test/utils_test.ts (limited to 'packages') diff --git a/packages/abi-gen/bin/abi-gen.js b/packages/abi-gen/bin/abi-gen.js index c46eb9b66..8d6bdccf8 100755 --- a/packages/abi-gen/bin/abi-gen.js +++ b/packages/abi-gen/bin/abi-gen.js @@ -1,2 +1,2 @@ #!/usr/bin/env node -require('../lib/index.js') +require('../lib/src/index.js') diff --git a/packages/abi-gen/coverage/.gitkeep b/packages/abi-gen/coverage/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json index fd02634f9..b85b20d19 100644 --- a/packages/abi-gen/package.json +++ b/packages/abi-gen/package.json @@ -12,6 +12,11 @@ "lint": "tslint --project .", "clean": "shx rm -rf lib scripts", "build": "tsc && copyfiles -u 2 './lib/monorepo_scripts/**/*' ./scripts", + "test": "yarn run_mocha", + "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", + "test:circleci": "yarn test:coverage", + "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", + "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "manual:postpublish": "yarn build; node ./scripts/postpublish.js" }, "bin": { @@ -35,6 +40,8 @@ "handlebars": "^4.0.11", "lodash": "^4.17.4", "mkdirp": "^0.5.1", + "sleep": "^5.1.1", + "tmp": "^0.0.33", "to-snake-case": "^1.0.0", "yargs": "^10.0.3" }, @@ -45,9 +52,14 @@ "@types/handlebars": "^4.0.36", "@types/mkdirp": "^0.5.1", "@types/node": "^8.0.53", + "@types/sleep": "^0.0.7", + "@types/tmp": "^0.0.33", "@types/yargs": "^10.0.0", + "chai": "^4.1.2", "copyfiles": "^1.2.0", + "dirty-chai": "^2.0.1", "make-promises-safe": "^1.1.0", + "mocha": "^5.2.0", "npm-run-all": "^4.1.2", "shx": "^0.2.2", "tslint": "5.8.0", diff --git a/packages/abi-gen/test/utils_test.ts b/packages/abi-gen/test/utils_test.ts new file mode 100644 index 000000000..c6147df38 --- /dev/null +++ b/packages/abi-gen/test/utils_test.ts @@ -0,0 +1,86 @@ +import * as chai from 'chai'; +import * as dirtyChai from 'dirty-chai'; +import * as fs from 'fs'; +import 'mocha'; +import * as sleep from 'sleep'; +import * as tmp from 'tmp'; + +import { utils } from '../src/utils'; + +tmp.setGracefulCleanup(); // remove tmp files even if there are failures + +chai.use(dirtyChai); + +const expect = chai.expect; + +const SLEEP_MS = 10; // time to wait before re-timestamping a file + +describe('makeOutputFileName()', () => { + it('should handle Metacoin usage', () => { + expect(utils.makeOutputFileName('Metacoin')).to.equal('metacoin'); + }); + it('should handle special zrx_token case', () => { + expect(utils.makeOutputFileName('ZRXToken')).to.equal('zrx_token'); + }); + it('should handle special erc_token case', () => { + expect(utils.makeOutputFileName('ERC20Token')).to.equal('erc20_token'); + }); +}); + +describe('writeOutputFile()', () => { + let tempFilePath: string; + before(() => { + tempFilePath = tmp.fileSync( + { discardDescriptor: true }, // close file (so we can update it) + ).name; + }); + it('should write content to output file', () => { + const content = 'hello world'; + + utils.writeOutputFile(tempFilePath, content); + + expect(fs.readFileSync(tempFilePath).toString()).to.equal(content); + }); +}); + +describe('isOutputFileUpToDate()', () => { + it('should throw ENOENT when there is no abi file', () => { + expect(utils.isOutputFileUpToDate.bind('nonexistant1', 'nonexistant2')).to.throw('ENOENT'); + }); + + describe('when the abi input file exists', () => { + let abiFile: string; + before(() => { + abiFile = tmp.fileSync( + { discardDescriptor: true }, // close file (set timestamp) + ).name; + }); + + describe('without an existing output file', () => { + it('should return false', () => { + expect(utils.isOutputFileUpToDate(abiFile, 'nonexistant_file')).to.be.false(); + }); + }); + + describe('with an existing output file', () => { + let outputFile: string; + before(() => { + sleep.msleep(SLEEP_MS); // to ensure different timestamp + outputFile = tmp.fileSync( + { discardDescriptor: true }, // close file (set timestamp) + ).name; + }); + + it('should return true when output file and is newer than abi file', async () => { + expect(utils.isOutputFileUpToDate(abiFile, outputFile)).to.be.true(); + }); + + it('should return false when output file exists but is older than abi file', () => { + sleep.msleep(SLEEP_MS); // to ensure different timestamp + fs.closeSync(fs.openSync(abiFile, 'w')); // touch abi file + + expect(utils.isOutputFileUpToDate(abiFile, outputFile)).to.be.false(); + }); + }); + }); +}); -- cgit v1.2.3