fbp-spec
Version:
Data-driven FBP component/graph testing tool
189 lines (155 loc) • 6.02 kB
text/coffeescript
common = require './common'
protocol = require './protocol'
testsuite = require './testsuite'
expectation = require './expectation'
fbp = require 'fbp'
fbpClient = require 'fbp-protocol-client'
debug = require('debug')('fbp-spec:runner')
debugReceivedMessages = (client) ->
client.on 'graph', ({command, payload}) ->
debug 'recv graph', command, payload
client.on 'network', ({command, payload}) ->
debug 'recv network', command, payload
client.on 'runtime', ({command, payload}) ->
debug 'recv runtime', command, payload
client.on 'component', ({command, payload}) ->
debug 'recv component', command, payload
client.on 'execution', (status) ->
debug 'recv execution', status
class Runner
constructor: () ->
if .protocol? and .address?
# is a runtime definition
Transport = fbpClient.getTransport .protocol
= new Transport
= null
# TODO: check the runtime capabilities before continuing
# TODO: have a timeout
connect: (callback) ->
debug 'connect'
onStatus = (status) =>
return if not status.online # ignore, might get false before getting a true
.removeListener 'status', onStatus
debug 'connected', status
return callback null
.on 'status', onStatus
.connect()
.on 'network', ({command, payload}) ->
console.log payload.message if command is 'output' and payload.message
debugReceivedMessages
disconnect: (callback) ->
debug 'disconnect'
onStatus = (status) =>
err = if not status.online then null else new Error 'Runtime online after disconnect()'
.removeListener 'status', onStatus
debug 'disconnected', err
return callback err
.on 'status', onStatus
.disconnect()
setupSuite: (suite, callback) ->
debug 'setup suite', "\"#{suite.name}\""
if suite.fixture.type == 'json'
graph = JSON.parse suite.fixture.data
else if suite.fixture.type == 'fbp'
graph = fbp.parse suite.fixture.data
graph.properties = {} if not graph.properties
else
graph = null
protocol.sendGraph , graph, (err, graphId) =>
= graphId
return callback err if err
protocol.startNetwork , graphId, (err) =>
return callback err
teardownSuite: (suite, callback) ->
debug 'teardown suite', "\"#{suite.name}\""
# FIXME: also remove the graph. Ideally using a 'destroy' message in FBP protocol
protocol.stopNetwork , , (err) =>
return callback err
runTest: (testcase, callback) ->
debug 'runtest', "\"#{testcase.name}\""
received = {}
onReceived = (port, data) =>
received[port] = data
nExpected = Object.keys(testcase.expect).length
if Object.keys(received).length == nExpected
.removeListener 'runtime', checkPacket
return callback null, received
checkPacket = (msg) =>
d = msg.payload
# FIXME: also check # and d.graph ==
if msg.command == 'packet' and d.event == 'data'
onReceived d.port, d.payload
else if msg.command == 'packet' and ['begingroup', 'endgroup', 'connect', 'disconnect'].indexOf(d.event) != -1
# ignored
else
debug 'unknown runtime message', msg
.on 'runtime', checkPacket
# send input packets
protocol.sendPackets , , testcase.inputs, (err) =>
return callback err if err
runTestAndCheck = (runner, testcase, callback) ->
runner.runTest testcase, (err, actual) ->
error = null
if testcase.skip
results =
passed: false
# TODO: pass some skipped state? its indirectly in .skip though
else
try
expectation.expect testcase, actual
catch e
error = e
# FIXME: only catch actual AssertionErrors
results =
passed: not error
error: error?.message
return callback err, results
runSuite = (runner, suite, runTest, callback) ->
runner.setupSuite suite, (err) ->
debug 'setup suite', err
return callback err, suite if err
common.asyncSeries suite.cases, runTest, (err) ->
debug 'testrun complete', err
runner.teardownSuite suite, (err) ->
debug 'teardown suite', err
return callback err, suite
exports.getComponentSuites = (runner, callback) ->
protocol.getCapabilities runner.client, (err, caps) ->
return callback err if err
return callback null, [] unless 'component:getsource' in caps
protocol.getComponentTests runner.client, (err, tests) ->
return callback err if err
suites = loadComponentSuites tests
debug 'get component suites', tests.length, suites.length
return callback null, suites
loadComponentSuites = (componentTests) ->
suites = []
for name, tests of componentTests
try
ss = testsuite.loadYAML tests
suites = suites.concat ss
catch e
# ignore, could be non fbp-spec test
# TODO: include tests type in FBP protocol, so we know whether this is error or legit
continue
return suites
# will update each of the testcases in
# with .passed and .error states as tests are ran
runAll = (runner, suites, updateCallback, doneCallback) ->
runTest = (testcase, callback) ->
done = (error) ->
updateCallback suites
callback error
runTestAndCheck runner, testcase, (err, results) ->
for key, val of results
testcase[key] = val
debug 'ran test', '"testcase.name"', testcase.passed, err
return done null # ignore error to not bail out early
runOneSuite = (suite, cb) ->
runSuite runner, suite, runTest, cb
debug 'running suites', (s.name for s in suites)
common.asyncSeries suites, runOneSuite, (err) ->
return doneCallback err
exports.Runner = Runner
exports.runAll = runAll
exports.runTestAndCheck = runTestAndCheck