From 41f12fabccb73c6c3ef18ace205f625c61854fe9 Mon Sep 17 00:00:00 2001 From: Cazacu Tudor Date: Tue, 30 Jul 2019 16:34:21 +0300 Subject: [PATCH 1/5] [Issue 64] Add support for running multiple unit tests frameworks --- lib/commands/make.js | 19 ++--- lib/commands/test.js | 165 +++---------------------------------------- lib/utils/schema.js | 4 ++ lib/utils/utils.js | 8 +++ 4 files changed, 25 insertions(+), 171 deletions(-) diff --git a/lib/commands/make.js b/lib/commands/make.js index 64599a6..252d687 100644 --- a/lib/commands/make.js +++ b/lib/commands/make.js @@ -1,13 +1,14 @@ -const archiver = require('archiver') const fs = require('fs') const path = require('path') -const log = require('../utils/log') -const properties = require('../utils/properties') const mkdirp = require('mkdirp') const fse = require('fs-extra') const rimraf = require('rimraf') +const log = require('../utils/log') +const archiver = require('archiver') +const utils = require('../utils/utils') const inserter = require('../utils/inserter') const childProcess = require('child_process') +const properties = require('../utils/properties') const temp = '.ukor' function moveToTempDir(flavor) { @@ -31,18 +32,8 @@ function moveToTempDir(flavor) { } } -function execPreBuildScript() { - if (properties.preBuild != null && properties.preBuild != '') { - log.info('exec ' + properties.preBuild) - let result = childProcess.execFileSync('node', [properties.preBuild]) - if (log.level == 'verbose') { - log.verbose([properties.preBuild, 'exec:', result].join(' ')) - } - } -} - function bundleFlavor(options, callback) { - execPreBuildScript() + if (properties.preBuild) utils.execScript(properties.preBuild) const flavor = options.flavor let flavorSrc = properties.flavors[flavor].src diff --git a/lib/commands/test.js b/lib/commands/test.js index 9a4a41e..f0a071f 100644 --- a/lib/commands/test.js +++ b/lib/commands/test.js @@ -1,169 +1,20 @@ const request = require('request') const log = require('../utils/log') -const path = require('path') const installer = require('./install') -const http = require('http') -const fs = require('fs') -const getIP = require('ip') -const XMLWriter = require('xml-writer') -const mkdirp = require('mkdirp') +const utils = require('../utils/utils') const properties = require('../utils/properties') -function runLogServer(ip, port, timeout, callback) { - let logServer = http.createServer((req, res) => { - let body = [] - req.on('data', data => { - body.push(data) - }) - req.on('end', () => { - try { - body = Buffer.concat(body).toString() - let stats = JSON.parse(body) - writeJunit(stats) - if (stats) { - log.info('------------') - log.info('Test Results') - log.info( - 'Pass: %d | Fail: %d | Crash: %d', - stats.correct, - stats.fail, - stats.crash - ) - log.info('===================================') - stats.suites.forEach(suite => { - log.info('%s: %d tests', suite.name, suite.tests.length) - suite.tests.forEach(test => { - log.info('\t%s: %s', test.name, test.result) - if (test.result != 'Success') { - log.info( - '\t\tError %d: %s', - test.error.code, - test.error.message - ) - } - }) - log.info('---------------------------------') - }) - log.info( - 'Pass: %d | Fail: %d | Crash: %d', - stats.correct, - stats.fail, - stats.crash - ) - if (stats.fail > 0) { - process.exit(-1) - } - } - callback ? callback(stats) : null - res.statusCode = '200' - } catch (e) { - log.error(e.message) - res.statusCode = '403' - } - res.end('ok', () => { - logServer.close() - }) - }) - }) - logServer.listen(parseInt(port), getIP.address(), () => { - log.info('listening for tests on %s:%s', getIP.address(), port) - }) - return logServer +function runTestScript(script, execArgs) { + log.info(`Running ${script} script with args ${execArgs}...`) + utils.execScript(script, execArgs) } -function writeJunit(stats) { - log.info('building junit xml') - const xml = new XMLWriter() - xml.startDocument() - xml - .startElement('testsuites') - .writeAttribute('name', 'rokuTests') - .writeAttribute('tests', '' + stats.total) - .writeAttribute('failures', '' + stats.fail) - stats.suites.forEach(suite => { - xml - .startElement('testsuite') - .writeAttribute('name', suite.name) - .writeAttribute('tests', '' + suite.total) - .writeAttribute('failures', suite.fail) - .writeAttribute('skipped', suite.skipped) - suite.tests.forEach(test => { - xml - .startElement('testcase') - .writeAttribute('name', test.name) - .writeAttribute('time', '' + test.time * 0.001) - if (test.result.toLowerCase() === 'fail') { - xml - .startElement('failure') - .writeAttribute('type', '' + test.error.code) - .text(test.error.message) - .endElement() - } else if (test.result.toLowerCase() === 'skipped') { - xml - .startElement('skipped') - .endElement() - } - xml.endElement() - }) - xml.endElement() +function test(options, callback) { + installer.installTest(options, (ip, success) => { + if (success) properties.runUnitTestsScript ? runTestScript(properties.runUnitTestsScript, [ip]) : log.error(`runUnitTestScript missing from properties`) }) - xml.endElement() - xml.endDocument() - const testOut = `./${properties.sourceDir}/tests` - mkdirp(testOut) - log.info(`writing ${testOut}/ukorTests.xml and .junit`) - fs.writeFileSync(path.join(testOut, 'ukorTests.junit'), xml.toString()) - fs.writeFileSync(path.join(testOut, 'ukorTests.xml'), xml.toString()) - log.info('successfully wrote junit xml') -} - -function runTests(ip, auth, port, callback) { - setTimeout(() => { - let url = - 'http://' + - ip + - ':8060/launch/dev?RunTests=true&host=' + - getIP.address() + - '&port=' + - port - log.debug('starting tests with: %s', url) - request.post( - url, - { - auth: auth - }, - (err, response, body) => { - if (response.statusCode != 200) { - log.error('Error starting tests: %d', response.statusCode) - process.exit(-1) - } else { - log.info('Tests started') - } - callback ? callback(response.statusCode == 200) : null - } - ) - }, 10*1000) //run tests 10 seconds after pushing } module.exports = { - test: (options, callback) => { - installer.installTest(options, (ip, success) => { - log.info('finished install, running tests') - if (!success) return - let port = options.port || '8086' - let server = runLogServer(ip, port, 5 * 60 * 1000, results => { - if (results) { - process.exit(0) - } else { - process.exit(-1) - } - }) - runTests(ip, options.auth, options.port, success => { - if (!success) { - server.close() - process.exit(-1) - } - }) - }) - } + test } diff --git a/lib/utils/schema.js b/lib/utils/schema.js index b1d83fd..2160747 100644 --- a/lib/utils/schema.js +++ b/lib/utils/schema.js @@ -82,6 +82,10 @@ module.exports = { type: 'string', pattern: '\S*', }, + runUnitTestsScript: { + type: 'string', + pattern: '\S*', + }, version: { type: 'string', pattern: '^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:[\+][0-9A-Za-z-]+)?$', diff --git a/lib/utils/utils.js b/lib/utils/utils.js index ffe05a1..1ae6ba0 100644 --- a/lib/utils/utils.js +++ b/lib/utils/utils.js @@ -4,6 +4,7 @@ const log = require('./log') const xml2js = require('xml2js') const request = require('request') const properties = require('./properties') +const childProcess = require('child_process') const matchers = { ip: /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/, usn: /^[A-Z0-9]{12}$/ @@ -49,6 +50,12 @@ function parseAuth(auth) { return null } +function execScript(script, args=[]) { + log.info('exec: ' + script) + let result = childProcess.execFileSync('node', [script].concat(args)) + log.verbose([script, 'result: ', result].join(' ')) +} + function continueIfExists(object, message) { if (!object) { log.error(message) @@ -73,6 +80,7 @@ function getUsn(options) { module.exports = { parseRoku, parseAuth, + execScript, continueIfExists, getAllSourceFiles, getDeviceInfo, From 706bbcffc0c9b6cf4b5bb866cfdbde1888641c6e Mon Sep 17 00:00:00 2001 From: Cazacu Tudor Date: Tue, 30 Jul 2019 17:02:20 +0300 Subject: [PATCH 2/5] [Issue 64] Update README.md --- README.md | 53 ++++++++++++++++++++++++++++---------------- lib/commands/make.js | 1 - 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 02608ee..287269f 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ The `constants.yaml` file is *per-flavor*. In the file, you can define strings ( buildDir: 'build' sourceDir: 'src' preBuild: 'scripts/main.js' +runUnitTestsScript: 'scripts/runTests.js' mainFlavor: 'flavorName' flavors: { flavorName: { @@ -182,28 +183,39 @@ NOTE: you will need to define a `packageReference` and `packageKey` in `ukor.pro ## Testing -Ukor uses `UnitTestFramework.brs` as part of its unit test runner and test reporting feature. This is especially useful if you plan on having Continuous Integration as part of your workflow. +With ukor you have possibility to use whatever unit test framework you want ! ### Setup -First, copy the modified `UnitTestFramework.brs` in [lib/brs/](../master/lib/brs/UnitTestFramework.brs) to your `src/test/source/` folder, so it loads at startup for when testing. +#### 1. Define you custom tests running js script: -> Note that the original `UnitTestFramework.brs` can be found [here](https://github.com/rokudev/unit-testing-framework) +Example: `/scripts/runTests.js` +``` +const child_process = require('child_process') + +const args = process.argv.slice(2) +const ip = args[0] // the roku device ip given as an argument by ukor + +// main function call +main() + +function main() { + child_process.execSync(`npx rooibosC -c ./.rooibosrc.json`) + child_process.execSync(`curl -d '' "http://${ip}:8060/launch/dev?RunTests=true"`) +} +``` -Next, dd the following snippet in your startup function, after `screen.show()` but before the event loop +#### 2. Add the path of your script to ukor.properties.yaml -```brightscript -if params.RunTests = "true" - runner = TestRunner() - if params.host <> invalid - runner.logger.SetServer(params.host, params.port) - else - runner.logger.SetServerURL(params.url) - end if - ' other setup if needed - runner.run() -end if ``` +... +buildDir: build +sourceDir: src +preBuild: scripts/preBuild.js + +runUnitTestsScript: scripts/runTests.js +``` + You should now be able to execute your test suite using the `test` command. @@ -213,11 +225,14 @@ ukor test [flavor] [roku] ### What's happening? -Basically, we modified the rokudev `UnitTestFramework.brs` file to make a `JSON` of test results, and then `POST` that to the specified server. `ukor test [flavor]` builds and deploys the specified flavor with the `test` src folder, and then restarts the channel with parameters to run tests and point the results to the client machine. `ukor` will log the results, and also output results in `xml` and `junit` format to `.out/tests/ukorTests.[xml|junit]`. +After running the test command, ukor will deploy the channel on given device. +Once the channel is deployed, ukor starts to run the `runTests.js` script with the device `ip` as argument. -notes: -- Ukor now copies `UnitTestFramework.brs` with `ukor init`! -- `UnitTestFramework.brs` is now up to date with the rokudev repo! +You can get `ip` inside of `runTests.js` by: +``` +const args = process.argv.slice(2) +const ip = args[0] // the roku device ip given as an argument by ukor +``` # Contributing to Ukor diff --git a/lib/commands/make.js b/lib/commands/make.js index 252d687..445026a 100644 --- a/lib/commands/make.js +++ b/lib/commands/make.js @@ -7,7 +7,6 @@ const log = require('../utils/log') const archiver = require('archiver') const utils = require('../utils/utils') const inserter = require('../utils/inserter') -const childProcess = require('child_process') const properties = require('../utils/properties') const temp = '.ukor' From e830b917434dfb4b2c423f496faf8df5a81dcc89 Mon Sep 17 00:00:00 2001 From: Cazacu Tudor Date: Tue, 30 Jul 2019 17:26:24 +0300 Subject: [PATCH 3/5] [Issue 64] Update sample channel --- lib/sample/src/main/components/AppScene.brs | 4 +- lib/sample/src/main/components/AppScene.xml | 12 +- lib/sample/src/main/manifest | 8 + lib/sample/src/main/source/main.brs | 37 +- lib/sample/src/scripts/preBuild.js | 18 + lib/sample/src/scripts/runTests.js | 11 + .../src/test/source/UnitTestFramework.brs | 2145 ----------------- lib/sample/ukor.properties.yaml | 12 + 8 files changed, 78 insertions(+), 2169 deletions(-) create mode 100644 lib/sample/src/scripts/preBuild.js create mode 100644 lib/sample/src/scripts/runTests.js delete mode 100644 lib/sample/src/test/source/UnitTestFramework.brs create mode 100644 lib/sample/ukor.properties.yaml diff --git a/lib/sample/src/main/components/AppScene.brs b/lib/sample/src/main/components/AppScene.brs index b07b4f3..fdbd0f7 100644 --- a/lib/sample/src/main/components/AppScene.brs +++ b/lib/sample/src/main/components/AppScene.brs @@ -1,3 +1,3 @@ sub init() - -end sub \ No newline at end of file + ' your code here +end sub diff --git a/lib/sample/src/main/components/AppScene.xml b/lib/sample/src/main/components/AppScene.xml index cd21910..9c4b581 100644 --- a/lib/sample/src/main/components/AppScene.xml +++ b/lib/sample/src/main/components/AppScene.xml @@ -1,9 +1,3 @@ - - - - - -