Set the msg.sender in the solidity userdoc

pull/5370/head
yann300 6 years ago
parent b4694a82e5
commit ff3af0cc11
  1. 7
      remix-simulator/src/methods/accounts.js
  2. 9
      remix-tests/sol/tests_accounts.sol.js
  3. 23
      remix-tests/src/compiler.js
  4. 54
      remix-tests/src/index.js
  5. 43
      remix-tests/src/testRunner.js
  6. 61
      remix-tests/tests/testRunner.js
  7. 28
      remix-tests/tests/various_sender/sender_test.sol

@ -3,10 +3,15 @@ var Web3 = require('web3')
var Accounts = function () { var Accounts = function () {
this.web3 = new Web3() this.web3 = new Web3()
// TODO: make it random and/or use remix-libs // TODO: make it random and/or use remix-libs
this.accounts = [this.web3.eth.accounts.create(['abcd'])] this.accounts = [this.web3.eth.accounts.create(['abcd']), this.web3.eth.accounts.create(['ef12']), this.web3.eth.accounts.create(['ef34'])]
this.accounts[this.accounts[0].address.toLowerCase()] = this.accounts[0] this.accounts[this.accounts[0].address.toLowerCase()] = this.accounts[0]
this.accounts[this.accounts[1].address.toLowerCase()] = this.accounts[1]
this.accounts[this.accounts[2].address.toLowerCase()] = this.accounts[2]
this.accounts[this.accounts[0].address.toLowerCase()].privateKey = Buffer.from(this.accounts[this.accounts[0].address.toLowerCase()].privateKey.slice(2), 'hex') this.accounts[this.accounts[0].address.toLowerCase()].privateKey = Buffer.from(this.accounts[this.accounts[0].address.toLowerCase()].privateKey.slice(2), 'hex')
this.accounts[this.accounts[1].address.toLowerCase()].privateKey = Buffer.from(this.accounts[this.accounts[1].address.toLowerCase()].privateKey.slice(2), 'hex')
this.accounts[this.accounts[2].address.toLowerCase()].privateKey = Buffer.from(this.accounts[this.accounts[2].address.toLowerCase()].privateKey.slice(2), 'hex')
} }
Accounts.prototype.methods = function () { Accounts.prototype.methods = function () {

@ -0,0 +1,9 @@
module.exports = `pragma solidity ^0.4.7;
library TestsAccounts {
function getAccount(uint index) returns (address) {
>accounts<
return accounts[index];
}
}
`

@ -9,18 +9,28 @@ String.prototype.regexIndexOf = function (regex, startpos) {
return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf return (indexOf >= 0) ? (indexOf + (startpos || 0)) : indexOf
} }
function writeTestAccountsContract (accounts) {
var testAccountContract = require('../sol/tests_accounts.sol.js')
var body = 'address[' + accounts.length + '] memory accounts'
if (!accounts.length) body += ';'
else {
body += '= [' + accounts.map((value) => { return `address(${value})` }).join(',') + '];'
}
return testAccountContract.replace('>accounts<', body)
}
var userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-' var userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-'
var isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' electron/') > -1) var isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' electron/') > -1)
// TODO: replace this with remix's own compiler code // TODO: replace this with remix's own compiler code
function compileFileOrFiles (filename, isDirectory, cb) { function compileFileOrFiles (filename, isDirectory, opts, cb) {
let compiler, filepath let compiler, filepath
let accounts = opts.accounts || []
const sources = { const sources = {
'tests.sol': { content: require('../sol/tests.sol.js') }, 'tests.sol': { content: require('../sol/tests.sol.js') },
'remix_tests.sol': { content: require('../sol/tests.sol.js') } 'remix_tests.sol': { content: require('../sol/tests.sol.js') },
'remix_accounts.sol': { content: writeTestAccountsContract(accounts) }
} }
// TODO: for now assumes filepath dir contains all tests, later all this // TODO: for now assumes filepath dir contains all tests, later all this
// should be replaced with remix's & browser solidity compiler code // should be replaced with remix's & browser solidity compiler code
filepath = (isDirectory ? filename : path.dirname(filename)) filepath = (isDirectory ? filename : path.dirname(filename))
@ -61,12 +71,13 @@ function compileFileOrFiles (filename, isDirectory, cb) {
}) })
} }
function compileContractSources (sources, importFileCb, cb) { function compileContractSources (sources, importFileCb, cb, opts) {
let compiler, filepath let compiler, filepath
let accounts = opts.accounts || []
// Iterate over sources keys. Inject test libraries. Inject test library import statements. // Iterate over sources keys. Inject test libraries. Inject test library import statements.
if (!('remix_tests.sol' in sources) && !('tests.sol' in sources)) { if (!('remix_tests.sol' in sources) && !('tests.sol' in sources)) {
sources['remix_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 s = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm const s = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm
for (let file in sources) { for (let file in sources) {

@ -16,13 +16,22 @@ var createWeb3Provider = function () {
return web3 return web3
} }
var runTestSources = function (contractSources, testCallback, resultCallback, finalCallback, importFileCb) { var runTestSources = function (contractSources, testCallback, resultCallback, finalCallback, importFileCb, opts) {
opts = opts || {}
let web3 = opts.web3 || createWeb3Provider()
let accounts = opts.accounts || null
async.waterfall([ async.waterfall([
function getAccountList (next) {
if (accounts) return next()
web3.eth.getAccounts((_err, _accounts) => {
accounts = _accounts
next()
})
},
function compile (next) { function compile (next) {
Compiler.compileContractSources(contractSources, importFileCb, next) Compiler.compileContractSources(contractSources, importFileCb, next)
}, },
function deployAllContracts (compilationResult, next) { function deployAllContracts (compilationResult, next) {
let web3 = createWeb3Provider()
Deployer.deployAll(compilationResult, web3, function (err, contracts) { Deployer.deployAll(compilationResult, web3, function (err, contracts) {
if (err) { if (err) {
next(err) next(err)
@ -33,19 +42,21 @@ var runTestSources = function (contractSources, testCallback, resultCallback, fi
}, },
function determineTestContractsToRun (compilationResult, contracts, next) { function determineTestContractsToRun (compilationResult, contracts, next) {
let contractsToTest = [] let contractsToTest = []
let contractsToTestDetails = []
for (let filename in compilationResult) { for (let filename in compilationResult) {
if (filename.indexOf('_test.sol') < 0) { if (filename.indexOf('_test.sol') < 0) {
continue continue
} }
Object.keys(compilationResult[filename]).forEach(contractName => { Object.keys(compilationResult[filename]).forEach(contractName => {
contractsToTestDetails.push(compilationResult[filename][contractName])
contractsToTest.push(contractName) contractsToTest.push(contractName)
}) })
} }
next(null, contractsToTest, contracts) next(null, contractsToTest, contractsToTestDetails, contracts)
}, },
function runTests (contractsToTest, contracts, next) { function runTests (contractsToTest, contractsToTestDetails, contracts, next) {
let totalPassing = 0 let totalPassing = 0
let totalFailing = 0 let totalFailing = 0
let totalTime = 0 let totalTime = 0
@ -67,7 +78,7 @@ var runTestSources = function (contractSources, testCallback, resultCallback, fi
} }
async.eachOfLimit(contractsToTest, 1, (contractName, index, cb) => { async.eachOfLimit(contractsToTest, 1, (contractName, index, cb) => {
TestRunner.runTest(contractName, contracts[contractName], _testCallback, (err, result) => { TestRunner.runTest(contractName, contracts[contractName], contractsToTestDetails[index], { accounts }, _testCallback, (err, result) => {
if (err) { if (err) {
return cb(err) return cb(err)
} }
@ -95,7 +106,8 @@ var runTestSources = function (contractSources, testCallback, resultCallback, fi
], finalCallback) ], finalCallback)
} }
var runTestFiles = function (filepath, isDirectory, web3) { var runTestFiles = function (filepath, isDirectory, web3, opts) {
opts = opts || {}
const { Signale } = require('signale') const { Signale } = require('signale')
// signale configuration // signale configuration
const options = { const options = {
@ -118,37 +130,49 @@ var runTestFiles = function (filepath, isDirectory, web3) {
} }
} }
const signale = new Signale(options) const signale = new Signale(options)
let accounts = opts.accounts || null
async.waterfall([ async.waterfall([
function getAccountList (next) {
if (accounts) return next(null)
web3.eth.getAccounts((_err, _accounts) => {
accounts = _accounts
next(null)
})
},
function compile (next) { function compile (next) {
Compiler.compileFileOrFiles(filepath, isDirectory, next) Compiler.compileFileOrFiles(filepath, isDirectory, { accounts }, next)
}, },
function deployAllContracts (compilationResult, next) { function deployAllContracts (compilationResult, next) {
Deployer.deployAll(compilationResult, web3, function (err, contracts) { Deployer.deployAll(compilationResult, web3, function (err, contracts) {
if (err) { if (err) {
next(err) next(err)
} }
next(null, compilationResult, contracts) next(null, compilationResult, contracts)
}) })
}, },
function determineTestContractsToRun (compilationResult, contracts, next) { function determineTestContractsToRun (compilationResult, contracts, next) {
let contractsToTest = [] let contractsToTest = []
if (isDirectory) { let contractsToTestDetails = []
fs.readdirSync(filepath).forEach(filename => { var gatherContractsFrom = (filename) => {
if (filename.indexOf('_test.sol') < 0) { if (filename.indexOf('_test.sol') < 0) {
return return
} }
Object.keys(compilationResult[path.basename(filename)]).forEach(contractName => { Object.keys(compilationResult[path.basename(filename)]).forEach(contractName => {
contractsToTest.push(contractName) contractsToTest.push(contractName)
contractsToTestDetails.push(compilationResult[path.basename(filename)][contractName])
}) })
}
if (isDirectory) {
fs.readdirSync(filepath).forEach(filename => {
gatherContractsFrom(filename)
}) })
} else { } else {
contractsToTest = Object.keys(compilationResult[path.basename(filepath)]) gatherContractsFrom(filepath)
} }
next(null, contractsToTest, contractsToTestDetails, contracts)
next(null, contractsToTest, contracts)
}, },
function runTests (contractsToTest, contracts, next) { function runTests (contractsToTest, contractsToTestDetails, contracts, next) {
let totalPassing = 0 let totalPassing = 0
let totalFailing = 0 let totalFailing = 0
let totalTime = 0 let totalTime = 0
@ -172,7 +196,7 @@ var runTestFiles = function (filepath, isDirectory, web3) {
} }
async.eachOfLimit(contractsToTest, 1, (contractName, index, cb) => { async.eachOfLimit(contractsToTest, 1, (contractName, index, cb) => {
TestRunner.runTest(contractName, contracts[contractName], testCallback, (err, result) => { TestRunner.runTest(contractName, contracts[contractName], contractsToTestDetails[index], { accounts }, testCallback, (err, result) => {
if (err) { if (err) {
return cb(err) return cb(err)
} }

@ -2,6 +2,20 @@ var async = require('async')
var changeCase = require('change-case') var changeCase = require('change-case')
var Web3 = require('web3') var Web3 = require('web3')
function getFunctionFullName (signature, methodIdentifiers) {
for (var method in methodIdentifiers) {
if (signature.replace('0x', '') === methodIdentifiers[method].replace('0x', '')) {
return method
}
}
return null
}
function getOverridedSender (userdoc, signature, methodIdentifiers) {
let fullName = getFunctionFullName(signature, methodIdentifiers)
return fullName && userdoc.methods[fullName] ? userdoc.methods[fullName].notice : null
}
function getAvailableFunctions (jsonInterface) { function getAvailableFunctions (jsonInterface) {
return jsonInterface.reverse().filter((x) => x.type === 'function').map((x) => x.name) return jsonInterface.reverse().filter((x) => x.type === 'function').map((x) => x.name)
} }
@ -24,7 +38,7 @@ function createRunList (jsonInterface) {
if (availableFunctions.indexOf('beforeEach') >= 0) { if (availableFunctions.indexOf('beforeEach') >= 0) {
runList.push({name: 'beforeEach', type: 'internal', constant: false}) runList.push({name: 'beforeEach', type: 'internal', constant: false})
} }
runList.push({name: func.name, type: 'test', constant: func.constant}) runList.push({name: func.name, signature: func.signature, type: 'test', constant: func.constant})
if (availableFunctions.indexOf('afterEach') >= 0) { if (availableFunctions.indexOf('afterEach') >= 0) {
runList.push({name: 'afterEach', type: 'internal', constant: false}) runList.push({name: 'afterEach', type: 'internal', constant: false})
} }
@ -37,7 +51,7 @@ function createRunList (jsonInterface) {
return runList return runList
} }
function runTest (testName, testObject, testCallback, resultsCallback) { function runTest (testName, testObject, contractDetails, opts, testCallback, resultsCallback) {
let runList = createRunList(testObject._jsonInterface) let runList = createRunList(testObject._jsonInterface)
let passingNum = 0 let passingNum = 0
@ -45,12 +59,32 @@ function runTest (testName, testObject, testCallback, resultsCallback) {
let timePassed = 0 let timePassed = 0
let web3 = new Web3() let web3 = new Web3()
var userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-'
var isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' electron/') > -1)
if (!isBrowser) {
let signale = require('signale')
signale.warn('DO NOT TRY TO ACCESS (IN YOUR SOLIDITY TEST) AN ACCOUNT GREATER THAN THE LENGTH OF THE FOLLOWING ARRAY (' + opts.accounts.length + ') :')
signale.warn(opts.accounts)
signale.warn('e.g: the following code won\'t work in the current context:')
signale.warn('TestsAccounts.getAccount(' + opts.accounts.length + ')')
}
testCallback({type: 'contract', value: testName, filename: testObject.filename}) testCallback({type: 'contract', value: testName, filename: testObject.filename})
async.eachOfLimit(runList, 1, function (func, index, next) { async.eachOfLimit(runList, 1, function (func, index, next) {
let sender
if (func.signature) {
sender = getOverridedSender(contractDetails.userdoc, func.signature, contractDetails.evm.methodIdentifiers)
if (opts.accounts) {
sender = opts.accounts[sender]
}
}
let sendParams
if (sender) sendParams = { from: sender }
let method = testObject.methods[func.name].apply(testObject.methods[func.name], []) let method = testObject.methods[func.name].apply(testObject.methods[func.name], [])
let startTime = Date.now() let startTime = Date.now()
if (func.constant) { if (func.constant) {
method.call().then((result) => { method.call(sendParams).then((result) => {
let time = Math.ceil((Date.now() - startTime) / 1000.0) let time = Math.ceil((Date.now() - startTime) / 1000.0)
if (result) { if (result) {
testCallback({type: 'testPass', value: changeCase.sentenceCase(func.name), time: time, context: testName}) testCallback({type: 'testPass', value: changeCase.sentenceCase(func.name), time: time, context: testName})
@ -63,7 +97,7 @@ function runTest (testName, testObject, testCallback, resultsCallback) {
next() next()
}) })
} else { } else {
method.send().on('receipt', function (receipt) { method.send(sendParams).on('receipt', function (receipt) {
try { try {
let time = Math.ceil((Date.now() - startTime) / 1000.0) let time = Math.ceil((Date.now() - startTime) / 1000.0)
let topic = Web3.utils.sha3('AssertionEvent(bool,string)') let topic = Web3.utils.sha3('AssertionEvent(bool,string)')
@ -95,6 +129,7 @@ function runTest (testName, testObject, testCallback, resultsCallback) {
return next(err) return next(err)
} }
}).on('error', function (err) { }).on('error', function (err) {
console.error(err)
next(err) next(err)
}) })
} }

@ -10,19 +10,28 @@ const Provider = require('remix-simulator').Provider
function compileAndDeploy (filename, callback) { function compileAndDeploy (filename, callback) {
let web3 = new Web3() let web3 = new Web3()
web3.setProvider(new Provider()) web3.setProvider(new Provider())
let compilationData
let accounts
async.waterfall([ async.waterfall([
function getAccountList (next) {
web3.eth.getAccounts((_err, _accounts) => {
accounts = _accounts
next(_err)
})
},
function compile (next) { function compile (next) {
Compiler.compileFileOrFiles(filename, false, next) Compiler.compileFileOrFiles(filename, false, {accounts}, next)
}, },
function deployAllContracts (compilationResult, next) { function deployAllContracts (compilationResult, next) {
compilationData = compilationResult
Deployer.deployAll(compilationResult, web3, next) Deployer.deployAll(compilationResult, web3, next)
} }
], function (_err, contracts) { ], function (_err, contracts) {
callback(null, contracts) callback(null, compilationData, contracts, accounts)
}) })
} }
describe('testRunner', function () { describe('testRunner', function () {
describe('#runTest', function() { describe('#runTest', function() {
describe('test with beforeAll', function () { describe('test with beforeAll', function () {
@ -30,7 +39,7 @@ describe('testRunner', function () {
let tests = [], results = {} let tests = [], results = {}
before(function (done) { before(function (done) {
compileAndDeploy(filename, function (_err, contracts) { compileAndDeploy(filename, function (_err, compilationData, contracts, accounts) {
var testCallback = function (test) { var testCallback = function (test) {
tests.push(test) tests.push(test)
} }
@ -38,7 +47,7 @@ describe('testRunner', function () {
results = _results results = _results
done() done()
} }
TestRunner.runTest('MyTest', contracts.MyTest, testCallback, resultsCallback) TestRunner.runTest('MyTest', contracts.MyTest, compilationData['simple_storage_test.sol']['MyTest'], { accounts }, testCallback, resultsCallback)
}) })
}) })
@ -66,7 +75,7 @@ describe('testRunner', function () {
let tests = [], results = {} let tests = [], results = {}
before(function (done) { before(function (done) {
compileAndDeploy(filename, function (_err, contracts) { compileAndDeploy(filename, function (_err, compilationData, contracts, accounts) {
var testCallback = function (test) { var testCallback = function (test) {
tests.push(test) tests.push(test)
} }
@ -74,7 +83,7 @@ describe('testRunner', function () {
results = _results results = _results
done() done()
} }
TestRunner.runTest('MyTest', contracts.MyTest, testCallback, resultsCallback) TestRunner.runTest('MyTest', contracts.MyTest, compilationData['simple_storage_test.sol']['MyTest'], { accounts }, testCallback, resultsCallback)
}) })
}) })
@ -101,7 +110,7 @@ describe('testRunner', function () {
let tests = [], results = {} let tests = [], results = {}
before(function (done) { before(function (done) {
compileAndDeploy(filename, function (_err, contracts) { compileAndDeploy(filename, function (_err, compilationData, contracts, accounts) {
var testCallback = function (test) { var testCallback = function (test) {
tests.push(test) tests.push(test)
} }
@ -109,8 +118,8 @@ describe('testRunner', function () {
results = _results results = _results
done() done()
} }
TestRunner.runTest('StringTest', contracts.StringTest, testCallback, resultsCallback) TestRunner.runTest('StringTest', contracts.StringTest, compilationData['simple_string_test.sol']['StringTest'], { accounts }, testCallback, resultsCallback)
TestRunner.runTest('StringTest2', contracts.StringTest2, testCallback, resultsCallback) TestRunner.runTest('StringTest2', contracts.StringTest2, compilationData['simple_string_test.sol']['StringTest2'], { accounts }, testCallback, resultsCallback)
}) })
}) })
@ -138,7 +147,7 @@ describe('testRunner', function () {
let tests = [], results = {} let tests = [], results = {}
before(function (done) { before(function (done) {
compileAndDeploy(filename, function (_err, contracts) { compileAndDeploy(filename, function (_err, compilationData, contracts, accounts) {
var testCallback = function (test) { var testCallback = function (test) {
tests.push(test) tests.push(test)
} }
@ -146,7 +155,7 @@ describe('testRunner', function () {
results = _results results = _results
done() done()
} }
TestRunner.runTest('IntegerTest', contracts.IntegerTest, testCallback, resultsCallback) TestRunner.runTest('IntegerTest', contracts.IntegerTest, compilationData['number_test.sol']['IntegerTest'], { accounts }, testCallback, resultsCallback)
}) })
}) })
@ -157,5 +166,33 @@ describe('testRunner', function () {
assert.equal(results.failureNum, 2) assert.equal(results.failureNum, 2)
}) })
}) })
// Test Transaction with different sender
describe('various sender', function () {
let filename = 'tests/various_sender/sender_test.sol'
let tests = [], results = {}
before(function (done) {
compileAndDeploy(filename, function (_err, compilationData, contracts, accounts) {
var testCallback = function (test) {
tests.push(test)
}
var resultsCallback = function (_err, _results) {
results = _results
done()
}
TestRunner.runTest('SenderTest', contracts.SenderTest, compilationData['sender_test.sol']['SenderTest'], { accounts }, testCallback, resultsCallback)
})
})
it('should have 4 passing tests', function () {
assert.equal(results.passingNum, 4)
})
it('should have 1 failing tests', function () {
assert.equal(results.failureNum, 0)
})
})
}) })
}) })

@ -0,0 +1,28 @@
pragma solidity ^0.4.7;
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "remix_accounts.sol";
contract SenderTest {
function beforeAll () {}
/// 1
function checkSenderIs1 () public {
Assert.equal(msg.sender, TestsAccounts.getAccount(1), "wrong sender in checkSenderIs1");
}
/// 0
function checkSenderIs0 () public {
Assert.equal(msg.sender, TestsAccounts.getAccount(0), "wrong sender in checkSenderIs0");
}
/// 1
function checkSenderIsNt0 () public {
Assert.notEqual(msg.sender, TestsAccounts.getAccount(0), "wrong sender in checkSenderIsNot0");
}
/// 2
function checkSenderIsnt2 () public {
Assert.notEqual(msg.sender, TestsAccounts.getAccount(1), "wrong sender in checkSenderIsnt2");
}
}
Loading…
Cancel
Save