diff --git a/tests/README.md b/tests/README.md index b345d96c..5fea6c97 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 00000000..1ae71632 --- /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 00000000..84734eb0 --- /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 00000000..97a51601 --- /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