Skip to content
This repository has been archived by the owner on Mar 3, 2021. It is now read-only.

sequence for tests using CLI #1329

Merged
merged 4 commits into from
Nov 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions remix-tests/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,16 @@ function processFile(filePath: string, sources: SrcIfc, isRoot: boolean = false)
const userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-'
const isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' electron/') > -1)

// TODO: replace this with remix's own compiler code
/**
* @dev Compile file or files before running tests (used for CLI execution)
* @param filename Name of file
* @param isDirectory True, if path is a directory
* @param opts Options
* @param cb Callback
*
* TODO: replace this with remix's own compiler code
*/

export function compileFileOrFiles(filename: string, isDirectory: boolean, opts: any, cb: Function) {
let compiler: any
const accounts: string[] = opts.accounts || []
Expand Down Expand Up @@ -125,11 +134,20 @@ export function compileFileOrFiles(filename: string, isDirectory: boolean, opts:
if (!isBrowser) require('signale').fatal(errors)
return cb(errors)
}
cb(err, result.contracts)
cb(err, result.contracts, result.sources) //return callback with contract details & ASTs
})
}
}

/**
* @dev Compile contract source before running tests (used for IDE tests execution)
* @param sources sources
* @param versionUrl url of selected compiler version to load
* @param usingWorker if true, load compiler using web worker
* @param importFileCb Import file callback
* @param opts Options
* @param cb Callback
*/
export function compileContractSources(sources: SrcIfc, versionUrl: any, usingWorker: boolean, importFileCb: any, opts: any, cb: Function) {
let compiler, filepath: string
const accounts: string[] = opts.accounts || []
Expand Down Expand Up @@ -172,6 +190,6 @@ export function compileContractSources(sources: SrcIfc, versionUrl: any, usingWo
if (!isBrowser) require('signale').fatal(errors)
return cb(errors)
}
cb(err, result.contracts)
cb(err, result.contracts, result.sources) // return callback with contract details & ASTs
})
}
35 changes: 25 additions & 10 deletions remix-tests/src/runTestFiles.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import async from 'async'
import fs from './fileSystem'
import { runTest } from './testRunner'
import { TestResultInterface, ResultsInterface } from './types'
import { TestResultInterface, ResultsInterface, compilationInterface, ASTInterface } from './types'
import colors from 'colors'
import Web3 = require('web3')

import { compileFileOrFiles } from './compiler'
import { deployAll } from './deployer'

/**
* @dev run test contract files (used for CLI)
* @param filepath Path of file
* @param isDirectory True, if path is a directory
* @param web3 Web3
* @param opts Options
*/

