123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- #!/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}
|