nodemcu-firmware/tests/tap-driver.expect

267 lines
8.0 KiB
Plaintext
Raw Permalink Normal View History

#!/usr/bin/env expect
# Push a file to the device, run it, and watch the tests run
#
# A typical invocation looks like:
# TCLLIBPATH=./expectnmcu ./tap-driver.expect -serial /dev/ttyUSB3 ./mispec.lua ./mispec_file.lua
#
# For debugging the driver itself, it may be useful to invoke expect with -d,
# which will give a great deal of diagnostic information about the expect state
# machine's internals:
#
# TCLLIBPATH=./expectnmcu expect -d ./tap-driver.expect ...
#
# The -debug option will turn on some additional reporting from this driver program, as well.
package require expectnmcu::core
package require expectnmcu::xfer
package require cmdline
set cmd_parameters {
{ serial.arg "/dev/ttyUSB0" "Set the serial interface name" }
{ tpfx.arg "TAP: " "Set the expected TAP test prefix" }
{ lfs.arg "" "Flash a file to LFS" }
{ noxfer "Do not send files, just run script" }
{ runfunc "Last argument is function, not file" }
{ notests "Don't run tests, just xfer files" }
{ nontestshim "Don't shim NTest when testing" }
{ debug "Enable debugging reporting" }
}
set cmd_usage "- A NodeMCU Lua-based-test runner"
if {[catch {array set cmdopts [cmdline::getoptions ::argv $cmd_parameters $cmd_usage]}]} {
send_user [cmdline::usage $cmd_parameters $cmd_usage]
send_user "\n Additional arguments should be files be transferred\n"
send_user " The last file transferred will be run with `dofile`\n"
exit 0
}
if { ${cmdopts(noxfer)} } {
if { [ llength ${::argv} ] > 1 } {
send_user "No point in more than one argument if noxfer given\n"
exit 1
}
} {
set xfers ${::argv}
if { ${cmdopts(runfunc)} } {
# Last argument is command, not file to xfer
set xfers [lreplace xfers end end]
}
foreach arg ${xfers} {
if { ! [file exists ${arg}] } {
send_user "File ${arg} does not exist\n"
exit 1
}
}
}
if { ${cmdopts(lfs)} ne "" } {
if { ! [file exists ${cmdopts(lfs)}] } {
send_user "LFS file does not exist\n"
exit 1
}
}
proc sus { what } { send_user "\n===> ${what} <===\n" }
proc sui { what } { send_user "\n---> ${what} <---\n" }
proc sud { what } {
upvar 1 cmdopts cmdopts
if { ${cmdopts(debug)} } { send_user "\n~~~> ${what} <~~~\n" }
}
set victim [::expectnmcu::core::connect ${cmdopts(serial)}]
sus "Machine has booted"
if { ${cmdopts(lfs)} ne "" } {
::expectnmcu::xfer::sendfile ${victim} ${cmdopts(lfs)} "tap-driver.lfs"
send -i ${victim} "=node.LFS.reload(\"tap-driver.lfs\")\n"
::expectnmcu::core::waitboot ${victim}
}
if { ! ${cmdopts(noxfer)} } {
foreach arg ${xfers} {
::expectnmcu::xfer::sendfile ${victim} ${arg} [file tail ${arg}]
}
}
set tfn [file tail [lindex ${::argv} end ] ]
if { ${cmdopts(notests)} || ${tfn} eq "" } {
sus "No tests requested, and so operations are completed"
exit 0
}
sus "Files transferred; running ${tfn}"
if { ! ${cmdopts(nontestshim)} } {
::expectnmcu::core::send_exp_prompt_c ${victim} "function ntshim(...)"
::expectnmcu::core::send_exp_prompt_c ${victim} " local test = (require \"NTest\")(...)"
::expectnmcu::core::send_exp_prompt_c ${victim} " test.outputhandler = require\"NTestTapOut\""
::expectnmcu::core::send_exp_prompt_c ${victim} " return test"
::expectnmcu::core::send_exp_prompt ${victim} "end"
} else {
sui "Not shimming NTest output; test must report its own TAP messages"
}
# ntshim may be nil at this point if -nontestshim was given; that's fine
if { ${cmdopts(runfunc)} } {
send -i ${victim} "[ lindex ${::argv} end ](ntshim)\n"
expect -i ${victim} -re "\\(ntshim\\)\[\r\n\]+" { }
} else {
send -i ${victim} "assert(loadfile(\"${tfn}\"))(ntshim)\n"
expect -i ${victim} -re "assert\\(loadfile\\(\"${tfn}\"\\)\\)\\(ntshim\\)\[\r\n\]+" { }
}
set tpfx ${cmdopts(tpfx)}
set toeol "\[^\n\]*(?=\n)"
# Wait for the test to start and tell us how many
# success lines we should expect
set ntests 0
set timeout 10
expect {
-i ${victim} -re "${tpfx}1\\.\\.(\\d+)(?=\r?\n)" {
global ntests
set ntests $expect_out(1,string)
}
-i ${victim} -re "${tpfx}Bail out!${toeol}" {
sus "Bail out before start"
exit 2
}
-i ${victim} -re ${::expectnmcu::core::panicre} {
sus "Panic!"
exit 2
}
# A prefixed line other than a plan (1..N) or bailout means we've not got
# a plan. Leave ${ntests} at 0 and proceed to run the protocol.
-i ${victim} -notransfer -re "${tpfx}${toeol}" { }
# -i ${victim} -ex "\n> " {
# sus "Prompt before start!"
# exit 2
# }
# Consume other outputs and discard as if they were comments
# This must come as the last pattern that looks at input
-i ${victim} -re "(?p).${toeol}" { exp_continue }
timeout {
send_user "Failure: time out getting started\n"
exit 2
}
}
if { ${ntests} == 0 } {
sus "System did not report plan; will look for summary at end"
} else {
sus "Expecting ${ntests} test results"
}
set timeout 60
set exitwith 0
set failures 0
for {set this 1} {${ntests} == 0 || ${this} <= ${ntests}} {incr this} {
expect {
-i ${victim} -re "${tpfx}#${toeol}" {
sud "Harness got comment: ${expect_out(buffer)}"
exp_continue
}
-i ${victim} -re "${tpfx}ok (\\d+)\\D${toeol}" {
sud "Harness acknowledge OK! ${this} ${expect_out(1,string)}"
set tid ${expect_out(1,string)}
if { ${tid} != "" && ${tid} != ${this} } {
sui "WARNING: Test reporting misaligned at ${this} (got ${tid})"
}
}
-i ${victim} -re "${tpfx}ok #${toeol}" {
sud "Harness acknowledge anonymous ok! ${this}"
}
-i ${victim} -re "${tpfx}not ok (\\d+)\\D${toeol}" {
sud "Failure in simulation after ${this} ${expect_out(1,string)}"
set tid ${expect_out(1,string)}
if { ${tid} != "" && ${tid} != ${this} } {
sui "WARNING: Test reporting misaligned at ${this}"
}
set exitwith [expr max(${exitwith},1)]
incr failures
}
-i ${victim} -re "${tpfx}not ok #${toeol}" {
sud "Failure (anonymous) in simulation after ${this}"
set exitwith [expr max(${exitwith},1)]
incr failures
}
-i ${victim} -re "${tpfx}Bail out!${toeol}" {
sus "Bail out after ${this} tests"
exit 2
}
-i ${victim} -re "${tpfx}POST 1\\.\\.(\\d+)(?=\r?\n)" {
# A post-factual plan; this must be the end of testing
global ntests
set ntests ${expect_out(1,string)}
if { ${ntests} != ${this} } {
sus "Postfix plan claimed ${ntests} but we saw ${this}"
set exitwith [expr max(${exitwith},2)]
incr failures
}
# break out of for loop
set this ${ntests}
}
-i ${victim} -re "${tpfx}${toeol}" {
sus "TAP line not understood!"
exit 2
}
# -i ${victim} -ex ${::expectnmcu::core::promptstr} {
# sus "Prompt while running tests!"
# exit 2
# }
-i ${victim} -re ${::expectnmcu::core::panicre} {
sus "Panic!"
exit 2
}
# Consume other outputs and discard as if they were comments
# This must come as the last pattern that looks at input
-re "(?p).${toeol}" { exp_continue }
timeout {
send_user "Failure: time out\n"
exit 2
}
}
}
# We think we're done running tests; send a final command for synchronization
send -i ${victim} "print(\"f\",\"i\",\"n\")\n"
expect -i ${victim} -re "print\\(\"f\",\"i\",\"n\"\\)\[\r\n\]+" { }
expect {
-i ${victim} -ex "f\ti\tn" { }
-i ${victim} -re "${tpfx}#${toeol}" {
sud "Harness got comment: ${expect_out(buffer)}"
exp_continue
}
-i ${victim} -re "${tpfx}Bail out!${toeol}" {
sus "Bail out after all tests finished"
exit 2
}
-i ${victim} -re "${tpfx}${toeol}" {
sus "Unexpected TAP output after tests finished"
exit 2
}
-i ${victim} -re ${::expectnmcu::core::panicre} {
sus "Panic!"
exit 2
}
-re "(?p).${toeol}" { exp_continue }
timeout {
send_user "Failure: time out\n"
exit 2
}
}
if { ${exitwith} == 0 } {
sus "All tests reported in OK"
} else {
sus "${failures} TEST FAILURES; REVIEW LOGS"
}
exit ${exitwith}