export function runTestFiles(filepath: string, isDirectory: boolean, web3: Web3, opts?: object) {
opts = opts || {}
const sourceASTs: any = {}
const { Signale } = require('signale')
// signale configuration
const options = {
Expand Down Expand Up @@ -44,26 +53,31 @@ export function runTestFiles(filepath: string, isDirectory: boolean, web3: Web3,
function compile(next: Function) {
compileFileOrFiles(filepath, isDirectory, { accounts }, next)
},
function deployAllContracts (compilationResult, next: Function) {
function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next: Function) {
// Extract AST of test contract file source
for(const filename in asts) {
if(filename.includes('_test.sol'))
sourceASTs[filename] = asts[filename].ast
}
deployAll(compilationResult, web3, (err, contracts) => {
if (err) {
next(err)
}
next(null, compilationResult, contracts)
})
},
function determineTestContractsToRun (compilationResult, contracts, next: Function) {
let contractsToTest: any[] = []
function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next: Function) {
let contractsToTest: string[] = []
let contractsToTestDetails: any[] = []
const gatherContractsFrom = function(filename: string) {
if (filename.indexOf('_test.sol') < 0) {
return
}
try {
Object.keys(compilationResult[filename]).forEach(contractName => {
contractsToTest.push(contractName)
contractsToTestDetails.push(compilationResult[filename][contractName])
})
Object.keys(compilationResult[filename]).forEach(contractName => {
contractsToTest.push(contractName)
contractsToTestDetails.push(compilationResult[filename][contractName])
})
} catch (e) {
console.error(e)
}
Expand All @@ -77,7 +91,7 @@ export function runTestFiles(filepath: string, isDirectory: boolean, web3: Web3,
}
next(null, contractsToTest, contractsToTestDetails, contracts)
},
function runTests(contractsToTest, contractsToTestDetails, contracts, next: Function) {
function runTests(contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next: Function) {
let totalPassing: number = 0
let totalFailing: number = 0
let totalTime: number = 0
Expand All @@ -103,7 +117,8 @@ export function runTestFiles(filepath: string, isDirectory: boolean, web3: Web3,

async.eachOfLimit(contractsToTest, 1, (contractName: string, index, cb) => {
try {
runTest(contractName, contracts[contractName], contractsToTestDetails[index], { accounts }, _testCallback, (err, result) => {
const fileAST = sourceASTs[contracts[contractName]['filename']]
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts }, _testCallback, (err, result) => {
if (err) {
console.log(err)
return cb(err)
Expand Down
33 changes: 25 additions & 8 deletions remix-tests/src/runTestSources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ require('colors')
import { compileContractSources } from './compiler'
import { deployAll } from './deployer'
import { runTest } from './testRunner'
import { TestResultInterface, ResultsInterface } from './types'
import { TestResultInterface } from './types'

import Web3 = require('web3')
import { Provider } from 'remix-simulator'
import { FinalResult } from './types'
import { FinalResult, SrcIfc, compilationInterface, ASTInterface } from './types'

const createWeb3Provider = async function () {
let web3 = new Web3()
Expand All @@ -18,8 +18,20 @@ const createWeb3Provider = async function () {
return web3
}

export async function runTestSources(contractSources, versionUrl, usingWorker, testCallback, resultCallback, finalCallback, importFileCb, opts) {
/**
* @dev Run tests from source of a test contract file (used for IDE)
* @param contractSources Sources of contract
* @param versionUrl url of selected compiler version to load
* @param usingWorker if true, load compiler using web worker
* @param testCallback Test callback
* @param resultCallback Result Callback
* @param finalCallback Final Callback
* @param importFileCb Import file callback
* @param opts Options
*/
export async function runTestSources(contractSources: SrcIfc, versionUrl: string, usingWorker: boolean, testCallback: Function, resultCallback: Function, finalCallback: any, importFileCb: Function, opts: any) {
opts = opts || {}
const sourceASTs: any = {}
let web3 = opts.web3 || await createWeb3Provider()
let accounts = opts.accounts || null
async.waterfall([
Expand All @@ -33,7 +45,11 @@ export async function runTestSources(contractSources, versionUrl, usingWorker, t
function compile (next) {
compileContractSources(contractSources, versionUrl, usingWorker, importFileCb, { accounts }, next)
},
function deployAllContracts (compilationResult, next) {
function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) {
for(const filename in asts) {
if(filename.includes('_test.sol'))
sourceASTs[filename] = asts[filename].ast
}
deployAll(compilationResult, web3, (err, contracts) => {
if (err) {
next(err)
Expand All @@ -42,8 +58,8 @@ export async function runTestSources(contractSources, versionUrl, usingWorker, t
next(null, compilationResult, contracts)
})
},
function determineTestContractsToRun (compilationResult, contracts, next) {
let contractsToTest: any[] = []
function determineTestContractsToRun (compilationResult: compilationInterface, contracts: any, next) {
let contractsToTest: string[] = []
let contractsToTestDetails: any[] = []

for (let filename in compilationResult) {
Expand All @@ -57,7 +73,7 @@ export async function runTestSources(contractSources, versionUrl, usingWorker, t
}
next(null, contractsToTest, contractsToTestDetails, contracts)
},
function runTests(contractsToTest, contractsToTestDetails, contracts, next) {
function runTests(contractsToTest: string[], contractsToTestDetails: any[], contracts: any, next) {
let totalPassing = 0
let totalFailing = 0
let totalTime = 0
Expand All @@ -79,7 +95,8 @@ export async function runTestSources(contractSources, versionUrl, usingWorker, t
}

async.eachOfLimit(contractsToTest, 1, (contractName: string, index: string | number, cb: ErrorCallback) => {
runTest(contractName, contracts[contractName], contractsToTestDetails[index], { accounts }, _testCallback, (err, result) => {
const fileAST = sourceASTs[contracts[contractName]['filename']]
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts }, _testCallback, (err, result) => {
if (err) {
return cb(err)
}
Expand Down
71 changes: 50 additions & 21 deletions remix-tests/src/testRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,55 @@ function getOverridedSender (userdoc, signature: string, methodIdentifiers) {
return fullName && accountIndex ? accountIndex[1] : null
}

function getAvailableFunctions (jsonInterface) {
return jsonInterface.reverse().filter((x) => x.type === 'function').map((x) => x.name)
/**
* @dev returns functions of a test contract file in same sequence they appear in file (using passed AST)
* @param fileAST AST of test contract file source
* @param testContractName Name of test contract
*/

function getAvailableFunctions (fileAST: any, testContractName: string) {
const contractAST: any[] = fileAST.nodes.filter(node => node.name === testContractName && node.nodeType === 'ContractDefinition')
const funcNodes: any[] = contractAST[0].nodes.filter(node => node.kind === 'function' && node.nodeType === "FunctionDefinition")
const funcList: string[] = funcNodes.map(node => node.name)
return funcList;
}

function getTestFunctions (jsonInterface) {
let specialFunctions = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach']
return jsonInterface.filter((x) => specialFunctions.indexOf(x.name) < 0 && x.type === 'function')
/**
* @dev returns ABI of passed method list from passed interface
* @param jsonInterface Json Interface
* @param funcList Methods to extract the interface of
*/

function getTestFunctionsInterface (jsonInterface: any, funcList: string[]) {
const functionsInterface: any[] = []
const specialFunctions = ['beforeAll', 'beforeEach', 'afterAll', 'afterEach']
for(const func of funcList){
if(!specialFunctions.includes(func)) {
const funcInterface= jsonInterface.find(node => node.type === 'function' && node.name === func)
functionsInterface.push(funcInterface)
}
}
return functionsInterface
}

function createRunList (jsonInterface): RunListInterface[] {
let availableFunctions = getAvailableFunctions(jsonInterface)
let testFunctions = getTestFunctions(jsonInterface)
/**
* @dev Prepare a list of tests to run using test contract file ABI, AST & contract name
* @param jsonInterface File JSON interface
* @param fileAST File AST
* @param testContractName Test contract name
*/

function createRunList (jsonInterface: any, fileAST: any, testContractName: string): RunListInterface[] {
const availableFunctions: string[] = getAvailableFunctions(fileAST, testContractName)
const testFunctionsInterface: any[] = getTestFunctionsInterface(jsonInterface, availableFunctions)

let runList: RunListInterface[] = []

if (availableFunctions.indexOf('beforeAll') >= 0) {
runList.push({ name: 'beforeAll', type: 'internal', constant: false })
}

for (let func of testFunctions) {
for (const func of testFunctionsInterface) {
if (availableFunctions.indexOf('beforeEach') >= 0) {
runList.push({ name: 'beforeEach', type: 'internal', constant: false })
}
Expand All @@ -54,13 +84,12 @@ function createRunList (jsonInterface): RunListInterface[] {
return runList
}

export function runTest (testName, testObject: any, contractDetails: any, opts: any, testCallback: TestCbInterface, resultsCallback: ResultCbInterface) {
let runList = createRunList(testObject._jsonInterface)

export function runTest (testName, testObject: any, contractDetails: any, fileAST: any, opts: any, testCallback: TestCbInterface, resultsCallback: ResultCbInterface) {
const runList: RunListInterface[] = createRunList(testObject._jsonInterface, fileAST, testName)
let passingNum: number = 0
let failureNum: number = 0
let timePassed: number = 0
let web3 = new Web3()
const web3 = new Web3()

const accts: TestResultInterface = {
type: 'accountList',
Expand All @@ -87,11 +116,11 @@ export function runTest (testName, testObject: any, contractDetails: any, opts:
let sendParams
if (sender) sendParams = { from: sender }

let method = testObject.methods[func.name].apply(testObject.methods[func.name], [])
let startTime = Date.now()
const method = testObject.methods[func.name].apply(testObject.methods[func.name], [])
const startTime = Date.now()
if (func.constant) {
method.call(sendParams).then((result) => {
let time = (Date.now() - startTime) / 1000.0
const time = (Date.now() - startTime) / 1000.0
if (result) {
const resp: TestResultInterface = {
type: 'testPass',
Expand All @@ -118,12 +147,12 @@ export function runTest (testName, testObject: any, contractDetails: any, opts:
} else {
method.send(sendParams).on('receipt', (receipt) => {
try {
let time: number = (Date.now() - startTime) / 1000.0
let topic = Web3.utils.sha3('AssertionEvent(bool,string)')
const time: number = (Date.now() - startTime) / 1000.0
const topic = Web3.utils.sha3('AssertionEvent(bool,string)')
let testPassed: boolean = false

for (let i in receipt.events) {
let event = receipt.events[i]
for (const i in receipt.events) {
const event = receipt.events[i]
if (event.raw.topics.indexOf(topic) >= 0) {
const testEvent = web3.eth.abi.decodeParameters(['bool', 'string'], event.raw.data)
if (!testEvent[0]) {
Expand Down Expand Up @@ -160,7 +189,7 @@ export function runTest (testName, testObject: any, contractDetails: any, opts:
}
}).on('error', function (err: any) {
console.error(err)
let time: number = (Date.now() - startTime) / 1000.0
const time: number = (Date.now() - startTime) / 1000.0
const resp: TestResultInterface = {
type: 'testFailure',
value: changeCase.sentenceCase(func.name),
Expand Down
Loading