Merge branch 'master' of https://github.com/ethereum/remix-project into indexdbpackupdate

pull/5370/head
filip mertens 3 years ago
commit 0b864493fe
  1. 23
      apps/remix-ide-e2e/src/tests/solidityUnittests.spec.ts
  2. 5
      apps/remix-ide/src/app/files/fileProvider.js
  3. 30
      apps/remix-ide/src/app/tabs/test-tab.js
  4. 2
      apps/remix-ide/webpack.config.js
  5. 10
      apps/solidity-compiler/src/app/compiler-api.ts
  6. 2
      libs/remix-core-plugin/src/lib/compiler-content-imports.ts
  7. 4
      libs/remix-lib/src/web3Provider/web3VmProvider.ts
  8. 2
      libs/remix-tests/sol/tests_accounts.sol.ts
  9. 11
      libs/remix-tests/src/compiler.ts
  10. 10
      libs/remix-tests/src/deployer.ts
  11. 1
      libs/remix-tests/src/index.ts
  12. 4
      libs/remix-tests/src/runTestFiles.ts
  13. 40
      libs/remix-tests/src/runTestSources.ts
  14. 2
      libs/remix-tests/tests/testRunner.spec.ts

@ -13,7 +13,7 @@ module.exports = {
return sources
},
'Should launch solidity unit test plugin': function (browser: NightwatchBrowser) {
'Should launch solidity unit test plugin and create test files in FE': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
.clickLaunchIcon('filePanel')
.addFile('simple_storage.sol', sources[0]['simple_storage.sol'])
@ -23,6 +23,15 @@ module.exports = {
.click('*[data-id="verticalIconsKindsolidityUnitTesting"]')
.waitForElementPresent('*[data-id="sidePanelSwapitTitle"]')
.assert.containsText('*[data-id="sidePanelSwapitTitle"]', 'SOLIDITY UNIT TESTING')
.clickLaunchIcon('filePanel')
.waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_tests.sol"]')
.waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_accounts.sol"]')
.openFile('.deps/remix-tests/remix_tests.sol')
// remix_test.sol should be opened in editor
.getEditorValue((content) => browser.assert.ok(content.indexOf('library Assert {') !== -1))
.openFile('.deps/remix-tests/remix_accounts.sol')
// remix_accounts.sol should be opened in editor
.getEditorValue((content) => browser.assert.ok(content.indexOf('library TestsAccounts {') !== -1))
},
'Should generate test file': function (browser: NightwatchBrowser) {
@ -149,7 +158,7 @@ module.exports = {
.click('*[data-id="testTabGenerateTestFolder"]')
},
'Changing current path when workspace changed': function (browser: NightwatchBrowser) {
'Changing current path when workspace changed and checking test files creation': function (browser: NightwatchBrowser) {
browser
.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
.clickLaunchIcon('settings')
@ -167,6 +176,14 @@ module.exports = {
.waitForElementVisible('*[data-id="fileSystem-modal-footer-ok-react"]')
.execute(function () { (document.querySelector('[data-id="fileSystem-modal-footer-ok-react"]') as HTMLElement).click() })
.waitForElementPresent('*[data-id="workspacesSelect"] option[value="workspace_new"]')
.waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_tests.sol"]')
.waitForElementVisible('li[data-id="treeViewLitreeViewItem.deps/remix-tests/remix_accounts.sol"]')
.openFile('.deps/remix-tests/remix_tests.sol')
// remix_test.sol should be opened in editor
.getEditorValue((content) => browser.assert.ok(content.indexOf('library Assert {') !== -1))
.openFile('.deps/remix-tests/remix_accounts.sol')
// remix_accounts.sol should be opened in editor
.getEditorValue((content) => browser.assert.ok(content.indexOf('library TestsAccounts {') !== -1))
// end of creating
.clickLaunchIcon('solidityUnitTesting')
.pause(2000)
@ -277,6 +294,8 @@ module.exports = {
.setValue('*[data-id="slider"]', new Array(1).fill(browser.Keys.RIGHT_ARROW))
.waitForElementContainsText('*[data-id="functionPanel"]', 'equal(a, b, message)', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalPassed()', 60000)
// remix_test.sol should be opened in editor
.getEditorValue((content) => browser.assert.ok(content.indexOf('library Assert {') !== -1))
.pause(5000)
.clickLaunchIcon('solidityUnitTesting').pause(2000)
.scrollAndClick('#Check_winning_proposal_again')

@ -17,8 +17,9 @@ class FileProvider {
}
addNormalizedName (path, url) {
this.providerExternalsStorage.set(this.type + '/' + path, url)
this.providerExternalsStorage.set(this.reverseKey + url, this.type + '/' + path)
if (this.type) path = this.type + '/' + path
this.providerExternalsStorage.set(path, url)
this.providerExternalsStorage.set(this.reverseKey + url, path)
}
removeNormalizedName (path) {

@ -7,7 +7,7 @@ var async = require('async')
var tooltip = require('../ui/tooltip')
var Renderer = require('../ui/renderer')
var css = require('./styles/test-tab-styles')
var { UnitTestRunner } = require('@remix-project/remix-tests')
var { UnitTestRunner, assertLibCode } = require('@remix-project/remix-tests')
const _paq = window._paq = window._paq || []
@ -16,7 +16,7 @@ const TestTabLogic = require('./testTab/testTab')
const profile = {
name: 'solidityUnitTesting',
displayName: 'Solidity unit testing',
methods: ['testFromPath', 'testFromSource', 'setTestFolderPath'],
methods: ['testFromPath', 'testFromSource', 'setTestFolderPath', 'getTestlibs'],
events: [],
icon: 'assets/img/unitTesting.webp',
description: 'Fast tool to generate unit tests for your contracts',
@ -42,7 +42,7 @@ module.exports = class TestTab extends ViewPlugin {
this.areTestsRunning = false
this.defaultPath = 'tests'
this.offsetToLineColumnConverter = offsetToLineColumnConverter
this.allFilesInvolved = []
this.allFilesInvolved = ['.deps/remix-tests/remix_tests.sol', '.deps/remix-tests/remix_accounts.sol']
this.isDebugging = false
this.currentErrors = []
@ -74,11 +74,25 @@ module.exports = class TestTab extends ViewPlugin {
}
}
getTestlibs () {
return { assertLibCode, accountsLibCode: this.testRunner.accountsLibCode }
}
async createTestLibs () {
const provider = await this.fileManager.currentFileProvider()
if (provider) {
provider.addExternal('.deps/remix-tests/remix_tests.sol', assertLibCode, 'remix_tests.sol')
provider.addExternal('.deps/remix-tests/remix_accounts.sol', this.testRunner.accountsLibCode, 'remix_accounts.sol')
}
}
async onActivation () {
const isSolidityActive = await this.call('manager', 'isActive', 'solidity')
if (!isSolidityActive) {
await this.call('manager', 'activatePlugin', 'solidity')
}
await this.testRunner.init()
await this.createTestLibs()
this.updateRunAction()
}
@ -110,9 +124,13 @@ module.exports = class TestTab extends ViewPlugin {
this.setCurrentPath(this.defaultPath)
})
this.on('filePanel', 'workspaceCreated', async () => {
this.createTestLibs()
})
this.testRunner.event.on('compilationFinished', (success, data, source) => {
if (success) {
this.allFilesInvolved = Object.keys(data.sources)
this.allFilesInvolved.push(...Object.keys(data.sources))
// forwarding the event to the appManager infra
// This is listened by compilerArtefacts to show data while debugging
this.emit('compilationFinished', source.target, source, 'soljson', data)
@ -526,8 +544,8 @@ module.exports = class TestTab extends ViewPlugin {
this.fileManager.readFile(testFilePath).then((content) => {
const runningTests = {}
runningTests[testFilePath] = { content }
const { currentVersion, evmVersion, optimize, runs } = this.compileTab.getCurrentCompilerConfig()
const currentCompilerUrl = urlFromVersion(currentVersion)
const { currentVersion, evmVersion, optimize, runs, isUrl } = this.compileTab.getCurrentCompilerConfig()
const currentCompilerUrl = isUrl ? currentVersion : urlFromVersion(currentVersion)
const compilerConfig = {
currentCompilerUrl,
evmVersion,

@ -21,7 +21,7 @@ module.exports = config => {
mode: 'production',
devtool: 'source-map',
optimization: {
minimize: true,
minimize: false,
minimizer: [new TerserPlugin()]
}
}

@ -143,12 +143,17 @@ export const CompilerApiMixin = (Base) => class extends Base {
// This function is used for passing the compiler configuration to 'remix-tests'
getCurrentCompilerConfig () {
const compilerState = this.getCompilerState()
return {
let compilerDetails: any = {
currentVersion: compilerState.currentVersion,
evmVersion: compilerState.evmVersion,
optimize: compilerState.optimize,
runs: compilerState.runs
}
if (this.data.loading) {
compilerDetails.currentVersion = this.data.loadingUrl
compilerDetails.isUrl = true
}
return compilerDetails
}
/**
@ -193,8 +198,9 @@ export const CompilerApiMixin = (Base) => class extends Base {
if (this.onContentChanged) this.onContentChanged()
})
this.data.eventHandlers.onLoadingCompiler = () => {
this.data.eventHandlers.onLoadingCompiler = (url) => {
this.data.loading = true
this.data.loadingUrl = url
this.emit('statusChanged', { key: 'loading', title: 'loading compiler...', type: 'info' })
}
this.compiler.event.register('loadingCompiler', this.data.eventHandlers.onLoadingCompiler)

@ -1,7 +1,6 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import { RemixURLResolver } from '@remix-project/remix-url-resolver'
const remixTests = require('@remix-project/remix-tests')
const profile = {
name: 'contentImport',
@ -117,7 +116,6 @@ export class CompilerImports extends Plugin {
* @returns {Promise} - string content
*/
async resolveAndSave (url, targetPath) {
if (url.indexOf('remix_tests.sol') !== -1) return remixTests.assertLibCode
try {
const provider = await this.call('fileManager', 'getProviderOf', url)
if (provider) {

@ -304,7 +304,9 @@ export class Web3VmProvider {
nextKey: null
})
}
cb('unable to retrieve storage ' + txIndex + ' ' + address)
// Before https://github.com/ethereum/remix-project/pull/1703, it used to throw error as
// 'unable to retrieve storage ' + txIndex + ' ' + address
cb(null, { storage: {} })
}
getBlockNumber (cb) { cb(null, 'vm provider') }

@ -3,7 +3,7 @@ module.exports = `// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;
library TestsAccounts {
function getAccount(uint index) public returns (address) {
function getAccount(uint index) pure public returns (address) {
>accounts<
return accounts[index];
}

@ -12,13 +12,13 @@ function regexIndexOf (inputString: string, regex: RegExp, startpos = 0) {
return (indexOf >= 0) ? (indexOf + (startpos)) : indexOf
}
function writeTestAccountsContract (accounts: string[]) {
export function writeTestAccountsContract (accounts: string[]) {
const testAccountContract = require('../sol/tests_accounts.sol')
let body = `address[${accounts.length}] memory accounts;`
if (!accounts.length) body += ';'
else {
accounts.map((address, index) => {
body += `\naccounts[${index}] = ${address};\n`
body += `\n\t\taccounts[${index}] = ${address};\n`
})
}
return testAccountContract.replace('>accounts<', body)
@ -172,14 +172,7 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts
*/
export function compileContractSources (sources: SrcIfc, compilerConfig: CompilerConfiguration, importFileCb: any, opts: any, cb): void {
let compiler
const accounts: string[] = opts.accounts || []
const filepath = opts.testFilePath || ''
// Iterate over sources keys. Inject test libraries. Inject test library import statements.
if (!('remix_tests.sol' in sources) && !('tests.sol' in sources)) {
sources['tests.sol'] = { content: require('../sol/tests.sol.js') }
sources['remix_tests.sol'] = { content: require('../sol/tests.sol.js') }
sources['remix_accounts.sol'] = { content: writeTestAccountsContract(accounts) }
}
const testFileImportRegEx = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm
const includeTestLibs = '\nimport \'remix_tests.sol\';\n'

@ -11,18 +11,12 @@ import { compilationInterface } from './types'
* @param callback Callback
*/
export function deployAll (compileResult: compilationInterface, web3: Web3, withDoubleGas: boolean, deployCb, callback) {
export function deployAll (compileResult: compilationInterface, web3: Web3, testsAccounts, withDoubleGas: boolean, deployCb, callback) {
const compiledObject = {}
const contracts = {}
let accounts: string[] = []
const accounts: string[] = testsAccounts
async.waterfall([
function getAccountList (next) {
web3.eth.getAccounts((_err, _accounts) => {
accounts = _accounts
next()
})
},
function getContractData (next) {
for (const contractFile in compileResult) {
for (const contractName in compileResult[contractFile]) {

@ -3,3 +3,4 @@ export { UnitTestRunner } from './runTestSources'
export { runTest } from './testRunner'
export * from './types'
export const assertLibCode = require('../sol/tests.sol')
export { writeTestAccountsContract } from './compiler'

@ -61,13 +61,13 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
for (const filename in asts) {
if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast }
}
deployAll(compilationResult, web3, false, null, (err, contracts) => {
deployAll(compilationResult, web3, accounts, false, null, (err, contracts) => {
if (err) {
// If contract deployment fails because of 'Out of Gas' error, try again with double gas
// This is temporary, should be removed when remix-tests will have a dedicated UI to
// accept deployment params from UI
if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) {
deployAll(compilationResult, web3, true, null, (error, contracts) => {
deployAll(compilationResult, web3, accounts, true, null, (error, contracts) => {
if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array
else next(null, compilationResult, contracts)
})

@ -1,9 +1,7 @@
import async, { ErrorCallback } from 'async'
import { compileContractSources } from './compiler'
import { compileContractSources, writeTestAccountsContract } from './compiler'
import { deployAll } from './deployer'
import { runTest } from './testRunner'
import Web3 from 'web3'
import { EventEmitter } from 'events'
import { Provider, extend } from '@remix-project/remix-simulator'
@ -15,13 +13,22 @@ require('colors')
export class UnitTestRunner {
event
accountsLibCode
testsAccounts: string[] | null
web3
constructor () {
this.event = new EventEmitter()
}
async createWeb3Provider () {
const web3 = new Web3()
async init (web3 = null, accounts = null) {
this.web3 = await this.createWeb3Provider(web3)
this.testsAccounts = accounts || await this.web3.eth.getAccounts()
this.accountsLibCode = writeTestAccountsContract(this.testsAccounts)
}
async createWeb3Provider (optWeb3) {
const web3 = optWeb3 || new Web3()
const provider: any = new Provider()
await provider.init()
web3.setProvider(provider)
@ -42,30 +49,23 @@ export class UnitTestRunner {
async runTestSources (contractSources: SrcIfc, compilerConfig: CompilerConfiguration, testCallback, resultCallback, deployCb:any, finalCallback: any, importFileCb, opts: Options) {
opts = opts || {}
const sourceASTs: any = {}
const web3 = opts.web3 || await this.createWeb3Provider()
let accounts: string[] | null = opts.accounts || null
if (opts.web3 || opts.accounts) this.init(opts.web3, opts.accounts)
async.waterfall([
function getAccountList (next) {
if (accounts) return next()
web3.eth.getAccounts((_err, _accounts) => {
accounts = _accounts
next()
})
},
(next) => {
compileContractSources(contractSources, compilerConfig, importFileCb, { accounts, testFilePath: opts.testFilePath, event: this.event }, next)
compileContractSources(contractSources, compilerConfig, importFileCb, { accounts: this.testsAccounts, testFilePath: opts.testFilePath, event: this.event }, next)
},
function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) {
(compilationResult: compilationInterface, asts: ASTInterface, next) => {
for (const filename in asts) {
if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast }
}
deployAll(compilationResult, web3, false, deployCb, (err, contracts) => {
deployAll(compilationResult, this.web3, this.testsAccounts, false, deployCb, (err, contracts) => {
if (err) {
// If contract deployment fails because of 'Out of Gas' error, try again with double gas
// This is temporary, should be removed when remix-tests will have a dedicated UI to
// accept deployment params from UI
if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) {
deployAll(compilationResult, web3, true, deployCb, (error, contracts) => {
deployAll(compilationResult, this.web3, this.testsAccounts, true, deployCb, (error, contracts) => {
if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array
else next(null, compilationResult, contracts)
})
@ -88,7 +88,7 @@ export class UnitTestRunner {
}
next(null, contractsToTest, contractsToTestDetails, contracts)
},
function runTests (contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) {
(contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) => {
let totalPassing = 0
let totalFailing = 0
let totalTime = 0
@ -111,7 +111,7 @@ export class UnitTestRunner {
async.eachOfLimit(contractsToTest, 1, (contractName: string, index: string | number, cb: ErrorCallback) => {
const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']]
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts, web3 }, _testCallback, (err, result) => {
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts: this.testsAccounts, web3: this.web3 }, _testCallback, (err, result) => {
if (err) {
return cb(err)
}

@ -67,7 +67,7 @@ async function compileAndDeploy(filename: string, callback: Function) {
}
try {
compilationData = compilationResult
deployAll(compilationResult, web3, false, null, next)
deployAll(compilationResult, web3, accounts, false, null, next)
} catch (e) {
throw e
}

Loading…
Cancel
Save