From 35ab1b28f22ed6ccbca657bff0f84f239f925e71 Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Mon, 11 Nov 2019 17:39:17 +0530 Subject: [PATCH 1/6] import supported in test contract --- remix-tests/src/compiler.ts | 59 +++++++++++++------ .../examples_5/contract/simple_storage.sol | 22 +++++++ remix-tests/tests/examples_5/lib/EvenOdd.sol | 15 +++++ .../examples_5/test/simple_storage_test.sol | 24 ++++++++ remix-tests/tests/testRunner.ts | 27 +++++++++ 5 files changed, 129 insertions(+), 18 deletions(-) create mode 100644 remix-tests/tests/examples_5/contract/simple_storage.sol create mode 100644 remix-tests/tests/examples_5/lib/EvenOdd.sol create mode 100644 remix-tests/tests/examples_5/test/simple_storage_test.sol diff --git a/remix-tests/src/compiler.ts b/remix-tests/src/compiler.ts index ae48a8c097..3d9b297479 100644 --- a/remix-tests/src/compiler.ts +++ b/remix-tests/src/compiler.ts @@ -21,6 +21,32 @@ function writeTestAccountsContract (accounts: string[]) { return testAccountContract.replace('>accounts<', body) } +function processFile(filePath: string, sources: any, isRoot: boolean = false) { + try{ + const importRegEx = /import ['"](.+?)['"];/g; + let group: any =''; + if(filePath.includes('tests.sol') || filePath.includes('remix_tests.sol') || filePath.includes('remix_accounts.sol') || Object.keys(sources).includes(filePath)) + return + + let content = fs.readFileSync(filePath, { encoding: 'utf-8' }); + const s = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm + if (isRoot && filePath.indexOf('_test.sol') > 0 && regexIndexOf(content, s) < 0) { + const includeTestLibs = '\nimport \'remix_tests.sol\';\n' + content = includeTestLibs.concat(content) + } + sources[filePath] = {content}; + importRegEx.exec(''); // Resetting state of RegEx + while (group = importRegEx.exec(content)) { + const importedFile = group[1]; + const importedFilePath = path.join(path.dirname(filePath), importedFile); + processFile(importedFilePath, sources) + } + } + catch(error){ + throw error; + } + }; + const userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-' const isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' electron/') > -1) @@ -34,26 +60,23 @@ export function compileFileOrFiles(filename: string, isDirectory: boolean, opts: 'remix_accounts.sol': { content: writeTestAccountsContract(accounts) } } const filepath = (isDirectory ? filename : path.dirname(filename)) - // TODO: for now assumes filepath dir contains all tests, later all this - // should be replaced with remix's & browser solidity compiler code - - // This logic is wrong - // We should only look into current file if a full file name with path is given - // We should only walk through directory if a directory name is passed try { - // walkSync only if it is a directory - fs.walkSync(filepath, (foundpath: string) => { - // only process .sol files - if (foundpath.split('.').pop() === 'sol') { - let c = fs.readFileSync(foundpath).toString() - const s = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm - let includeTestLibs = '\nimport \'remix_tests.sol\';\n' - if (foundpath.indexOf('_test.sol') > 0 && regexIndexOf(c, s) < 0) { - c = includeTestLibs.concat(c) - } - sources[foundpath] = { content: c } + if(!isDirectory && fs.existsSync(filename)) { + if (filename.split('.').pop() === 'sol') { + processFile(filename, sources, true) + } else { + throw new Error('Not a solidity file') } - }) + } else { + // walkSync only if it is a directory + fs.walkSync(filepath, (foundpath: string) => { + // only process .sol files + if (foundpath.split('.').pop() === 'sol') { + processFile(foundpath, sources, true) + } + }) + } + } catch (e) { throw e } finally { diff --git a/remix-tests/tests/examples_5/contract/simple_storage.sol b/remix-tests/tests/examples_5/contract/simple_storage.sol new file mode 100644 index 0000000000..745fe39605 --- /dev/null +++ b/remix-tests/tests/examples_5/contract/simple_storage.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.5.0; + +import "../../examples_4/SafeMath.sol"; +import "../lib/EvenOdd.sol"; + +contract SimpleStorage is EvenOdd{ + using SafeMath for uint256; + uint public storedData; + + constructor() public { + storedData = 100; + } + + function set(uint x) public { + storedData = x; + } + + function get() public view returns (uint retVal) { + return storedData; + } + +} diff --git a/remix-tests/tests/examples_5/lib/EvenOdd.sol b/remix-tests/tests/examples_5/lib/EvenOdd.sol new file mode 100644 index 0000000000..f5847ee481 --- /dev/null +++ b/remix-tests/tests/examples_5/lib/EvenOdd.sol @@ -0,0 +1,15 @@ +pragma solidity ^0.5.0; + +contract EvenOdd { + + /** + * @dev Tells whether a number is even or odd + * @param num Number to check + */ + + function check(int num) public pure returns (string memory){ + if(num % 2 == 0) + return "EVEN"; + return "ODD"; + } +} \ No newline at end of file diff --git a/remix-tests/tests/examples_5/test/simple_storage_test.sol b/remix-tests/tests/examples_5/test/simple_storage_test.sol new file mode 100644 index 0000000000..00be3f627e --- /dev/null +++ b/remix-tests/tests/examples_5/test/simple_storage_test.sol @@ -0,0 +1,24 @@ +pragma solidity ^0.5.0; +import "./../contract/simple_storage.sol"; + +contract StorageResolveTest { + SimpleStorage foo; + + function beforeAll() public { + foo = new SimpleStorage(); + } + + function initialValueShouldBe100() public returns (bool) { + return Assert.equal(foo.get(), 100, "initial value is not correct"); + } + + //Test imported contract functions + function checkIfEven() public returns (bool) { + return Assert.equal(foo.check(10), 'EVEN', "value is odd"); + } + + function checkIfOdd() public returns (bool) { + return Assert.equal(foo.check(11), 'ODD', "value is even"); + } + +} diff --git a/remix-tests/tests/testRunner.ts b/remix-tests/tests/testRunner.ts index 1ee893578e..ba79ca8b54 100644 --- a/remix-tests/tests/testRunner.ts +++ b/remix-tests/tests/testRunner.ts @@ -183,6 +183,33 @@ describe('testRunner', () => { }) }) + // Test multiple directory import in test contract + describe('test multiple directory import in test contract', function () { + let filename = 'tests/examples_5/test/simple_storage_test.sol' + + before(function (done) { + compileAndDeploy(filename, (_err, compilationData, contracts, accounts) => { + runTest('StorageResolveTest', contracts.StorageResolveTest, compilationData[filename]['StorageResolveTest'], { accounts }, testCallback, resultsCallback(done)) + }) + }) + + after(() => { tests = [] }) + + it('should 3 passing tests', function () { + assert.equal(results.passingNum, 3) + }) + + it('should return 4 messages', function () { + deepEqualExcluding(tests, [ + { type: 'accountList', value: accounts }, + { type: 'contract', value: 'StorageResolveTest', filename: 'tests/examples_5/test/simple_storage_test.sol' }, + { type: 'testPass', value: 'Initial value should be100', context: 'StorageResolveTest' }, + { type: 'testPass', value: 'Check if odd', context: 'StorageResolveTest' }, + { type: 'testPass', value: 'Check if even', context: 'StorageResolveTest' } + ], ['time']) + }) + }) + //Test signed/unsigned integer weight describe('test number weight', function () { let filename = 'tests/number/number_test.sol' From 7b21a71ded9d430fc9a9f550a87a9085a4305b30 Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Tue, 12 Nov 2019 13:06:46 +0530 Subject: [PATCH 2/6] requested changes done --- remix-tests/src/compiler.ts | 70 ++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/remix-tests/src/compiler.ts b/remix-tests/src/compiler.ts index 3d9b297479..6162e46e3a 100644 --- a/remix-tests/src/compiler.ts +++ b/remix-tests/src/compiler.ts @@ -21,31 +21,53 @@ function writeTestAccountsContract (accounts: string[]) { return testAccountContract.replace('>accounts<', body) } +/** + * Check if path includes name of a remix test file + * @param path file path to check + */ + +function isRemixTestFile(path: string) { + return ['tests.sol', 'remix_tests.sol', 'remix_accounts.sol'].some(name => path.includes(name)) +} + +/** + * @dev Process file to prepare sources object to be passed in solc compiler input + * + * See: https://solidity.readthedocs.io/en/latest/using-the-compiler.html#input-description + * + * @param filePath path of file to process + * @param sources existing 'sources' object in which keys are the "global" names of the source files and + * value is object containing content of corresponding file with under key 'content' + * @param isRoot True, If file is a root test contract file which is getting processed, not an imported file + */ + function processFile(filePath: string, sources: any, isRoot: boolean = false) { - try{ - const importRegEx = /import ['"](.+?)['"];/g; - let group: any =''; - if(filePath.includes('tests.sol') || filePath.includes('remix_tests.sol') || filePath.includes('remix_accounts.sol') || Object.keys(sources).includes(filePath)) - return + const importRegEx: RegExp = /import ['"](.+?)['"];/g; + let group: RegExpExecArray| null = null; + const isFileAlreadyInSources: boolean = Object.keys(sources).includes(filePath) - let content = fs.readFileSync(filePath, { encoding: 'utf-8' }); - const s = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm - if (isRoot && filePath.indexOf('_test.sol') > 0 && regexIndexOf(content, s) < 0) { - const includeTestLibs = '\nimport \'remix_tests.sol\';\n' - content = includeTestLibs.concat(content) - } - sources[filePath] = {content}; - importRegEx.exec(''); // Resetting state of RegEx - while (group = importRegEx.exec(content)) { - const importedFile = group[1]; - const importedFilePath = path.join(path.dirname(filePath), importedFile); - processFile(importedFilePath, sources) - } + // Return if file is a remix test file or already processed + if(isRemixTestFile(filePath) || isFileAlreadyInSources) + return + + let content: string = fs.readFileSync(filePath, { encoding: 'utf-8' }); + const testFileImportRegEx: RegExp = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm + + // import 'remix_tests.sol', if file is a root test contract file and doesn't already have it + if (isRoot && filePath.includes('_test.sol') && regexIndexOf(content, testFileImportRegEx) < 0) { + const includeTestLibs: string = '\nimport \'remix_tests.sol\';\n' + content = includeTestLibs.concat(content) } - catch(error){ - throw error; + sources[filePath] = {content}; + importRegEx.exec(''); // Resetting state of RegEx + + // Process each 'import' in file content + while (group = importRegEx.exec(content)) { + const importedFile: string = group[1]; + const importedFilePath: string = path.join(path.dirname(filePath), importedFile); + processFile(importedFilePath, sources) } - }; +} const userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-' const isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' electron/') > -1) @@ -53,13 +75,13 @@ const isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' elect // TODO: replace this with remix's own compiler code export function compileFileOrFiles(filename: string, isDirectory: boolean, opts: any, cb: Function) { let compiler: any - let accounts = opts.accounts || [] - const sources = { + let accounts: any[] = opts.accounts || [] + const sources: any = { 'tests.sol': { content: require('../sol/tests.sol.js') }, 'remix_tests.sol': { content: require('../sol/tests.sol.js') }, 'remix_accounts.sol': { content: writeTestAccountsContract(accounts) } } - const filepath = (isDirectory ? filename : path.dirname(filename)) + const filepath: string = (isDirectory ? filename : path.dirname(filename)) try { if(!isDirectory && fs.existsSync(filename)) { if (filename.split('.').pop() === 'sol') { From b6f71ac3d9f5025fd2749da4f89cc04f3d05d48f Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Tue, 12 Nov 2019 13:17:12 +0530 Subject: [PATCH 3/6] dev doc update --- remix-tests/src/compiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remix-tests/src/compiler.ts b/remix-tests/src/compiler.ts index 6162e46e3a..0ff0f9fbba 100644 --- a/remix-tests/src/compiler.ts +++ b/remix-tests/src/compiler.ts @@ -22,7 +22,7 @@ function writeTestAccountsContract (accounts: string[]) { } /** - * Check if path includes name of a remix test file + * @dev Check if path includes name of a remix test file * @param path file path to check */ From fa74b2e6a2d8218bba1df6593096fd97ffb73ddb Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Tue, 12 Nov 2019 16:24:02 +0530 Subject: [PATCH 4/6] sources type --- remix-tests/src/compiler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/remix-tests/src/compiler.ts b/remix-tests/src/compiler.ts index 0ff0f9fbba..52ca31590a 100644 --- a/remix-tests/src/compiler.ts +++ b/remix-tests/src/compiler.ts @@ -41,7 +41,7 @@ function isRemixTestFile(path: string) { * @param isRoot True, If file is a root test contract file which is getting processed, not an imported file */ -function processFile(filePath: string, sources: any, isRoot: boolean = false) { +function processFile(filePath: string, sources: SrcIfc, isRoot: boolean = false) { const importRegEx: RegExp = /import ['"](.+?)['"];/g; let group: RegExpExecArray| null = null; const isFileAlreadyInSources: boolean = Object.keys(sources).includes(filePath) @@ -76,7 +76,7 @@ const isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' elect export function compileFileOrFiles(filename: string, isDirectory: boolean, opts: any, cb: Function) { let compiler: any let accounts: any[] = opts.accounts || [] - const sources: any = { + const sources: SrcIfc = { 'tests.sol': { content: require('../sol/tests.sol.js') }, 'remix_tests.sol': { content: require('../sol/tests.sol.js') }, 'remix_accounts.sol': { content: writeTestAccountsContract(accounts) } From 1ece2283bd1c3a9ad8968df657c2699098df8d67 Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Tue, 12 Nov 2019 16:44:21 +0530 Subject: [PATCH 5/6] more types improved --- remix-tests/src/compiler.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/remix-tests/src/compiler.ts b/remix-tests/src/compiler.ts index 52ca31590a..a87cc689b0 100644 --- a/remix-tests/src/compiler.ts +++ b/remix-tests/src/compiler.ts @@ -75,7 +75,7 @@ const isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' elect // TODO: replace this with remix's own compiler code export function compileFileOrFiles(filename: string, isDirectory: boolean, opts: any, cb: Function) { let compiler: any - let accounts: any[] = opts.accounts || [] + const accounts: any[] = opts.accounts || [] const sources: SrcIfc = { 'tests.sol': { content: require('../sol/tests.sol.js') }, 'remix_tests.sol': { content: require('../sol/tests.sol.js') }, @@ -130,19 +130,20 @@ export function compileFileOrFiles(filename: string, isDirectory: boolean, opts: } } -export function compileContractSources(sources: SrcIfc, versionUrl: any, usingWorker: any, importFileCb: any, opts: any, cb: Function) { +export function compileContractSources(sources: SrcIfc, versionUrl: any, usingWorker: boolean, importFileCb: any, opts: any, cb: Function) { let compiler, filepath: string - let accounts = opts.accounts || [] + const accounts = opts.accounts || [] // Iterate over sources keys. Inject test libraries. Inject test library import statements. if (!('remix_tests.sol' in sources) && !('tests.sol' in sources)) { 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 - let includeTestLibs = '\nimport \'remix_tests.sol\';\n' + const testFileImportRegEx: RegExp = /^(import)\s['"](remix_tests.sol|tests.sol)['"];/gm + + let includeTestLibs: string = '\nimport \'remix_tests.sol\';\n' for (let file in sources) { - const c = sources[file].content - if (file.indexOf('_test.sol') > 0 && c && regexIndexOf(c, s) < 0) { + const c: string = sources[file].content + if (file.includes('_test.sol') && c && regexIndexOf(c, testFileImportRegEx) < 0) { sources[file].content = includeTestLibs.concat(c) } } From f5383f7a7ba9419e15a94029e155c7a1723c63d2 Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Tue, 12 Nov 2019 16:46:46 +0530 Subject: [PATCH 6/6] types --- remix-tests/src/compiler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/remix-tests/src/compiler.ts b/remix-tests/src/compiler.ts index a87cc689b0..d2d5ed253e 100644 --- a/remix-tests/src/compiler.ts +++ b/remix-tests/src/compiler.ts @@ -75,7 +75,7 @@ const isBrowser = !(typeof (window) === 'undefined' || userAgent.indexOf(' elect // 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: any[] = opts.accounts || [] + const accounts: string[] = opts.accounts || [] const sources: SrcIfc = { 'tests.sol': { content: require('../sol/tests.sol.js') }, 'remix_tests.sol': { content: require('../sol/tests.sol.js') }, @@ -132,7 +132,7 @@ export function compileFileOrFiles(filename: string, isDirectory: boolean, opts: export function compileContractSources(sources: SrcIfc, versionUrl: any, usingWorker: boolean, importFileCb: any, opts: any, cb: Function) { let compiler, filepath: string - const accounts = opts.accounts || [] + const accounts: string[] = opts.accounts || [] // Iterate over sources keys. Inject test libraries. Inject test library import statements. if (!('remix_tests.sol' in sources) && !('tests.sol' in sources)) { sources['remix_tests.sol'] = { content: require('../sol/tests.sol.js') }