From 5fc1a362e003cef5d294360a0533ec1a56b23be7 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Tue, 28 Dec 2021 01:58:58 +0000 Subject: [PATCH 01/11] tests: tests/README basic module requirements --- tests/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/README.md b/tests/README.md index 3cebbb1ab8..b345d96c81 100644 --- a/tests/README.md +++ b/tests/README.md @@ -19,6 +19,18 @@ NodeMCU DUTs, but there is reason to want to orchestrate DUTs and the environment using the host. Files matching the glob `NTest_*.lua` are intended for on-DUT execution. +## Modules Required + +Basic test harness functionality depends on having at least the following C +modules available: + +* `crypto` +* `encoder` +* `file` + +Of course, most of the test functionality itself will require additional +modules. + ## Manual Test Invocation At the moment, the testing regime and host-based orchestration is still in From 14f06e8c9891a57fbd08f8af2fb1de58664f10fd Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Thu, 24 Dec 2020 15:04:53 +0000 Subject: [PATCH 02/11] tests: introduce NTestEnv utility functions --- tests/README.md | 11 ++++++ tests/conf/dut0/testenv.conf | 3 ++ tests/conf/dut1/testenv.conf | 3 ++ tests/utils/NTestEnv.lua | 72 ++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 tests/conf/dut0/testenv.conf create mode 100644 tests/conf/dut1/testenv.conf create mode 100644 tests/utils/NTestEnv.lua diff --git a/tests/README.md b/tests/README.md index b345d96c81..5fea6c9739 100644 --- a/tests/README.md +++ b/tests/README.md @@ -226,4 +226,15 @@ GPIO 15 |Secondary UART TX; DUT 0 GPIO 13, I/O exp B 6 via 4K7 Also used as HS GPIO 16 |I/O expander B 5 via 4K7 resistor, for deep-sleep tests ADC 0 | +# Probing the Test Environment from Tests +The `NTestEnv` module provides convenient functions for preflight checks +and limited adaptation of test programs. + +## Test Configuration File + +Our tests expect a `testenv.conf` in SPIFFS to provide parameters to +some tests. This file is a JSON map with the following keys: + +- "DUT". Its value is either 0 or 1 indicating which DUT is running the + given test. diff --git a/tests/conf/dut0/testenv.conf b/tests/conf/dut0/testenv.conf new file mode 100644 index 0000000000..1ae716326d --- /dev/null +++ b/tests/conf/dut0/testenv.conf @@ -0,0 +1,3 @@ +{ + "DUT": 0 +} diff --git a/tests/conf/dut1/testenv.conf b/tests/conf/dut1/testenv.conf new file mode 100644 index 0000000000..84734eb0a0 --- /dev/null +++ b/tests/conf/dut1/testenv.conf @@ -0,0 +1,3 @@ +{ + "DUT": 1 +} diff --git a/tests/utils/NTestEnv.lua b/tests/utils/NTestEnv.lua new file mode 100644 index 0000000000..97a516010c --- /dev/null +++ b/tests/utils/NTestEnv.lua @@ -0,0 +1,72 @@ +-- A series of convenient utility functions for use with NTest on NodeMCU +-- devices under test (DUTs). + +local NTE = {} + +-- Determine if the firmware has been built with a given module +function NTE.hasC(m) + -- this isn't great, but it's what we've got + local mstr = node.info('build_config').modules + return mstr:match("^"..m..",") -- first module in the list + or mstr:match(","..m.."$") -- last module in the list + or mstr:match(","..m..",") -- somewhere else +end + +-- Determine if a given Lua module is available for require-ing. +function NTE.hasL(m) + for _, l in ipairs(package.loaders) do + if type(l(m)) == "function" then return true end + end + return false +end + +-- Look up one (or more) feature keys in our configuration file, +-- returning the associated value (or a map from keys to values). +function NTE.getFeat(...) + -- Stream through our configuration file and extract the features attested + -- + -- We expect the configuration file to attest features as keys in a dictionary + -- so that they can be efficiently probed here but also so that we can + -- parameterize features. + -- + -- { + -- "feat1" : ... , + -- "feat2" : ... + -- } + local reqFeats = { ... } + local decoder = sjson.decoder({ + metatable = { + checkpath = function(_, p) + if #p > 1 then return true end -- something we're keeping + if #p == 0 then return true end -- root table + local thisFeat = p[1] + assert (type(thisFeat) == "string") + for _, v in ipairs(reqFeats) do + if v == thisFeat then return true end -- requested feature + end + return false + end + } + }) + local cfgf = file.open("testenv.conf", "r") + + if cfgf == nil then return {} end -- no features if no config file + + local cstr + repeat + cstr = cfgf:read(); decoder:write(cstr or "") + until (not cstr or #cstr == 0) + cfgf:close() + local givenFeats = decoder:result() + assert (type(givenFeats) == "table", "Malformed configuration file") + + local res = {} + for _, v in ipairs(reqFeats) do + res[v] = givenFeats[v] or error("Missing required feature " .. v) + end + + if #reqFeats == 1 then return res[reqFeats[1]] end + return res +end + +return NTE From 2b453611e60f769259201751fedecfa20e39ea4e Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Thu, 30 Dec 2021 07:53:02 +0000 Subject: [PATCH 03/11] tests: adc require running on DUT 0 using NTestEnv --- tests/NTest_adc_env.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/NTest_adc_env.lua b/tests/NTest_adc_env.lua index 6a3267f0d7..c01f49a4c2 100644 --- a/tests/NTest_adc_env.lua +++ b/tests/NTest_adc_env.lua @@ -4,21 +4,23 @@ local N = ... N = (N or require "NTest")("adc-env") --- TODO: Preflight test that we are in the correct environment with an I2C --- expander in the right place with the right connections. +local NTE = require "NTestEnv" -- TODO: Use the mcp23017 module in the main tree rather than hand-coding -- the commands N.test('setup', function() - -- Configure the ADC + -- Ensure that we're on DUT 0 + assert(NTE.getFeat('DUT') == 0, "Not on DUT 0") + + -- Configure the ADC (this implicitly checks for having the ADC module) if adc.force_init_mode(adc.INIT_ADC) then node.restart() error "Must reboot to get to ADC mode" end - -- Configure the I2C bus + -- Configure the I2C bus (again, implicitly testing...) i2c.setup(0, 2, 1, i2c.FAST) -- Set the IO expander port B to channels 0 and 1 as outputs From 2f0cbfcb32a35a0bde4e6854225e99eec8e6de51 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Wed, 17 Jun 2020 18:15:06 +0100 Subject: [PATCH 04/11] tests: Add script to make LFS for running tests The NTest tests take a lot of RAM if we just run them there. So instead, push them into LFS to give us some more wiggle room. --- tests/preflight-lfs.sh | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100755 tests/preflight-lfs.sh diff --git a/tests/preflight-lfs.sh b/tests/preflight-lfs.sh new file mode 100755 index 0000000000..51cdee9ff2 --- /dev/null +++ b/tests/preflight-lfs.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env zsh + +set -e -u + +SELFDIR=$(dirname $(readlink -f $0)) + +LUAFILES=( + # Infrastructure + ${SELFDIR}/../lua_examples/lfs/_init.lua + ${SELFDIR}/../lua_examples/lfs/dummy_strings.lua + ${SELFDIR}/../lua_examples/pipeutils.lua # accelerates TCL xfer + + ${SELFDIR}/NTest/NTest.lua + ${SELFDIR}/utils/NTestEnv.lua + ${SELFDIR}/utils/NTestTapOut.lua + + # Lua modules exercised by test programs + + # Test programs + ${SELFDIR}/NTest_*.lua +) + +if [ -e ${SELFDIR}/../luac.cross.int ]; then + echo "Found integer Lua cross compiler..." + ${SELFDIR}/../luac.cross.int -f -o ${NODEMCU_TESTTMP}/tmp-lfs-int.img ${LUAFILES[@]} + echo " ... and generated ${NODEMCU_TESTTMP}/tmp-lfs-int.img" +fi + +if [ -e ${SELFDIR}/../luac.cross ]; then + echo "Found float Lua cross compiler..." + ${SELFDIR}/../luac.cross -f -o ${NODEMCU_TESTTMP}/tmp-lfs-float.img ${LUAFILES[@]} + echo " ... and generated ${NODEMCU_TESTTMP}/tmp-lfs-float.img" +fi From 9e5b3528369741c434eab3b39e3c84e439b2dbd1 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Wed, 17 Jun 2020 18:16:30 +0100 Subject: [PATCH 05/11] tests: preflight script to grab host IP address --- tests/preflight-host-ip.expect | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tests/preflight-host-ip.expect diff --git a/tests/preflight-host-ip.expect b/tests/preflight-host-ip.expect new file mode 100644 index 0000000000..96e5655e4c --- /dev/null +++ b/tests/preflight-host-ip.expect @@ -0,0 +1,31 @@ +#!/usr/bin/env expect + +log_file -noappend -a "/dev/fd/2" +log_user 0 + +# Boot a NodeMCU device, connect it to the network, and try to extract the +# host's IP address to use when listening for this endpoint. + +package require expectnmcu::core +package require expectnmcu::net + +package require cmdline +set cmd_parameters { + { serial.arg "/dev/ttyUSB0" "Set the serial interface name" } + { wifi.arg "" "Command to run to bring up the network" } +} +set cmd_usage "- Figure out the host IP to use for a NodeMCU DUT" +if {[catch {array set cmdopts [cmdline::getoptions ::argv $cmd_parameters $cmd_usage]}]} { + send_user [cmdline::usage $cmd_parameters $cmd_usage] + exit 0 +} + + +set victim [::expectnmcu::core::connect ${cmdopts(serial)}] +if {0 < [string length ${cmdopts(wifi)}]} { + ::expectnmcu::core::send_exp_prompt ${victim} ${cmdopts(wifi)} +} +set myip [::expectnmcu::net::guessmyip [::expectnmcu::net::waitwifista ${victim}]] + +log_file +send_user ${myip} From 42e67f4fa286f63456cba3cc4f0a08abc41a76ec Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Thu, 31 Dec 2020 02:22:02 +0000 Subject: [PATCH 06/11] tests: preflight-dut scripting Will use this to set RCRs and such as part of the test environment run --- tests/preflight-dut.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/preflight-dut.lua diff --git a/tests/preflight-dut.lua b/tests/preflight-dut.lua new file mode 100644 index 0000000000..2c079fb6d5 --- /dev/null +++ b/tests/preflight-dut.lua @@ -0,0 +1,10 @@ +print("\nTAP: 1..1\n") + +-- On-DUT configuration of RCRs. +-- XXX NOT YET +-- node.startup{command = '!_init'} + +-- Do this in the future so the prompt from dofile gets emitted +tmr.create():alarm(1, tmr.ALARM_SINGLE, function() + print("\nTAP: ok 1 # preflight-dut completed\n") +end) From 9e4c0d5214d7919937645e25404f235c263f295f Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Thu, 30 Dec 2021 04:09:46 +0000 Subject: [PATCH 07/11] tests: Slam docs together Notably, move updated wiring description to README and remove it from HardwareTestHarness --- tests/HardwareTestHarness.md | 104 ++++++----------------------------- tests/README.md | 87 ++++++++++++++++------------- 2 files changed, 67 insertions(+), 124 deletions(-) diff --git a/tests/HardwareTestHarness.md b/tests/HardwareTestHarness.md index 64bdb0cf9f..96f505113c 100644 --- a/tests/HardwareTestHarness.md +++ b/tests/HardwareTestHarness.md @@ -48,43 +48,13 @@ Peripherals ### I2C Bus -There is an I2C bus hanging off DUT 0. Attached hardware is used both as -tests of modules directly and also to facilitate testing other modules -(e.g., gpio). - Most of the positions on the board are connected to the DUT1 I2C bus. -#### MCP23017: I/O Expander - -At address 0x20. An 16-bit tristate GPIO expander, this chip is used to -test I2C, GPIO, and ADC functionality. This chip's interconnections are -as follows: - -MPC23017 | Purpose ----------|-------------------------------------------------------------- -/RESET |DUT0 reset. This resets the chip whenever the host computer resets DUT 0 over its serial link (using DTR/RTS). -B 0 |4K7 resistor to DUT 0 ADC. -B 1 |2K2 resistor to DUT 0 ADC. -B 2 |Direct to DUT1 RST -B 3 |Direct to DUT1 D3 -B 4 |When low, connects the alternate UART pins on DUT0 to RX,TX on DUT1 -B 5 |DUT1 GPIO16/WAKE via 4K7 resitor -B 6 |DUT0 GPIO13 via 4K4 resistor and DUT1 GPIO15 via 4K7 resistor (also feeds in the primary TX from DUT1 when enabled by B4) -B 7 |DUT0 GPIO15 via 4K7 resistor and DUT1 GPIO13 via 4K7 resistor (also feeds the primary RX on DUT1 when enabled by B4) - -Notes: - -- DUT 0's ADC pin is connected via a 2K2 reistor to this chip's port - B, pin 1 and via a 4K7 resistor to port B, pin 0. This gives us the - ability to produce approximately 0 (both pins low), 1.1 (pin 0 high, - pin 1 low), 2.2 (pin 1 high, pin 0 low), and 3.3V (both pins high) - on the ADC pin. -- Port B pins 6 and 7 sit on the UART cross-wiring between DUT 0 and - DUT 1. The 23017 will be tristated for inter-DUT UART tests, but - these -- All of port A, remain available for expansion and are routed to the breadboard area. - -#### WS2812s +#### MCP23017: I/O Expander (DUT 0, 0x20) + +As per the [Generic Framework](README.md). + +### WS2812s There are three WS2812s connected on DUT1/D4. The last Ws2812 is positioned so that a TCS34725 module can be mounted upside down over it to read out the color of the WS2812. That device is connected to @@ -94,22 +64,34 @@ LED is connected to the INT pin and so you can disable the LED under software co #### Oled Displays Each of the D1 Minis is connected to a position for a 128x64 OLED display, again on the primary I2C bus. +Note: Not yet used in the test harness. #### Servo On DUT1 pin D4/GPIO 2 there is a connection to a position for a small servo. The servo is powered by the 5V voltage rail. +Note: Not yet used in the test harness. + #### DHTxx On DUT1 pin D6/GPIO 12 there is a connection to a position for a DHTxx device. The silk screen indicates the orientation of the device. +Note: Not yet used in the test harness. + #### DS18B20 There are two positions for DS18B20s -- one with the VCC pin connected and one without. The data pin is connected to DUT1 pin D5/GPIO 14. +Note: Not yet used in the test harness. + +Note: It would make sense to augment the 1-Wire testing facility to +include bus-drive power, perhaps via the MCP23017, especially if we ever +augment the driver as per +https://github.com/nodemcu/nodemcu-firmware/issues/1995 + #### I2C devices with VCC/GND/SCL/SDA pin order There are three positions for I2C devices which have the pins in the VCC/GND/SCL/SDA order. These @@ -121,61 +103,9 @@ There are three positions for I2C devices with other pin orders. Each of these p to a crossbar switch and so four blobs of solder can configure each of these headers into any desired pin order. As far as I can tell, most of the cheap modules use the VCC/GND/SCL/SDA order. - Breadboard Area =============== All the pins on each D1 Mini and the A port of the MCP23017 are brought out to a breadboard area. This can be used to solder components and/or wires, or even a header could be soldered on to transfer all the signals to a conventional breadboard. - - -ESP8266 Device 0 Connections ----------------------------- - -ESP | Usage -----------|---------------------------------------------------------- -D3/GPIO 0 |Used to enter programming mode; otherwise unused in test environment. -TX/GPIO 1 |Primary UART transmit; reserved for host communication -D4/GPIO 2 |[reserved for 1-Wire] [+ reserved for 23017 INT[AB] connections] -RX/GPIO 3 |Primary UART recieve; reserved for host communication -D2/GPIO 4 |I2C SDA. Connected to MCP23017, Oled display and the TCS34725 if present. -D1/GPIO 5 |I2C SCL -GPIO 6 |[Reserved for on-chip flash] -GPIO 7 |[Reserved for on-chip flash] -GPIO 8 |[Reserved for on-chip flash] -GPIO 9 |[Reserved for on-chip flash] -GPIO 10 |[Reserved for on-chip flash] -GPIO 11 |[Reserved for on-chip flash] -D6/GPIO 12 | -D7/GPIO 13 |Secondary UART RX; DUT 1 GPIO 15, I/O expander B 6 -D5/GPIO 14 | -D8/GPIO 15 |Secondary UART TX; DUT 1 GPIO 13, I/O expander B 7 -D0/GPIO 16 | -A0/ADC 0 |Resistor divider with I/O expander - -ESP8266 Device 1 Connections ----------------------------- - -ESP | Usage -----------|---------------------------------------------------------- -D3/GPIO 0 |Used to enter programming mode; otherwise unused in test environment. -TX/GPIO 1 |Primary UART transmit; reserved for host communication -D4/GPIO 2 |Connected to chain of 3 WS2812s. Also connected to the servo position. -RX/GPIO 3 |Primary UART recieve; reserved for host communication -D2/GPIO 4 |I2C SDA. Connected to all the other I2C positions on the board -D1/GPIO 5 |I2C SCL -GPIO 6 |[Reserved for on-chip flash] -GPIO 7 |[Reserved for on-chip flash] -GPIO 8 |[Reserved for on-chip flash] -GPIO 9 |[Reserved for on-chip flash] -GPIO 10 |[Reserved for on-chip flash] -GPIO 11 |[Reserved for on-chip flash] -D6/GPIO 12 |Connected to data pin for DHTxx -D7/GPIO 13 |Secondary UART RX; DUT 0 GPIO 15, I/O exp B 7 via 4K7 Also used as HSPI MOSI for SPI tests -D5/GPIO 14 |Connected to data pin for DS18B20s. -D8/GPIO 15 |Secondary UART TX; DUT 0 GPIO 13, I/O exp B 6 via 4K7 Also used as HSPI /CS for SPI tests -D0/GPIO 16 |I/O expander B 5 via 4K7 resistor, for deep-sleep tests -A0/ADC 0 | - - diff --git a/tests/README.md b/tests/README.md index 5fea6c9739..6477a9aa86 100644 --- a/tests/README.md +++ b/tests/README.md @@ -136,30 +136,34 @@ devices, as found on almost all ESP8266 boards with USB to UART adapters, but the host does not necessarily need to use USB to connect, so long as TXD, RXD, DTR, and RTS are wired across. -A particular implementation of this can be found at [Test Harness](HardwareTestHarness.md). +A particular implementation of this, with some additional goodies, can +be found at [Test Harness Board](HardwareTestHarness.md). ## Peripherals ### I2C Bus -There is an I2C bus hanging off DUT 0. Attached hardware is used both as -tests of modules directly and also to facilitate testing other modules -(e.g., gpio). +There is an I2C bus hanging off each DUT. Attached hardware is used both +as tests of modules directly and also to facilitate testing other +modules (e.g., gpio). -#### MCP23017: I/O Expander +#### MCP23017: I/O Expander (DUT 0, 0x20) -At address 0x20. An 16-bit tristate GPIO expander, this chip is used to -test I2C, GPIO, and ADC functionality. This chip's interconnections are -as follows: +At address 0x20 off DUT 0. An 16-bit tristate GPIO expander, this chip +is used to test I2C, GPIO, and ADC functionality. This chip's +interconnections are as follows: MPC23017 | Purpose ---------|-------------------------------------------------------------- /RESET |DUT0 reset. This resets the chip whenever the host computer resets DUT 0 over its serial link (using DTR/RTS). B 0 |4K7 resistor to DUT 0 ADC. B 1 |2K2 resistor to DUT 0 ADC. +B 2 |Direct to DUT1 RST +B 3 |Direct to DUT1 D3 +B 4 |When low, connects the alternate UART pins on DUT0 to RX,TX on DUT1 (NOTE: not yet used in the test harness) B 5 |DUT1 GPIO16/WAKE via 4K7 resitor -B 6 |DUT0 GPIO13 via 4K7 resistor and DUT1 GPIO15 via 4K7 resistor -B 7 |DUT0 GPIO15 via 4K7 resistor and DUT1 GPIO13 via 4K7 resistor +B 6 |DUT0 GPIO13 via 4K4 resistor and DUT1 GPIO15 via 4K7 resistor (also feeds in the primary TX from DUT1 when enabled by B4) +B 7 |DUT0 GPIO15 via 4K7 resistor and DUT1 GPIO13 via 4K7 resistor (also feeds the primary RX on DUT1 when enabled by B4) Notes: @@ -168,39 +172,48 @@ Notes: ability to produce approximately 0 (both pins low), 1.1 (pin 0 high, pin 1 low), 2.2 (pin 1 high, pin 0 low), and 3.3V (both pins high) on the ADC pin. + - Port B pins 6 and 7 sit on the UART cross-wiring between DUT 0 and DUT 1. The 23017 will be tristated for inter-DUT UART tests, but - these -- Port B pins 2, 3, and 4, as well as all of port A, remain available - for expansion. -- The interrupt pins are not yet routed, but could be. We reserve DUT - 0 GPIO 2 for this purpose with the understanding that the 23017's - interrupt functionality will be disabled (INTA, INTB set to - open-drain, GPINTEN set to 0) when not explicitly under test. + these are also useful for GPIO tests on DUT1. + +- All of port A remains available for expansion and are routed to the + breadboard area. + +- The interrupt pins are not yet routed to DUTs, but could be. We + reserve DUT 0 GPIO 2 for this purpose with the understanding that + the 23017's interrupt functionality will be disabled (INTA, INTB + set to open-drain, GPINTEN set to 0) when not explicitly under test. + +### WS2812s + +The test harness reserves DUT1/D4 for connection to WS2812. + +# DUT Connections ESP8266 Device 0 Connections ---------------------------- -ESP | Usage -----------|---------------------------------------------------------- -GPIO 0 |Used to enter programming mode; otherwise unused in test environment. -GPIO 1 |Primary UART transmit; reserved for host communication -GPIO 2 |[reserved for 1-Wire] [+ reserved for 23017 INT[AB] connections] -GPIO 3 |Primary UART recieve; reserved for host communication -GPIO 4 |I2C SDA -GPIO 5 |I2C SCL -GPIO 6 |[Reserved for on-chip flash] -GPIO 7 |[Reserved for on-chip flash] -GPIO 8 |[Reserved for on-chip flash] -GPIO 9 |[Reserved for on-chip flash] -GPIO 10 |[Reserved for on-chip flash] -GPIO 11 |[Reserved for on-chip flash] -GPIO 12 | -GPIO 13 |Secondary UART RX; DUT 1 GPIO 15, I/O expander B 6 -GPIO 14 | -GPIO 15 |Secondary UART TX; DUT 1 GPIO 13, I/O expander B 7 -GPIO 16 | -ADC 0 |Resistor divider with I/O expander +ESP | Usage +-----------|---------------------------------------------------------- +D3/GPIO 0 |Used to enter programming mode; otherwise unused in test environment. +TX/GPIO 1 |Primary UART transmit; reserved for host communication +D4/GPIO 2 |(+ reserved for 23017 INT[AB] connections) +RX/GPIO 3 |Primary UART recieve; reserved for host communication +D2/GPIO 4 |I2C SDA bus 0. Connected to MCP23017, DUT0 Oled display and the TCS34725 if present. +D1/GPIO 5 |I2C SCL bus 0 +GPIO 6 |(Reserved for on-chip flash) +GPIO 7 |(Reserved for on-chip flash) +GPIO 8 |(Reserved for on-chip flash) +GPIO 9 |(Reserved for on-chip flash) +GPIO 10 |(Reserved for on-chip flash) +GPIO 11 |(Reserved for on-chip flash) +D6/GPIO 12 | +D7/GPIO 13 |Secondary UART RX; DUT 1 GPIO 15, I/O expander B 6 +D5/GPIO 14 | +D8/GPIO 15 |Secondary UART TX; DUT 1 GPIO 13, I/O expander B 7 +D0/GPIO 16 | +A0/ADC 0 |Resistor divider with I/O expander ESP8266 Device 1 Connections ---------------------------- From 74554ddd4767ed0f4d9ffa06b541628f5bcd4048 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Tue, 31 Dec 2019 00:05:58 +0000 Subject: [PATCH 08/11] expect test framework: net TCL module --- tests/expectnmcu/net.tcl | 55 +++++++++++++++++++++++++++++++++++ tests/expectnmcu/pkgIndex.tcl | 1 + 2 files changed, 56 insertions(+) create mode 100644 tests/expectnmcu/net.tcl diff --git a/tests/expectnmcu/net.tcl b/tests/expectnmcu/net.tcl new file mode 100644 index 0000000000..7dc0b1a496 --- /dev/null +++ b/tests/expectnmcu/net.tcl @@ -0,0 +1,55 @@ +namespace eval expectnmcu::net { +} + +package require expectnmcu::core + +# Wait for `wifi.sta.getip()` to return something that looks like an address, +# indicating that the device under test is probably online. +proc ::expectnmcu::net::waitwifista { dev {timeout 10} } { + for {set i 0} {${i} < ${timeout}} {incr i} { + send -i ${dev} "=wifi.sta.getip()\n" + expect { + -i ${dev} -re "\n(\[^\n\t\]+)\t\[^\t\]+\t\[^\t\]+\[\r\n\]+> " { + # That looks like an address report to me + return ${expect_out(1,string)} + } + -i ${dev} -ex "nil\[\r\n\]+> " { + # must not be connected yet + sleep 1 + } + -i ${dev} -re ${::expectnmcu::core::panicre} { return -code error "Panic!" } + timeout { } + } + } + + return -code error "WIFI STA: no IP address" +} + +proc ::expectnmcu::net::guessmyip { victimip } { + # Guess our IP address by using the victim's + spawn "ip" "route" "get" ${victimip} + expect { + -re "src (\[0-9.\]+) " { + close + return ${expect_out(1,string)} + } + } + close + return -code error "Cannot find source IP" +} + +# Open a socat stream with some verbosity and wait for it to become +# ready before returning. +proc ::expectnmcu::net::mksocat { socat_remote } { + spawn -noecho "socat" "-dd" "STDIO,cfmakeraw" ${socat_remote} + + set timeout 10 + expect { + -i ${spawn_id} -ex "listening on" { return ${spawn_id} } + -i ${spawn_id} -ex "starting data transfer loop" { return ${spawn_id} } + timeout { return -code error "Timeout opening local socat" } + } +} + + +package provide expectnmcu::net 1.0 diff --git a/tests/expectnmcu/pkgIndex.tcl b/tests/expectnmcu/pkgIndex.tcl index 64dbff6987..04a5dffae9 100644 --- a/tests/expectnmcu/pkgIndex.tcl +++ b/tests/expectnmcu/pkgIndex.tcl @@ -9,4 +9,5 @@ # full path name of this file's directory. package ifneeded expectnmcu::core 1.0 [list source [file join $dir core.tcl]] +package ifneeded expectnmcu::net 1.0 [list source [file join $dir net.tcl]] package ifneeded expectnmcu::xfer 1.0 [list source [file join $dir xfer.tcl]] From 219b11698cdc9b3d9a547d48ed46964614550788 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Thu, 30 Dec 2021 08:12:28 +0000 Subject: [PATCH 09/11] tests: add preflight TLS script --- tests/preflight-tls.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/preflight-tls.sh diff --git a/tests/preflight-tls.sh b/tests/preflight-tls.sh new file mode 100644 index 0000000000..618a59abe7 --- /dev/null +++ b/tests/preflight-tls.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e -u + +genectls() { + openssl ecparam -genkey -name "${1}" -out "${2}.key" + openssl req -new -sha256 -subj "/CN=${1}" -key "${2}.key" -out "${2}.csr" + openssl req -x509 -sha256 -days 365 -key "${2}.key" -in "${2}.csr" -out "${2}.crt" +} + +PFX="${NODEMCU_TESTTMP}/tmp-ec256v1" +[ -r "${PFX}.key" ] || genectls "prime256v1" "${PFX}" + +PFX="${NODEMCU_TESTTMP}/tmp-ec384r1" +[ -r "${PFX}.key" ] || genectls "secp384r1" "${PFX}" + +PFX="${NODEMCU_TESTTMP}/tmp-rsa2048" +[ -r "${PFX}.key" ] || { + openssl genrsa -out "${PFX}.key" "2048" + openssl req -x509 -nodes -subj "/CN=rsa" -key "${PFX}.key" -out "${PFX}.crt" "-days" "365" +} From 742a041c73f96b425dd37e3ad8718998a35be9ab Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Sun, 2 Feb 2020 14:57:59 +0000 Subject: [PATCH 10/11] expect tests: mqtt module --- tests/test-mqtt.expect | 448 +++++++++++++++++++++++++++++++++ tests/test-mqtt.mosquitto.conf | 16 ++ tests/test-mqtt.mosquitto.sh | 6 + 3 files changed, 470 insertions(+) create mode 100755 tests/test-mqtt.expect create mode 100644 tests/test-mqtt.mosquitto.conf create mode 100755 tests/test-mqtt.mosquitto.sh diff --git a/tests/test-mqtt.expect b/tests/test-mqtt.expect new file mode 100755 index 0000000000..8b902a13f5 --- /dev/null +++ b/tests/test-mqtt.expect @@ -0,0 +1,448 @@ +#!/usr/bin/env expect + +# Walk a NodeMCU device through some basic MQTT functionality tests. +# +# Requires `openssl` and `mosquitto` host-side; tested only on Linux. Spawns a +# mosquitto broker using the configuration files next to this script, but can +# be told to use another (see -brokerhost, -brokertcp, -brokerssl, -mqttuser, -mqttpass). +# +# A typical invocation looks like: +# export NODEMCU_TESTTMP=/tmp/nodemcu +# ./preflight-tls.sh +# TCLLIBPATH=./expectnmcu ./test-mqtt.expect -serial /dev/ttyUSB3 -wifi "$(cat wificmd)" +# +# where the file `wificmd` contains something like +# wifi.setmode(wifi.STATION); wifi.sta.config({...}); wifi.sta.connect() +# where the ... is filled in with the local network's configuration. All on +# one line, tho', so that the script just gets one prompt back. +# +# For debugging the test 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 ./mqtt-test.expect ... + +if { [info exists ::env(NODEMCU_TESTTMP)] } { + set tdir $::env(NODEMCU_TESTTMP) +} else { + send_user "==> Specify NODEMCU_TESTTMP in environment <==" + exit 1 +} + +package require expectnmcu::core + namespace import expectnmcu::core::send_exp_prompt + namespace import expectnmcu::core::send_exp_prompt_c + namespace import expectnmcu::core::send_exp_res_prompt +package require expectnmcu::net +package require tcltest + +package require cmdline +set cmd_parameters { + { serial.arg "/dev/ttyUSB0" "Set the serial interface name" } + { wifi.arg "" "Command to run to bring up the network" } + { ip.arg "" "My IP address (will guess if not given)" } + { brokerhost.arg "" "Broker IP address (mine if not given)" } + { brokertcp.arg "1883" "Broker TCP port" } + { brokerssl.arg "8883" "Broker SSL-over-TCP port" } + { mqttuser.arg "" "MQTT username for testing" } + { mqttpass.arg "" "MQTT password for testing" } + { tcfg.arg "" "Specify key/value pairs for tcltest config" } +} +set cmd_usage "- A NodeMCU MQTT test program" +if {[catch {array set cmdopts [cmdline::getoptions ::argv $cmd_parameters $cmd_usage]}]} { + send_user [cmdline::usage $cmd_parameters $cmd_usage] + exit 0 +} + +proc onexit {} { + uplevel 1 {if {[info exists sub_sid]} { + close -i ${sub_sid} + }} + uplevel 1 {if {[info exists broker_sid]} { + exec "kill" [exp_pid -i ${broker_sid}] + close -i ${broker_sid} + }} +} +exit -onexit onexit + +::tcltest::configure -verbose pste +foreach {k v} [split ${cmdopts(tcfg)}] { ::tcltest::configure ${k} ${v} } + +# Boot the board +set victim [::expectnmcu::core::connect ${cmdopts(serial)}] +send_user "\n===> Machine has booted <===\n" + +# Connect the board to the network +if {0 < [string length ${cmdopts(wifi)}]} { + send_exp_prompt ${victim} ${cmdopts(wifi)} +} +set victimip [::expectnmcu::net::waitwifista ${victim}] +send_user "\n===> Victim IP address ${victimip} <===\n" + +if {0 < [string length ${cmdopts(ip)}]} { + set myip ${cmdopts(ip)} +} else { + set myip [::expectnmcu::net::guessmyip ${victimip}] +} + +send_user "\n===> I am ${myip} <===\n" + +if {0 == [string length ${cmdopts(brokerhost)}]} { + if {0 < [string length ${cmdopts(mqttuser)}]} { + send_user "MQTT user with self-managed broker doesn't make sense\n" + exit 2 + } + if {0 < [string length ${cmdopts(mqttpass)}]} { + send_user "MQTT password with self-managed broker doesn't make sense\n" + exit 2 + } + + set cmdopts(brokerhost) ${myip} + set cmdopts(mqttuser) "nmcutest" + set cmdopts(mqttpass) "nmcutest" + + spawn "./test-mqtt.mosquitto.sh" + set broker_sid ${spawn_id} + + # Wait for mosquitto to come online; it announces opening its listeners + for {set i 0} {${i} < 2} {incr i} { + expect { + -i ${broker_sid} "listen socket on port ${cmdopts(brokertcp)}" { } + -i ${broker_sid} "listen socket on port ${cmdopts(brokerssl)}" { } + -i ${broker_sid} "Error" { + send_user "===> Broker error! <===" + exit 1 + } + } + } + sleep 1 +} + +# Locally, spawn a MQTT client to listen for messages. We expect to see all +# the messages we generate, both on the host and the device under test, show +# up here, and no others. +spawn "mosquitto_sub" "-v" "-t" "nmcutest/#" "-q" "2" \ + "-h" "${cmdopts(brokerhost)}" "-P" "${cmdopts(brokertcp)}" \ + "-u" "${cmdopts(mqttuser)}" "-P" "${cmdopts(mqttpass)}" +set sub_sid ${spawn_id} + +proc publish [list msg {topic "nmcutest/host"} {qos 2} [list acksid ${sub_sid} ] ] { + upvar 1 cmdopts cmdopts + exec "mosquitto_pub" "-t" "${topic}" "-m" "${msg}" "-q" "${qos}" \ + "-h" "${cmdopts(brokerhost)}" "-P" "${cmdopts(brokertcp)}" \ + "-u" "${cmdopts(mqttuser)}" "-P" "${cmdopts(mqttpass)}" + expect { + -timeout 2 + timeout { return 0 } + -i ${acksid} -re "${topic} ${msg}\[\r\n\]" { return 1 } + } +} + +# Create some helper functions on the DUT +send_exp_prompt ${victim} "function mkcb(str, id) return function(...) print(str, id, ...) end end" + +# Ready the DUT by creating an insecure mqtt client to our broker +send_exp_prompt ${victim} "mqct = mqtt.Client(\"nmcutest\", 10, \"${cmdopts(mqttuser)}\", \"${cmdopts(mqttpass)}\")" +send_exp_prompt ${victim} "mqct:lwt(\"nmcutest/lwt\", \"lwt\", 2, 0)" +send_exp_prompt ${victim} "mqct:on(\"offline\" , mkcb(\"OFFL\", 1))" +send_exp_prompt ${victim} "mqct:on(\"puback\" , mkcb(\"PUBL\", 1))" +send_exp_prompt ${victim} "mqct:on(\"suback\" , mkcb(\"SUBA\", 1))" +send_exp_prompt ${victim} "mqct:on(\"unsuback\", mkcb(\"UNSA\", 1))" +send_exp_prompt ${victim} "mqct:on(\"message\" , mkcb(\"MESG\", 1))" +send_exp_prompt ${victim} "mqct:on(\"overflow\", mkcb(\"MOVR\", 1))" +send_exp_prompt ${victim} "mqct:connect(\"${cmdopts(brokerhost)}\", ${cmdopts(brokertcp)}, false, mkcb(\"CONN\",1), mkcb(\"CFAI\",1))" +expect { + -i ${victim} -re "CONN\t1\[^\n]*\n" { } + -i ${victim} -re "CFAI\t1\[^\n]*\n" { + send_user "\n===> MQTT connection failed, bailing out <===\n" + exit 1 + } + timeout { + send_user "\n===> MQTT connection timed out, bailing out <===\n" + exit 1 + } +} + +# Set some default expect handlers. +expect_after { + -i ${sub_sid} "nmcutest/lwt" { return "lwt" } + timeout { return "timeout" } + eof { return "eof" } +} + +# Proc to wait around for the device to heartbeat. Note that we +# are mostly waiting for the above expect_acter's lwt trigger! +proc check_pulse { victim } { + # Timeout is 1.5x keepalive, as required by spec + expect { + -timeout 15 + timeout { return "ok" } + # Pass through any debugging chatter + -i ${victim} -re ".+" { exp_continue -continue_timer } + } +} + +# {{{ +::tcltest::test basic_wait_after_connect { + Wait to ensure that our client is sending keepalives +} -body { + set res [eval check_pulse ${victim} ] + + # Help ensure any debugging output gets logged as part of this test. + send_exp_prompt ${victim} "" + + return ${res} +} -result "ok" +# }}} + +# {{{ +::tcltest::test basic_publish_qos1 { + Basic publish test, QoS 1 +} -body { + set res 0 + + send_exp_prompt ${victim} "mqct:publish(\"nmcutest/nmcu\", \"4567\", 1, 0)" + expect { + -i ${sub_sid} "nmcutest/nmcu 4567" { incr res } + } + expect { + -i ${victim} -re "PUBL\t1" { incr res } + } + + return ${res} +} -result 2 +# }}} + +# {{{ +::tcltest::test basic_publish_qos2 { + Basic publish test, QoS 2 +} -body { + set res 0 + + send_exp_prompt ${victim} "mqct:publish(\"nmcutest/nmcu\", \"1234\", 2, 0)" + expect { + -i ${sub_sid} "nmcutest/nmcu 1234" { incr res } + } + expect { + -i ${victim} -re "PUBL\t1" { incr res } + } + + return ${res} +} -result 2 +# }}} + +# {{{ +::tcltest::test basic_publish_double_qos2 { + Double-tap publish test, QoS 2 +} -body { + send_exp_prompt ${victim} \ + "mqct:publish(\"nmcutest/nmcu\", \"1357\", 2, 0); mqct:publish(\"nmcutest/nmcu\", \"0246\", 2, 0)" + for {set i 0} {${i} < 4} {incr i} { + expect { + -i ${sub_sid} "nmcutest/nmcu 1357" { } + -i ${sub_sid} "nmcutest/nmcu 0246" { } + -i ${victim} "PUBL\t1" { } + } + } + return ${i} +} -result 4 +# }}} + +# {{{ +::tcltest::test basic_wait_after_pub { + Wait to ensure that our client is still heartbeating after publishing +} -body { + set res [eval check_pulse ${victim} ] + send_exp_prompt ${victim} "" + return ${res} +} -result "ok" +# }}} + +# {{{ +::tcltest::test basic_subscribe_qos1 { + Sub test, QoS 1 +} -body { + set res 0 + + send_exp_prompt ${victim} "mqct:subscribe(\"nmcutest/host\", 1)" + expect { + -i ${victim} "SUBA\t1" { incr res } + } + + if { [ publish "12345" ] == 1 } { incr res } + expect { + -i ${victim} -re "MESG\t1\tuserdata:\[^\t]*\tnmcutest/host\t12345" { incr res } + } + + send_exp_prompt ${victim} "mqct:unsubscribe(\"nmcutest/host\")" + expect { + -i ${victim} "UNSA\t1" { incr res } + } + + return ${res} +} -result 4 +# }}} + +# {{{ +::tcltest::test basic_wait_after_sub { + Wait to ensure that our client is still heartbeating after subscribing +} -body { + set res [eval check_pulse ${victim} ] + send_exp_prompt ${victim} "" + return ${res} +} -result "ok" +# }}} + +# {{{ +::tcltest::test overflow { + Message Overflow test +} -body { + set res 0 + + send_exp_prompt ${victim} "mqct:subscribe(\"nmcutest/host\", 1)" + expect { + -i ${victim} "SUBA\t1" { incr res } + } + + if { [ publish [string repeat "A" 2000] ] == 1 } { incr res } + expect { + -i ${victim} -re "MOVR\t1\tuserdata:\[^\t]*\tnmcutest/host\tA*\[\r\n]" { incr res } + } + + send_exp_prompt ${victim} "mqct:unsubscribe(\"nmcutest/host\")" + expect { + -i ${victim} "UNSA\t1" { incr res } + } + + return ${res} +} -result 4 +# }}} + +# {{{ +::tcltest::test un-re-sub { + Messages not received after unsubscription +} -body { + set res 0 + + # We are not presently subscribed + if { [ publish "54321" ] == 1 } { incr res } + expect { + -timeout 2 + timeout { incr res } + -i ${victim} -re "MESG\t1\tuserdata:\[^\t]*\tnmcutest/host\t54321" { return "fail" } + } + + # Now subscribe and resend + send_exp_prompt ${victim} \ + "mqct:subscribe({\[\"nmcutest/host\"\]=2,\[\"nmcutest/host2\"\]=2})" + expect { + -i ${victim} "SUBA\t1" { incr res } + } + + exec "mosquitto_pub" "-t" "nmcutest/host" "-m" "09876" "-q" "2" \ + "-h" "${cmdopts(brokerhost)}" "-P" "${cmdopts(brokertcp)}" \ + "-u" "${cmdopts(mqttuser)}" "-P" "${cmdopts(mqttpass)}" + expect { + -i ${sub_sid} -re "nmcutest/host 09876\[\r\n\]" { incr res } + } + expect { + -i ${victim} -re "MESG\t1\tuserdata:\[^\t]*\tnmcutest/host\t09876" { incr res } + } + + return ${res} +} -result 5 +# }}} + +send_user "\n===> Graceful disconnect <===\n" + +send_exp_prompt ${victim} "mqct:close()" +expect { + -timeout 5 + -i ${victim} "OFFL\t1" { } + timeout { + send_user "\n===> Failed to hang up <===\n" + exit 1 + } +} + +# {{{ +::tcltest::test close_no_lwt { + Ensure that close() does not send a LWT +} -body { + expect { + -timeout 2 + -i ${sub_sid} -re "nmcutest/lwt" { return "fail" } + timeout { return "ok" } + } +} -result "ok" +# }}} + +# {{{ +::tcltest::test tls_bad_cert { + TLS connection, wrong certificate +} -body { + set cert [open "${tdir}/tmp-ec384r1.crt"] + send_exp_prompt_c ${victim} "tls.cert.verify(function(ix) return ix == 1 and \[\[" + while { [gets ${cert} line] >= 0 } { + send_exp_prompt_c ${victim} $line + } + send_exp_prompt ${victim} "]] end)" + close ${cert} + send_exp_prompt ${victim} "tls.cert.auth(false)" + send_exp_prompt ${victim} "tls.setDebug(2)" + + send_exp_prompt ${victim} "mqct:connect(\"${cmdopts(brokerhost)}\", ${cmdopts(brokerssl)}, true, mkcb(\"CONN\",2), mkcb(\"CFAI\",2))" + expect { + -i ${victim} -re "CFAI\t2\[^\n]*\n" { return "cfai" } + -i ${victim} -re "CONN\t2\[^\n]*\n" { return "conn" } + } +} -result "cfai" +# }}} + +set cert [open "${tdir}/tmp-ec256v1.crt"] +send_exp_prompt_c ${victim} "tls.cert.verify(function(ix) return ix == 1 and \[\[" +while { [gets ${cert} line] >= 0 } { + send_exp_prompt_c ${victim} $line +} +send_exp_prompt ${victim} "]] end)" +close ${cert} +send_exp_prompt ${victim} "tls.cert.auth(false)" + +send_exp_prompt ${victim} "mqct:connect(\"${cmdopts(brokerhost)}\", ${cmdopts(brokerssl)}, true, mkcb(\"CONN\",3), mkcb(\"CFAI\",3))" +expect { + -i ${victim} -re "CONN\t3\[^\n]*\n" { } + -i ${victim} -re "CFAI\t3\[^\n]*\n" { + send_user "\n===> Unable to reconnect over SSL <===\n" + exit 1 + } + timeout { + send_user "\n===> Timeout while reconnecting over SSL <===\n" + exit 1 + } +} + +# {{{ +::tcltest::test hangup_lwt { + Disconnect wifi and wait for everyone to notice +} -body { + set res 0 + + send_exp_prompt ${victim} "wifi.sta.disconnect()" + + # This one is more or less immediate, because the connection is actively + # torn down ESP-side. + expect { + -timeout 4 + -i ${victim} -re "OFFL\t1" { incr res } + } + expect { + -timeout 30 + -i ${sub_sid} -re "nmcutest/lwt" { incr res } + } + + return ${res} +} -result 2 +# }}} + +############################################################################### +send_exp_prompt ${victim} "collectgarbage()" +::tcltest::cleanupTests diff --git a/tests/test-mqtt.mosquitto.conf b/tests/test-mqtt.mosquitto.conf new file mode 100644 index 0000000000..815ca6aa77 --- /dev/null +++ b/tests/test-mqtt.mosquitto.conf @@ -0,0 +1,16 @@ +allow_anonymous true +log_dest stderr +persistence false + +# acl_file mqtt-test.mosquitto.acl +# password_file mqtt-test.mosquitto.passwd + +# Listener for cleartext +listener 1883 + +# Listener for TLS, using EC keys from preflight +listener 8883 +tls_version tlsv1.2 +cafile tmp-ec256v1.crt +certfile tmp-ec256v1.crt +keyfile tmp-ec256v1.key diff --git a/tests/test-mqtt.mosquitto.sh b/tests/test-mqtt.mosquitto.sh new file mode 100755 index 0000000000..ccec764b82 --- /dev/null +++ b/tests/test-mqtt.mosquitto.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +SELFDIR=$(dirname $(readlink -f $0)) +cd ${NODEMCU_TESTTMP} + +exec /usr/sbin/mosquitto -c ${SELFDIR}/test-mqtt.mosquitto.conf From 2144d26c379647b26cca2cf600c1ac6dd5000f90 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Thu, 31 Dec 2020 02:22:33 +0000 Subject: [PATCH 11/11] tests: top-level runner --- tests/all.sh | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100755 tests/all.sh diff --git a/tests/all.sh b/tests/all.sh new file mode 100755 index 0000000000..81893b9517 --- /dev/null +++ b/tests/all.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env zsh + +set -e -u -x + +[ -n "${NODEMCU_WIFI:-}" ] || { echo "Specify NODEMCU_WIFI"; exit 1; } +[ -n "${NODEMCU_DUT0:-}" ] || { echo "Specify NODEMCU_DUT0"; exit 1; } + +: ${NODEMCU_DUT0_NUM:=int} +: ${NODEMCU_DUT0_CONFIG:=conf/dut0/testenv.conf} + +if [ -n "${NODEMCU_DUT1:-}" ]; then + : ${NODEMCU_DUT1_NUM:=int} + : ${NODEMCU_DUT1_CONFIG:=conf/dut1/testenv.conf} +fi + +echo "Test configuration:" +echo " DUT0 ${NODEMCU_DUT0} (${NODEMCU_DUT0_NUM} ${NODEMCU_DUT0_CONFIG})" +if [ -n "${NODEMCU_DUT1:-}" ]; then + echo " DUT1 ${NODEMCU_DUT1} (${NODEMCU_DUT1_NUM} ${NODEMCU_DUT1_CONFIG})" +fi + +echo "Preflight..." + +export TCLLIBPATH=./expectnmcu +[ -z "${NODEMCU_TESTTMP:-}" ] && { + NODEMCU_TESTTMP="$(umask 077; mktemp -d -p /tmp nodemcu.XXXXXXXXXX)" + cleanup() { + rm -rf ${NODEMCU_TESTTMP} + } + trap cleanup EXIT +} + +export NODEMCU_TESTTMP + +# Bring the board up and do our IP extraction once rather than letting each +# test guess over and over. +if [ -z "${MYIP-}" ]; then + echo "Probing for my IP address; this might take a moment..." + MYIP=$(expect ./preflight-host-ip.expect -serial ${NODEMCU_DUT0} -wifi ${NODEMCU_WIFI}) + stty sane + echo "Guessed my IP as ${MYIP}" +fi + +./preflight-tls.sh # Make TLS keys +./preflight-lfs.sh # Make LFS images + +echo "Staging LFSes and running early commands on each DUT..." + +expect ./tap-driver.expect -serial ${NODEMCU_DUT0} -lfs ${NODEMCU_TESTTMP}/tmp-lfs-${NODEMCU_DUT0_NUM}.img ${NODEMCU_DUT0_CONFIG} preflight-dut.lua +if [ -n "${NODEMCU_DUT1:-}" ]; then + expect ./tap-driver.expect -serial ${NODEMCU_DUT1} -lfs ${NODEMCU_TESTTMP}/tmp-lfs-${NODEMCU_DUT1_NUM}.img ${NODEMCU_DUT1_CONFIG} preflight-dut.lua +fi + +echo "Running on-DUT tests..." + +# These are all in LFS (see preflight-lfs.sh) and so we can -noxfer and just run tests by name +T=( + NTest_adc_env + NTest_file + NTest_gpio_env + NTest_pixbuf + NTest_tmr + NTest_ws2812_effects + NTest_ws2812 +) + +for t in "${T[@]}"; do + expect ./tap-driver.expect -serial ${NODEMCU_DUT0} -noxfer -runfunc "node.LFS.get('${t}')" +done + +echo "Running from-host tests..." + +T=( + ./test-mqtt.expect +) + +for t in "${T[@]}"; do + expect ${t} -serial ${NODEMCU_DUT0} -wifi ${NODEMCU_WIFI} -ip ${MYIP} +done